changeset 1405:e4fe346c021e

Merge
author Benjamin Golinvaux <bgo@osimis.io>
date Wed, 29 Apr 2020 22:06:58 +0200
parents 3e644f6fadd4 (current diff) 5630c2fb7b0f (diff)
children 5d7ee14dc1eb
files Applications/Commands/BaseCommands.yml Applications/Generic/GuiAdapter.cpp Applications/Generic/GuiAdapter.h Applications/Generic/NativeStoneApplicationContext.cpp Applications/Generic/NativeStoneApplicationContext.h Applications/Generic/NativeStoneApplicationRunner.cpp Applications/Generic/NativeStoneApplicationRunner.h Applications/Generic/Scene2DInteractor.h Applications/IStoneApplication.h Applications/Qt/QCairoWidget.cpp Applications/Qt/QCairoWidget.h Applications/Qt/QStoneMainWindow.cpp Applications/Qt/QStoneMainWindow.h Applications/Qt/QtStoneApplicationRunner.cpp Applications/Qt/QtStoneApplicationRunner.h Applications/Samples/Deprecated/BasicPetCtFusionApplication.h Applications/Samples/Deprecated/CMakeLists.txt Applications/Samples/Deprecated/CMakeLists.txt.old Applications/Samples/Deprecated/EmptyApplication.h Applications/Samples/Deprecated/LayoutPetCtFusionApplication.h Applications/Samples/Deprecated/Qt/SampleMainWindow.cpp Applications/Samples/Deprecated/Qt/SampleMainWindow.h Applications/Samples/Deprecated/Qt/SampleMainWindow.ui Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.cpp Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.h Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.ui Applications/Samples/Deprecated/Qt/SampleQtApplicationRunner.h Applications/Samples/Deprecated/SampleApplicationBase.h Applications/Samples/Deprecated/SampleInteractor.h Applications/Samples/Deprecated/SampleList.h Applications/Samples/Deprecated/SampleMainNative.cpp Applications/Samples/Deprecated/SampleMainWasm.cpp Applications/Samples/Deprecated/Samples-status.md Applications/Samples/Deprecated/SimpleViewer/AppStatus.h Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.cpp Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.h Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.cpp Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.h Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui Applications/Samples/Deprecated/SimpleViewer/Qt/mainQt.cpp Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.cpp Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.h Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.cpp Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.h Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h Applications/Samples/Deprecated/SimpleViewer/Wasm/mainWasm.cpp Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.html Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.ts Applications/Samples/Deprecated/SimpleViewer/Wasm/styles.css Applications/Samples/Deprecated/SimpleViewer/Wasm/tsconfig-simple-viewer.json Applications/Samples/Deprecated/SimpleViewerApplicationSingleFile.h Applications/Samples/Deprecated/SingleFrameApplication.h Applications/Samples/Deprecated/SingleFrameEditorApplication.h Applications/Samples/Deprecated/SingleVolumeApplication.h Applications/Samples/Deprecated/StoneSampleCommands.yml Applications/Samples/Deprecated/StoneSampleCommands_generate.py Applications/Samples/Deprecated/StoneSampleCommands_generated.hpp Applications/Samples/Deprecated/StoneSampleCommands_generated.ts Applications/Samples/Deprecated/SynchronizedSeriesApplication.h Applications/Samples/Deprecated/TestPatternApplication.h Applications/Samples/Deprecated/Web/index.html Applications/Samples/Deprecated/Web/samples-styles.css Applications/Samples/Deprecated/Web/simple-viewer-single-file.html Applications/Samples/Deprecated/Web/simple-viewer-single-file.ts Applications/Samples/Deprecated/Web/simple-viewer-single-file.tsconfig.json Applications/Samples/Deprecated/Web/single-frame-editor.html Applications/Samples/Deprecated/Web/single-frame-editor.ts Applications/Samples/Deprecated/Web/single-frame-editor.tsconfig.json Applications/Samples/Deprecated/Web/single-frame.html Applications/Samples/Deprecated/Web/single-frame.ts Applications/Samples/Deprecated/Web/single-frame.tsconfig.json Applications/Samples/Deprecated/Web/tsconfig-samples.json Applications/Samples/Deprecated/build-wasm.sh Applications/Samples/Deprecated/build-wasm.sh.old Applications/Samples/Deprecated/build-web-ext.sh Applications/Samples/Deprecated/build-web.sh Applications/Samples/Deprecated/get-requirements-windows.ps1 Applications/Samples/Deprecated/nginx.local.conf Applications/Samples/Deprecated/package-lock.json Applications/Samples/Deprecated/rt-viewer-demo/CMakeLists.txt Applications/Samples/Deprecated/rt-viewer-demo/build-sdl-msvc15.ps1 Applications/Samples/Deprecated/rt-viewer-demo/build-wasm.sh Applications/Samples/Deprecated/rt-viewer-demo/build-web.sh Applications/Samples/Deprecated/rt-viewer-demo/index.html Applications/Samples/Deprecated/rt-viewer-demo/main.cpp Applications/Samples/Deprecated/rt-viewer-demo/nginx.local.conf Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.html Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.ts Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.tsconfig.json Applications/Samples/Deprecated/rt-viewer-demo/samples-styles.css Applications/Samples/Deprecated/rt-viewer-demo/start-serving-files.sh Applications/Samples/Deprecated/rt-viewer-demo/stop-serving-files.sh Applications/Samples/Deprecated/rt-viewer-demo/tsconfig-samples.json Applications/Samples/Deprecated/tsconfig-stone.json Applications/Sdl/SdlCairoSurface.cpp Applications/Sdl/SdlCairoSurface.h Applications/Sdl/SdlEngine.cpp Applications/Sdl/SdlEngine.h Applications/Sdl/SdlOrthancSurface.cpp Applications/Sdl/SdlOrthancSurface.h Applications/Sdl/SdlStoneApplicationRunner.cpp Applications/Sdl/SdlStoneApplicationRunner.h Applications/StoneApplicationContext.cpp Applications/StoneApplicationContext.h Applications/Wasm/StartupParametersBuilder.cpp Applications/Wasm/StartupParametersBuilder.h Framework/Radiography/RadiographyAlphaLayer.cpp Framework/Radiography/RadiographyAlphaLayer.h Framework/Radiography/RadiographyDicomLayer.cpp Framework/Radiography/RadiographyDicomLayer.h Framework/Radiography/RadiographyLayer.cpp Framework/Radiography/RadiographyLayer.h Framework/Radiography/RadiographyLayerCropTracker.cpp Framework/Radiography/RadiographyLayerCropTracker.h Framework/Radiography/RadiographyLayerMaskTracker.cpp Framework/Radiography/RadiographyLayerMaskTracker.h Framework/Radiography/RadiographyLayerMoveTracker.cpp Framework/Radiography/RadiographyLayerMoveTracker.h Framework/Radiography/RadiographyLayerResizeTracker.cpp Framework/Radiography/RadiographyLayerResizeTracker.h Framework/Radiography/RadiographyLayerRotateTracker.cpp Framework/Radiography/RadiographyLayerRotateTracker.h Framework/Radiography/RadiographyMaskLayer.cpp Framework/Radiography/RadiographyMaskLayer.h Framework/Radiography/RadiographyScene.cpp Framework/Radiography/RadiographyScene.h Framework/Radiography/RadiographySceneCommand.cpp Framework/Radiography/RadiographySceneCommand.h Framework/Radiography/RadiographySceneReader.cpp Framework/Radiography/RadiographySceneReader.h Framework/Radiography/RadiographySceneWriter.cpp Framework/Radiography/RadiographySceneWriter.h Framework/Radiography/RadiographyTextLayer.cpp Framework/Radiography/RadiographyTextLayer.h Framework/Radiography/RadiographyWidget.cpp Framework/Radiography/RadiographyWidget.h Framework/Radiography/RadiographyWindowingTracker.cpp Framework/Radiography/RadiographyWindowingTracker.h Platforms/Generic/DelayedCallCommand.cpp Platforms/Generic/DelayedCallCommand.h Platforms/Generic/IOracleCommand.h Platforms/Generic/Oracle.cpp Platforms/Generic/Oracle.h Platforms/Generic/OracleDelayedCallExecutor.h Platforms/Generic/OracleWebService.cpp Platforms/Generic/OracleWebService.h Platforms/Generic/WebServiceCommandBase.cpp Platforms/Generic/WebServiceCommandBase.h Platforms/Generic/WebServiceDeleteCommand.cpp Platforms/Generic/WebServiceDeleteCommand.h Platforms/Generic/WebServiceGetCommand.cpp Platforms/Generic/WebServiceGetCommand.h Platforms/Generic/WebServicePostCommand.cpp Platforms/Generic/WebServicePostCommand.h Platforms/Wasm/Defaults.cpp Platforms/Wasm/Defaults.h Platforms/Wasm/WasmDelayedCallExecutor.cpp Platforms/Wasm/WasmDelayedCallExecutor.h Platforms/Wasm/WasmDelayedCallExecutor.js Platforms/Wasm/WasmPlatformApplicationAdapter.cpp Platforms/Wasm/WasmPlatformApplicationAdapter.h Platforms/Wasm/WasmViewport.cpp Platforms/Wasm/WasmViewport.h Platforms/Wasm/WasmWebService.cpp Platforms/Wasm/WasmWebService.h Platforms/Wasm/WasmWebService.js Platforms/Wasm/default-library.js Platforms/Wasm/logger.ts Platforms/Wasm/stone-framework-loader.ts Platforms/Wasm/tsconfig-stone.json Platforms/Wasm/wasm-application-runner.ts Platforms/Wasm/wasm-viewport.ts Resources/CMake/ProtobufCodeGeneration.cmake Resources/CMake/QtConfiguration.cmake Resources/CodeGeneration/Graveyard/playground.ts Resources/CodeGeneration/Graveyard/playground2.ts Resources/CodeGeneration/Graveyard/playground3.ts Resources/CodeGeneration/Graveyard/playground4.py Resources/CodeGeneration/Graveyard/runts.ps1 Resources/CodeGeneration/Graveyard/test_stonegen.html Resources/CodeGeneration/Graveyard/test_stonegen.ts Resources/CodeGeneration/Graveyard/tsconfig.json Resources/CodeGeneration/README.md Resources/CodeGeneration/stonegentool.py Resources/CodeGeneration/stonegentool_test.py Resources/CodeGeneration/template.in.h.j2 Resources/CodeGeneration/template.in.ts.j2 Resources/CodeGeneration/testCppHandler/CMakeLists.txt Resources/CodeGeneration/testCppHandler/README.md Resources/CodeGeneration/testCppHandler/conanfile.txt Resources/CodeGeneration/testCppHandler/main.cpp Resources/CodeGeneration/testCppHandler/test_data/test_Message2.json Resources/CodeGeneration/testWasmIntegrated/CMakeLists.txt Resources/CodeGeneration/testWasmIntegrated/DefaultLibrary.js Resources/CodeGeneration/testWasmIntegrated/build-web.sh Resources/CodeGeneration/testWasmIntegrated/build.sh Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/json/json-forwards.h Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/json/json.h Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/jsoncpp.cpp Resources/CodeGeneration/testWasmIntegrated/main.cpp Resources/CodeGeneration/testWasmIntegrated/serve.py Resources/CodeGeneration/testWasmIntegrated/styles.css Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.html Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.ts Resources/CodeGeneration/test_data/test2.yaml Resources/CodeGeneration/test_data/testTestStoneCodeGen.yaml Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.cpp Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.h Samples/Deprecated/MultiPlatform/BasicScene/mainQt.cpp Samples/Deprecated/MultiPlatform/BasicScene/mainSdl.cpp Samples/Deprecated/Qt/BasicSceneWindow.cpp Samples/Deprecated/Qt/BasicSceneWindow.h Samples/Deprecated/Qt/BasicSceneWindow.ui Samples/Deprecated/Qt/CMakeLists.txt Samples/Deprecated/Qt/QStoneOpenGlWidget.cpp Samples/Deprecated/Qt/QStoneOpenGlWidget.h Samples/Deprecated/Qt/Scene2DInteractor.cpp Samples/Deprecated/Qt/Scene2DInteractor.h Samples/Deprecated/README.md Samples/Deprecated/Sdl/BasicScene.cpp Samples/Deprecated/Sdl/CMakeLists.txt Samples/Deprecated/Sdl/FusionMprSdl.cpp Samples/Deprecated/Sdl/FusionMprSdl.h Samples/Deprecated/Sdl/Loader.cpp Samples/Deprecated/Sdl/RadiographyEditor.cpp Samples/Deprecated/Sdl/TrackerSample.cpp Samples/Deprecated/Sdl/TrackerSampleApp.cpp Samples/Deprecated/Sdl/TrackerSampleApp.h Samples/Deprecated/Sdl/cpp.hint Samples/Deprecated/WebAssembly/BasicMPR.cpp Samples/Deprecated/WebAssembly/BasicMPR.html Samples/Deprecated/WebAssembly/BasicScene.cpp Samples/Deprecated/WebAssembly/BasicScene.html Samples/Deprecated/WebAssembly/CMakeLists.txt Samples/Deprecated/WebAssembly/Configuration.json Samples/Deprecated/WebAssembly/ConfigurationLocalSJO.json Samples/Deprecated/WebAssembly/NOTES.txt Samples/Deprecated/WebAssembly/app.js Samples/Deprecated/WebAssembly/dev.h Samples/Deprecated/WebAssembly/index.html Samples/Sdl/RtViewer/CMakeLists.txt
diffstat 491 files changed, 38470 insertions(+), 39005 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Wed Apr 29 22:06:24 2020 +0200
+++ b/.hgignore	Wed Apr 29 22:06:58 2020 +0200
@@ -4,19 +4,8 @@
 *.h.orig
 .vs/
 .vscode/
-Applications/Qt/archive/
-Applications/Samples/Deprecated/ThirdPartyDownloads/
-Applications/Samples/Deprecated/build-wasm/
-Applications/Samples/Deprecated/build-web/
-Applications/Samples/Deprecated/node_modules/
-Applications/Samples/Deprecated/rt-viewer-demo/ThirdPartyDownloads/
-Applications/Samples/Deprecated/rt-viewer-demo/build-sdl-msvc15/
-Applications/Samples/Deprecated/rt-viewer-demo/build-tsc-output/
-Applications/Samples/Deprecated/rt-viewer-demo/build-wasm/
-Applications/Samples/Deprecated/rt-viewer-demo/build-web/
-Applications/build-*
+build*/
 CMakeLists.txt.user
-Platforms/Generic/ThirdPartyDownloads/
 Resources/CodeGeneration/.env
 Resources/CodeGeneration/.idea
 Resources/CodeGeneration/__pycache__
@@ -42,6 +31,6 @@
 Samples/Deprecated/WebAssembly/build/
 Samples/Deprecated/WebAssembly/ThirdPartyDownloads/
 Samples/Deprecated/WebAssembly/installDir/
-
+UnitTestsSources/ThirdPartyDownloads/
 node_modules/
 
--- a/Applications/Commands/BaseCommands.yml	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-SelectTool:
-  target: Application
-  toolName: string
-  comment: Selects the current application tool
-DownloadDicom:
-  target: SliceViewerWidget
-  comment: Downloads the slice currently displayed in the SliceViewerWidget
-Export:
-  target: IWidget
-  comment: Export the content of the widget
\ No newline at end of file
--- a/Applications/Generic/GuiAdapter.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1142 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "GuiAdapter.h"
-
-#if ORTHANC_ENABLE_OPENGL == 1
-#  include "../../Framework/OpenGL/OpenGLIncludes.h"
-#endif
-
-#if ORTHANC_ENABLE_SDL == 1
-#  include <SDL_video.h>
-#  include <SDL_render.h>
-#  include <SDL.h>
-#endif
-
-#if ORTHANC_ENABLE_THREADS == 1
-#  include "../../Framework/Deprecated/Messages/LockingEmitter.h"
-#endif
-
-#include <Core/Compatibility.h>
-
-namespace OrthancStone
-{
-  std::ostream& operator<<(
-    std::ostream& os, const GuiAdapterKeyboardEvent& event)
-  {
-    os << "sym: " << event.sym << " (" << (int)(event.sym[0]) << ") ctrl: " << event.ctrlKey << ", " <<
-      "shift: " << event.shiftKey << ", " <<
-      "alt: " << event.altKey;
-    return os;
-  }
-
-  std::ostream& operator<<(
-    std::ostream& os, const GuiAdapterMouseEvent& event)
-  {
-    os << "targetX: " << event.targetX << " targetY: " << event.targetY << " button: " << event.button
-      << "ctrlKey: " << event.ctrlKey << "shiftKey: " << event.shiftKey << "altKey: " << event.altKey;
-      
-    return os;
-  }
-
-#if ORTHANC_ENABLE_WASM == 1
-  void GuiAdapter::Run(GuiAdapterRunFunc /*func*/, void* /*cookie*/)
-  {
-  }
-
-  void ConvertFromPlatform(
-    GuiAdapterUiEvent& dest,
-    int                      eventType,
-    const EmscriptenUiEvent& src)
-  {
-    // no data for now
-  }
-
-  void ConvertFromPlatform(
-    GuiAdapterMouseEvent& dest,
-    int                         eventType,
-    const EmscriptenMouseEvent& src)
-  {
-    memset(&dest, 0, sizeof(GuiAdapterMouseEvent));
-    switch (eventType)
-    {
-    case EMSCRIPTEN_EVENT_CLICK:
-      LOG(ERROR) << "Emscripten EMSCRIPTEN_EVENT_CLICK is not supported";
-      ORTHANC_ASSERT(false, "Not supported");
-      break;
-    case EMSCRIPTEN_EVENT_MOUSEDOWN:
-      dest.type = GUIADAPTER_EVENT_MOUSEDOWN;
-      break;
-    case EMSCRIPTEN_EVENT_DBLCLICK:
-      dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK;
-      break;
-    case EMSCRIPTEN_EVENT_MOUSEMOVE:
-      dest.type = GUIADAPTER_EVENT_MOUSEMOVE;
-      break;
-    case EMSCRIPTEN_EVENT_MOUSEUP:
-      dest.type = GUIADAPTER_EVENT_MOUSEUP;
-      break;
-    case EMSCRIPTEN_EVENT_WHEEL:
-      dest.type = GUIADAPTER_EVENT_WHEEL;
-      break;
-
-    default:
-      LOG(ERROR) << "Emscripten event: " << eventType << " is not supported";
-      ORTHANC_ASSERT(false, "Not supported");
-    }
-    //dest.timestamp = src.timestamp;
-    //dest.screenX = src.screenX;
-    //dest.screenY = src.screenY;
-    //dest.clientX = src.clientX;
-    //dest.clientY = src.clientY;
-    dest.ctrlKey = src.ctrlKey;
-    dest.shiftKey = src.shiftKey;
-    dest.altKey = src.altKey;
-    //dest.metaKey = src.metaKey;
-    dest.button = src.button;
-    //dest.buttons = src.buttons;
-    //dest.movementX = src.movementX;
-    //dest.movementY = src.movementY;
-    dest.targetX = src.targetX;
-    dest.targetY = src.targetY;
-    //dest.canvasX = src.canvasX;
-    //dest.canvasY = src.canvasY;
-    //dest.padding = src.padding;
-  }
-
-  void ConvertFromPlatform( GuiAdapterWheelEvent& dest, int eventType, const EmscriptenWheelEvent& src)
-  {
-    ConvertFromPlatform(dest.mouse, eventType, src.mouse);
-    dest.deltaX = src.deltaX;
-    dest.deltaY = src.deltaY;
-    switch (src.deltaMode)
-    {
-    case DOM_DELTA_PIXEL:
-      dest.deltaMode = GUIADAPTER_DELTA_PIXEL;
-      break;
-    case DOM_DELTA_LINE:
-      dest.deltaMode = GUIADAPTER_DELTA_LINE;
-      break;
-    case DOM_DELTA_PAGE:
-      dest.deltaMode = GUIADAPTER_DELTA_PAGE;
-      break;
-    default:
-      ORTHANC_ASSERT(false, "Unknown deltaMode: " << src.deltaMode <<
-        " in wheel event...");
-    }
-    dest.deltaMode = src.deltaMode;
-  }
-
-  void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const EmscriptenKeyboardEvent& src)
-  {
-    dest.sym[0] = src.key[0];
-    dest.sym[1] = 0;
-    dest.ctrlKey = src.ctrlKey;
-    dest.shiftKey = src.shiftKey;
-    dest.altKey = src.altKey;
-  }
-
-  template<typename GenericFunc>
-  struct FuncAdapterPayload
-  {
-    std::string canvasCssSelector;
-    void* userData;
-    GenericFunc callback;
-  };
-
-  template<typename GenericFunc,
-    typename GuiAdapterEvent,
-    typename EmscriptenEvent>
-    EM_BOOL OnEventAdapterFunc(
-      int eventType, const EmscriptenEvent* emEvent, void* userData)
-  {
-    // userData is OnMouseWheelFuncAdapterPayload
-    FuncAdapterPayload<GenericFunc>* payload =
-      reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData);
-    // LOG(INFO) << "OnEventAdapterFunc";
-    // LOG(INFO) << "------------------";
-    // LOG(INFO) << "eventType: " << eventType << " wheelEvent: " << 
-    //   (int)wheelEvent << " userData: " << userData << 
-    //   " payload->userData: " << payload->userData;
-
-    GuiAdapterEvent guiEvent;
-    ConvertFromPlatform(guiEvent, eventType, *emEvent);
-    bool ret = (*(payload->callback))(payload->canvasCssSelector, &guiEvent, payload->userData);
-    return static_cast<EM_BOOL>(ret);
-  }
-
-  template<typename GenericFunc,
-    typename GuiAdapterEvent,
-    typename EmscriptenEvent>
-    EM_BOOL OnEventAdapterFunc2(
-      int /*eventType*/, const EmscriptenEvent* wheelEvent, void* userData)
-  {
-    // userData is OnMouseWheelFuncAdapterPayload
-    FuncAdapterPayload<GenericFunc>* payload =
-      reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData);
-
-    GuiAdapterEvent guiEvent;
-    ConvertFromPlatform(guiEvent, *wheelEvent);
-    bool ret = (*(payload->callback))(payload->canvasCssSelector, &guiEvent, payload->userData);
-    return static_cast<EM_BOOL>(ret);
-  }
-
-  template<typename GenericFunc>
-  EM_BOOL OnEventAdapterFunc3(
-    double time, void* userData)
-  {
-    // userData is OnMouseWheelFuncAdapterPayload
-    FuncAdapterPayload<GenericFunc>* payload =
-      reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData);
-    //std::unique_ptr< FuncAdapterPayload<GenericFunc> > deleter(payload);
-    bool ret = (*(payload->callback))(time, payload->userData);
-    return static_cast<EM_BOOL>(ret);
-  }
-
-  /*
-  
-  Explanation
-  ===========
-
-  - in "older" Emscripten, where DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR doesn't exist or is set to 0,
-    the following strings need to be used to register events:
-    - for canvas, the canvas DOM id. In case of <canvas id="mycanvas1" width='640' ...></canvas>", the string needs 
-      to be "mycanvas"
-    - for the window (for key events), the string needs to be "#window"
-  - in newer Emscripten where DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR==1 (or maybe is not there anymore, in the
-    future as of 2020-04-20)
-    - for canvas, the canvas DOM id. In case of <canvas id="mycanvas1" width='640' ...></canvas>", the string needs
-      to be "#mycanvas"  (notice the "number sign", aka "hash", NOT AKA "sharp", as can be read on https://en.wikipedia.org/wiki/Number_sign)
-    - for the window (for key events), the string needs to be EMSCRIPTEN_EVENT_TARGET_WINDOW. I do not mean 
-      "EMSCRIPTEN_EVENT_TARGET_WINDOW", but the #define EMSCRIPTEN_EVENT_TARGET_WINDOW         ((const char*)2) that
-      can be found in emscripten/html5.h
-
-  The code below converts the input canvasId (as in the old emscripten) to the emscripten-compliant one, with the
-  following compile condition : #if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR == 1
-
-  If the DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR build parameter disappears, you might want to refactor this code
-  or continue to pass the DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR compile macro (which is different from the CMake
-  variable)     
-
-  What we are doing below:
-  - in older Emscripten, the registration functions will receive "mycanvas" and "#window" and the callbacks will receive 
-    the same std::string in their payload ("mycanvas" and "#window")
-
-  - in newer Emscripten, the registration functions will receive "#mycanvas" and EMSCRIPTEN_EVENT_TARGET_WINDOW, but 
-    the callbacks will receive "#mycanvas" and "#window" (since it is not possible to store the EMSCRIPTEN_EVENT_TARGET_WINDOW
-    magic value in an std::string, while we still want the callback to be able to change its behavior according to the
-    target element.
-  
-  */
-
-  void convertElementTarget(const char*& outCanvasCssSelectorSz, std::string& outCanvasCssSelector, const std::string& canvasId)
-  {
-    // only "#window" can start with a #
-    if (canvasId[0] == '#')
-    {
-      ORTHANC_ASSERT(canvasId == "#window");
-    }
-#if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR == 1
-    if (canvasId == "#window")
-    {
-      // we store this in the payload so that the callback can 
-      outCanvasCssSelector = "#window";
-      outCanvasCssSelectorSz = EMSCRIPTEN_EVENT_TARGET_WINDOW;
-    }
-    else
-    {
-      outCanvasCssSelector = "#" + canvasId;
-      outCanvasCssSelectorSz = outCanvasCssSelector.c_str();
-    }
-#else
-    if (canvasId == "#window")
-    {
-      // we store this in the payload so that the callback can 
-      outCanvasCssSelector = "#window";
-      outCanvasCssSelectorSz = outCanvasCssSelector.c_str();;
-    }
-    else
-    {
-      outCanvasCssSelector = canvasId;
-      outCanvasCssSelectorSz = outCanvasCssSelector.c_str();;
-    }
-#endif
-  }
-
-  // resize: (const char* target, void* userData, EM_BOOL useCapture, em_ui_callback_func callback)
-  template<
-    typename GenericFunc,
-    typename GuiAdapterEvent,
-    typename EmscriptenEvent,
-    typename EmscriptenSetCallbackFunc>
-    static void SetCallback(
-      EmscriptenSetCallbackFunc emFunc,
-      std::string canvasId, void* userData, bool capture, GenericFunc func)
-  {
-    std::string canvasCssSelector;
-    const char* canvasCssSelectorSz = NULL;
-    convertElementTarget(canvasCssSelectorSz, canvasCssSelector, canvasId);
-
-    // TODO: write RemoveCallback with an int id that gets returned from here
-
-    // create userdata payload
-    std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload(new FuncAdapterPayload<GenericFunc>());
-    payload->canvasCssSelector = canvasCssSelector;
-    payload->callback = func;
-    payload->userData = userData;
-    void* userDataRaw = reinterpret_cast<void*>(payload.release());
-
-    // call the registration function
-    (*emFunc)(
-      canvasCssSelectorSz,
-      userDataRaw,
-      static_cast<EM_BOOL>(capture),
-      &OnEventAdapterFunc<GenericFunc, GuiAdapterEvent, EmscriptenEvent>,
-      EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD);
-  }
-
-  template<
-    typename GenericFunc,
-    typename GuiAdapterEvent,
-    typename EmscriptenEvent,
-    typename EmscriptenSetCallbackFunc>
-    static void SetCallback2(
-      EmscriptenSetCallbackFunc emFunc,
-      std::string canvasId, void* userData, bool capture, GenericFunc func)
-  {
-    std::string canvasCssSelector;
-    const char* canvasCssSelectorSz = NULL;
-    convertElementTarget(canvasCssSelectorSz, canvasCssSelector, canvasId);
-
-    // TODO: write RemoveCallback with an int id that gets returned from here
-
-    // create userdata payload
-    std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload(new FuncAdapterPayload<GenericFunc>());
-    payload->canvasCssSelector = canvasCssSelector;
-    payload->callback = func;
-    payload->userData = userData;
-    void* userDataRaw = reinterpret_cast<void*>(payload.release());
-
-    // call the registration function
-    (*emFunc)(
-      canvasCssSelectorSz,
-      userDataRaw,
-      static_cast<EM_BOOL>(capture),
-      &OnEventAdapterFunc2<GenericFunc, GuiAdapterEvent, EmscriptenEvent>,
-      EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD);
-  }
-
-  template<
-    typename GenericFunc,
-    typename EmscriptenSetCallbackFunc>
-    static void SetAnimationFrameCallback(
-      EmscriptenSetCallbackFunc emFunc,
-      void* userData, GenericFunc func)
-  {
-    std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload(
-      new FuncAdapterPayload<GenericFunc>()
-    );
-    payload->canvasCssSelector = "UNDEFINED";
-    payload->callback = func;
-    payload->userData = userData;
-    void* userDataRaw = reinterpret_cast<void*>(payload.release());
-    (*emFunc)(
-      &OnEventAdapterFunc3<GenericFunc>,
-      userDataRaw);
-  }
-
-  void GuiAdapter::SetWheelCallback(
-    std::string canvasId, void* userData, bool capture, OnMouseWheelFunc func)
-  {
-    SetCallback<OnMouseWheelFunc, GuiAdapterWheelEvent, EmscriptenWheelEvent>(
-      &emscripten_set_wheel_callback_on_thread,
-      canvasId,
-      userData,
-      capture,
-      func);
-  }
-
-  
-  void GuiAdapter::SetMouseDblClickCallback(
-      std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
-  {
-    SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>(
-      &emscripten_set_dblclick_callback_on_thread,
-      canvasId,
-      userData,
-      capture,
-      func);
-  }
-
-
-  void GuiAdapter::SetMouseDownCallback(
-    std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
-  {
-    SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>(
-      &emscripten_set_mousedown_callback_on_thread,
-      canvasId,
-      userData,
-      capture,
-      func);
-  }
-
-  void GuiAdapter::SetMouseMoveCallback(
-    std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
-  {
-    // LOG(INFO) << "SetMouseMoveCallback -- " << "supplied userData: " << 
-    //   userData;
-
-    SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>(
-      &emscripten_set_mousemove_callback_on_thread,
-      canvasId,
-      userData,
-      capture,
-      func);
-  }
-
-  void GuiAdapter::SetMouseUpCallback(
-    std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
-  {
-    SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>(
-      &emscripten_set_mouseup_callback_on_thread,
-      canvasId,
-      userData,
-      capture,
-      func);
-  }
-
-  void GuiAdapter::SetKeyDownCallback(
-    std::string canvasId, void* userData, bool capture, OnKeyDownFunc func)
-  {
-    SetCallback2<OnKeyDownFunc, GuiAdapterKeyboardEvent, EmscriptenKeyboardEvent>(
-      &emscripten_set_keydown_callback_on_thread,
-      canvasId,
-      userData,
-      capture,
-      func);
-  }
-
-  void GuiAdapter::SetKeyUpCallback(
-    std::string canvasId, void* userData, bool capture, OnKeyUpFunc func)
-  {
-    SetCallback2<OnKeyUpFunc, GuiAdapterKeyboardEvent, EmscriptenKeyboardEvent>(
-      &emscripten_set_keyup_callback_on_thread,
-      canvasId,
-      userData,
-      capture,
-      func);
-  }
-
-#if 0
-  // useless under Wasm where canvas resize is handled automatically
-  void GuiAdapter::SetResizeCallback(
-    std::string canvasId, void* userData, bool capture, OnWindowResizeFunc func)
-  {
-    SetCallback<OnWindowResizeFunc, GuiAdapterUiEvent, EmscriptenUiEvent>(
-      &emscripten_set_resize_callback_on_thread,
-      canvasId,
-      userData,
-      capture,
-      func);
-  }
-#endif
-
-  void GuiAdapter::RequestAnimationFrame(
-    OnAnimationFrameFunc func, void* userData)
-  {
-    SetAnimationFrameCallback<OnAnimationFrameFunc>(
-      &emscripten_request_animation_frame_loop,
-      userData,
-      func);
-  }
-
-#if 0
-  void GuiAdapter::SetKeyDownCallback(
-    std::string canvasId, void* userData, bool capture, OnKeyDownFunc func)
-  {
-    emscripten_set_keydown_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func);
-  }
-  void GuiAdapter::SetKeyUpCallback(
-    std::string canvasId, void* userData, bool capture, OnKeyUpFunc func)
-  {
-    emscripten_set_keyup_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func);
-  }
-
-  // handled from within WebAssemblyViewport
-  //void GuiAdapter::SetResizeCallback(std::string canvasId, void* userData, bool capture, OnWindowResizeFunc func)
-  //{
-  //  emscripten_set_resize_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func);
-  //}
-
-  void GuiAdapter::RequestAnimationFrame(OnAnimationFrameFunc func, void* userData)
-  {
-    emscripten_request_animation_frame_loop(func, userData);
-  }
-#endif
-
-
-#else
-
-  // SDL ONLY
-  void ConvertFromPlatform(GuiAdapterMouseEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, const SDL_Event& source)
-  {
-    memset(&dest, 0, sizeof(GuiAdapterMouseEvent));
-    switch (source.type)
-    {
-    case SDL_MOUSEBUTTONDOWN:
-      if (source.button.clicks == 1) {
-        dest.type = GUIADAPTER_EVENT_MOUSEDOWN;
-      } else if (source.button.clicks == 2) {
-        dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK;
-      } else {
-        dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK;
-        LOG(WARNING) << "Multiple-click ignored.";
-      }
-      break;
-    case SDL_MOUSEMOTION:
-      dest.type = GUIADAPTER_EVENT_MOUSEMOVE;
-      break;
-    case SDL_MOUSEBUTTONUP:
-      dest.type = GUIADAPTER_EVENT_MOUSEUP;
-      break;
-    case SDL_MOUSEWHEEL:
-      dest.type = GUIADAPTER_EVENT_WHEEL;
-      break;
-    default:
-      LOG(ERROR) << "SDL event: " << source.type << " is not supported";
-      ORTHANC_ASSERT(false, "Not supported");
-    }
-    //dest.timestamp = src.timestamp;
-    //dest.screenX = src.screenX;
-    //dest.screenY = src.screenY;
-    //dest.clientX = src.clientX;
-    //dest.clientY = src.clientY;
-    dest.ctrlKey = ctrlPressed;
-    dest.shiftKey = shiftPressed;
-    dest.altKey = altPressed;
-    //dest.metaKey = src.metaKey;
-    switch (source.button.button)
-    {
-    case SDL_BUTTON_MIDDLE:
-      dest.button =GUIADAPTER_MOUSEBUTTON_MIDDLE;
-      break;
-
-    case SDL_BUTTON_RIGHT:
-      dest.button = GUIADAPTER_MOUSEBUTTON_RIGHT;
-      break;
-
-    case SDL_BUTTON_LEFT:
-      dest.button = GUIADAPTER_MOUSEBUTTON_LEFT;
-      break;
-
-    default:
-      break;
-    }
-    //dest.buttons = src.buttons;
-    //dest.movementX = src.movementX;
-    //dest.movementY = src.movementY;
-    dest.targetX = source.button.x;
-    dest.targetY = source.button.y;
-    //dest.canvasX = src.canvasX;
-    //dest.canvasY = src.canvasY;
-    //dest.padding = src.padding;
-  }
-
-  void ConvertFromPlatform(
-    GuiAdapterWheelEvent& dest,
-    bool ctrlPressed, bool shiftPressed, bool altPressed,
-    const SDL_Event& source)
-  {
-    ConvertFromPlatform(dest.mouse, ctrlPressed, shiftPressed, altPressed, source);
-    dest.deltaX = source.wheel.x;
-    dest.deltaY = source.wheel.y;
-  }
-
-  void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const SDL_Event& src)
-  {
-    memset(&dest, 0, sizeof(GuiAdapterMouseEvent));
-    switch (src.type)
-    {
-    case SDL_KEYDOWN:
-      dest.type = GUIADAPTER_EVENT_KEYDOWN;
-      break;
-    case SDL_KEYUP:
-      dest.type = GUIADAPTER_EVENT_KEYUP;
-      break;
-    default:
-      LOG(ERROR) << "SDL event: " << src.type << " is not supported";
-      ORTHANC_ASSERT(false, "Not supported");
-    }
-    dest.sym[0] = src.key.keysym.sym;
-    dest.sym[1] = 0;
-
-    if (src.key.keysym.mod & KMOD_CTRL)
-      dest.ctrlKey = true;
-    else
-      dest.ctrlKey = false;
-
-    if (src.key.keysym.mod & KMOD_SHIFT)
-      dest.shiftKey = true;
-    else
-      dest.shiftKey = false;
-
-    if (src.key.keysym.mod & KMOD_ALT)
-      dest.altKey = true;
-    else
-      dest.altKey = false;
-  }
-
-  // SDL ONLY
-  void GuiAdapter::SetSdlResizeCallback(
-    std::string canvasId, void* userData, bool capture, OnSdlWindowResizeFunc func)
-  {
-    resizeHandlers_.push_back(EventHandlerData<OnSdlWindowResizeFunc>(canvasId, func, userData));
-  }
-
-  // SDL ONLY
-  void GuiAdapter::SetMouseDownCallback(
-    std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
-  {
-    mouseDownHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData));
-  }
-
-  // SDL ONLY
-  void GuiAdapter::SetMouseDblClickCallback(
-    std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
-  {
-    mouseDblCickHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData));
-  }
-
-  // SDL ONLY
-  void GuiAdapter::SetMouseMoveCallback(
-    std::string canvasId, void* userData, bool capture, OnMouseEventFunc  func)
-  {
-    mouseMoveHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData));
-  }
-
-  // SDL ONLY
-  void GuiAdapter::SetMouseUpCallback(
-    std::string canvasId, void* userData, bool capture, OnMouseEventFunc  func)
-  {
-    mouseUpHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData));
-  }
-
-  // SDL ONLY
-  void GuiAdapter::SetWheelCallback(
-    std::string canvasId, void* userData, bool capture, OnMouseWheelFunc  func)
-  {
-    mouseWheelHandlers_.push_back(EventHandlerData<OnMouseWheelFunc>(canvasId, func, userData));
-  }
-
-  // SDL ONLY
-  void GuiAdapter::SetKeyDownCallback(
-    std::string canvasId, void* userData, bool capture, OnKeyDownFunc   func)
-  {
-    keyDownHandlers_.push_back(EventHandlerData<OnKeyDownFunc>(canvasId, func, userData));
-  }
-
-  // SDL ONLY
-  void GuiAdapter::SetKeyUpCallback(
-    std::string canvasId, void* userData, bool capture, OnKeyUpFunc    func)
-  {
-    keyUpHandlers_.push_back(EventHandlerData<OnKeyUpFunc>(canvasId, func, userData));
-  }
-
-  // SDL ONLY
-  void GuiAdapter::SetGenericSdlEventCallback(
-    std::string canvasId, void* userData, bool capture, OnSdlEventCallback func)
-  {
-    sdlEventHandlers_.push_back(EventHandlerData<OnSdlEventCallback>(canvasId, func, userData));
-  }
-
-  // SDL ONLY
-  void GuiAdapter::OnAnimationFrame()
-  {
-    std::vector<size_t> disabledAnimationHandlers;
-    for (size_t i = 0; i < animationFrameHandlers_.size(); i++)
-    {
-      // TODO: fix time 
-      bool goOn = (*(animationFrameHandlers_[i].first))(0, animationFrameHandlers_[i].second);
-
-      // If the function returns false, we need to emulate what happens in Web 
-      // and remove the function from the handlers...
-      if (!goOn)
-        disabledAnimationHandlers.push_back(i);
-    }
-    for (size_t i = 0; i < disabledAnimationHandlers.size(); i++)
-    {
-      ORTHANC_ASSERT(animationFrameHandlers_.begin() + disabledAnimationHandlers[i] < animationFrameHandlers_.end());
-      animationFrameHandlers_.erase(animationFrameHandlers_.begin() + disabledAnimationHandlers[i]);
-    }
-  }
-
-  // SDL ONLY
-  void GuiAdapter::OnResize(unsigned int width, unsigned int height)
-  {
-    for (size_t i = 0; i < resizeHandlers_.size(); i++)
-    {
-      (*(resizeHandlers_[i].func))(
-        resizeHandlers_[i].canvasName, NULL, width, height, resizeHandlers_[i].userData);
-    }
-  }
-
-
-
-  void GuiAdapter::OnSdlGenericEvent(const SDL_Event& sdlEvent)
-  {
-    // Events related to a window are only sent to the related canvas
-    // User events are sent to everyone (we can't filter them here)
-    
-    /*
-    SDL_WindowEvent    SDL_WINDOWEVENT
-    SDL_KeyboardEvent     SDL_KEYDOWN
-                          SDL_KEYUP
-    SDL_TextEditingEvent  SDL_TEXTEDITING
-    SDL_TextInputEvent    SDL_TEXTINPUT
-    SDL_MouseMotionEvent  SDL_MOUSEMOTION
-    SDL_MouseButtonEvent  SDL_MOUSEBUTTONDOWN 
-                          SDL_MOUSEBUTTONUP
-    SDL_MouseWheelEvent   SDL_MOUSEWHEEL
-    SDL_UserEvent         SDL_USEREVENT through ::SDL_LASTEVENT-1
-    */
-
-    // if this string is left empty, it means the message will be sent to
-    // all widgets.
-    // otherwise, it contains the originating message window title
-
-    std::string windowTitle;
-    uint32_t windowId = 0;
-
-    if (sdlEvent.type == SDL_WINDOWEVENT)
-      windowId = sdlEvent.window.windowID;
-    else if (sdlEvent.type == SDL_KEYDOWN || sdlEvent.type == SDL_KEYUP)
-      windowId = sdlEvent.key.windowID;
-    else if (sdlEvent.type == SDL_TEXTEDITING)
-      windowId = sdlEvent.edit.windowID;
-    else if (sdlEvent.type == SDL_TEXTINPUT)
-      windowId = sdlEvent.text.windowID;
-    else if (sdlEvent.type == SDL_MOUSEMOTION)
-      windowId = sdlEvent.motion.windowID;
-    else if (sdlEvent.type == SDL_MOUSEBUTTONDOWN || sdlEvent.type == SDL_MOUSEBUTTONUP)
-      windowId = sdlEvent.button.windowID;
-    else if (sdlEvent.type == SDL_MOUSEWHEEL)
-      windowId = sdlEvent.wheel.windowID;
-    else if (sdlEvent.type >= SDL_USEREVENT && sdlEvent.type <= (SDL_LASTEVENT-1))
-      windowId = sdlEvent.user.windowID;
-
-    if (windowId != 0)
-    {
-      SDL_Window* sdlWindow = SDL_GetWindowFromID(windowId);
-      ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowId << "\" is not a valid SDL window ID!");
-      const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
-      ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowId << "\" has a NULL window title!");
-      windowTitle = windowTitleSz;
-      ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowId << "\" has an empty window title!");
-    }
-
-    for (size_t i = 0; i < sdlEventHandlers_.size(); i++)
-    {
-      // normally, the handlers return a bool indicating whether they
-      // have handled the event or not, but we don't really care about this
-      std::string& canvasName = sdlEventHandlers_[i].canvasName;
-      
-      bool sendEvent = true;
-
-      if (windowTitle != "" && (canvasName != windowTitle))
-        sendEvent = false;
-
-      if (sendEvent)
-      {
-        OnSdlEventCallback func = sdlEventHandlers_[i].func;
-        (*func)(canvasName, sdlEvent, sdlEventHandlers_[i].userData);
-      }
-    }
-  }
-
-  // SDL ONLY
-  void GuiAdapter::OnMouseWheelEvent(uint32_t windowID, const GuiAdapterWheelEvent& event)
-  {
-    // the SDL window name IS the canvas name ("canvas" is used because this lib
-    // is designed for Wasm
-    SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID);
-    ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!");
-
-    const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
-    ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!");
-
-    std::string windowTitle(windowTitleSz);
-    ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!");
-
-    switch (event.mouse.type)
-    {
-    case GUIADAPTER_EVENT_WHEEL:
-      for (size_t i = 0; i < mouseWheelHandlers_.size(); i++)
-      {
-        if (mouseWheelHandlers_[i].canvasName == windowTitle)
-          (*(mouseWheelHandlers_[i].func))(windowTitle, &event, mouseWheelHandlers_[i].userData);
-      }
-      break;
-    default:
-      ORTHANC_ASSERT(false, "Wrong event.type: " << event.mouse.type << " in GuiAdapter::OnMouseWheelEvent(...)");
-      break;
-    }
-  }
-
-
-  void GuiAdapter::OnKeyboardEvent(uint32_t windowID, const GuiAdapterKeyboardEvent& event)
-  {
-    // only one-letter (ascii) keyboard events supported for now
-    ORTHANC_ASSERT(event.sym[0] != 0);
-    ORTHANC_ASSERT(event.sym[1] == 0);
-
-    SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID);
-    ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!");
-
-    const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
-    ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!");
-
-    std::string windowTitle(windowTitleSz);
-    ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!");
-
-    switch (event.type)
-    {
-    case GUIADAPTER_EVENT_KEYDOWN:
-      for (size_t i = 0; i < keyDownHandlers_.size(); i++)
-      {
-        (*(keyDownHandlers_[i].func))(windowTitle, &event, keyDownHandlers_[i].userData);
-      }
-      break;
-    case GUIADAPTER_EVENT_KEYUP:
-      for (size_t i = 0; i < keyUpHandlers_.size(); i++)
-      {
-        (*(keyUpHandlers_[i].func))(windowTitle, &event, keyUpHandlers_[i].userData);
-      }
-      break;
-    default:
-      ORTHANC_ASSERT(false, "Wrong event.type: " << event.type << " in GuiAdapter::OnKeyboardEvent(...)");
-      break;
-    }
-  }
-
-  // SDL ONLY
-  void GuiAdapter::OnMouseEvent(uint32_t windowID, const GuiAdapterMouseEvent& event)
-  {
-    if (windowID == 0)
-    {
-      LOG(WARNING) << "GuiAdapter::OnMouseEvent -- windowID == 0 and event won't be routed!";
-    }
-    else
-    {
-      // the SDL window name IS the canvas name ("canvas" is used because this lib
-      // is designed for Wasm
-      SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID);
-
-      ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!");
-
-      const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
-      ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!");
-
-      std::string windowTitle(windowTitleSz);
-      ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!");
-
-      switch (event.type)
-      {
-      case GUIADAPTER_EVENT_MOUSEDOWN:
-        for (size_t i = 0; i < mouseDownHandlers_.size(); i++)
-        {
-          if (mouseDownHandlers_[i].canvasName == windowTitle)
-            (*(mouseDownHandlers_[i].func))(windowTitle, &event, mouseDownHandlers_[i].userData);
-        }
-        break;
-      case GUIADAPTER_EVENT_MOUSEDBLCLICK:
-        for (size_t i = 0; i < mouseDblCickHandlers_.size(); i++)
-        {
-          if (mouseDblCickHandlers_[i].canvasName == windowTitle)
-            (*(mouseDblCickHandlers_[i].func))(windowTitle, &event, mouseDblCickHandlers_[i].userData);
-        }
-        break;
-      case GUIADAPTER_EVENT_MOUSEMOVE:
-        for (size_t i = 0; i < mouseMoveHandlers_.size(); i++)
-        {
-          if (mouseMoveHandlers_[i].canvasName == windowTitle)
-            (*(mouseMoveHandlers_[i].func))(windowTitle, &event, mouseMoveHandlers_[i].userData);
-        }
-        break;
-      case GUIADAPTER_EVENT_MOUSEUP:
-        for (size_t i = 0; i < mouseUpHandlers_.size(); i++)
-        {
-          if (mouseUpHandlers_[i].canvasName == windowTitle)
-            (*(mouseUpHandlers_[i].func))(windowTitle, &event, mouseUpHandlers_[i].userData);
-        }
-        break;
-      default:
-        ORTHANC_ASSERT(false, "Wrong event.type: " << event.type << " in GuiAdapter::OnMouseEvent(...)");
-        break;
-      }
-    }
-  }
-
-
-  // extern void Debug_SetContextToBeKilled(std::string title);
-  // extern void Debug_SetContextToBeRestored(std::string title);
-
-  // SDL ONLY
-  void GuiAdapter::RequestAnimationFrame(OnAnimationFrameFunc func, void* userData)
-  {
-    animationFrameHandlers_.push_back(std::make_pair(func, userData));
-  }
-
-# if ORTHANC_ENABLE_OPENGL == 1 && !defined(__APPLE__)   /* OpenGL debug is not available on OS X */
-
-  // SDL ONLY
-  static void GLAPIENTRY
-    OpenGLMessageCallback(GLenum source,
-      GLenum type,
-      GLuint id,
-      GLenum severity,
-      GLsizei length,
-      const GLchar * message,
-      const void* userParam)
-  {
-    if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
-    {
-      fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
-        (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
-        type, severity, message);
-    }
-  }
-# endif
-
-#if 0
-  // TODO: remove this when generic sdl event handlers are implemented in 
-  // the DoseView
-  // SDL ONLY
-  bool GuiAdapter::IsSdlViewPortRefreshEvent(const SDL_Event& event) const
-  {
-    SDL_Window* sdlWindow = SDL_GetWindowFromID(event.window.windowID);
-
-    ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << event.window.windowID << "\" is not a valid SDL window ID!");
-
-    const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
-
-    // now we need to find the DoseView from from the canvas name!
-    // (and retrieve the SdlViewport)
-    boost::shared_ptr<IGuiAdapterWidget> foundWidget;
-    VisitWidgets([&foundWidget, windowTitleSz](auto widget)
-    {
-      if (widget->GetCanvasIdentifier() == std::string(windowTitleSz))
-        foundWidget = widget;
-    });
-    ORTHANC_ASSERT(foundWidget, "The window named: \"" << windowTitleSz << "\" was not found in the registered widgets!");
-    return foundWidget->GetSdlViewport().IsRefreshEvent(event);
-  }
-#endif
-
-  // SDL ONLY
-  void GuiAdapter::Run(GuiAdapterRunFunc func, void* cookie)
-  {
-#if 1
-    // TODO: MAKE THIS DYNAMIC !!! See SdlOpenGLViewport vs Cairo in ViewportWrapper
-# if ORTHANC_ENABLE_OPENGL == 1 && !defined(__APPLE__)
-    glEnable(GL_DEBUG_OUTPUT);
-    glDebugMessageCallback(OpenGLMessageCallback, 0);
-# endif
-#endif
-
-    // Uint32 SDL_GetWindowID(SDL_Window* window)
-    // SDL_Window* SDL_GetWindowFromID(Uint32 id)   // may return NULL
-
-    bool stop = false;
-    while (!stop)
-    {
-      {
-        // TODO: lock all viewports here! (use a scoped object)
-        if(func != NULL)
-          (*func)(cookie);
-        OnAnimationFrame(); // in SDL we must call it
-      }
-
-      while (!stop)
-      {
-        std::vector<SDL_Event> sdlEvents;
-        std::map<Uint32,SDL_Event> userEventsMap;
-        
-        SDL_Event sdlEvent;
-
-        // FIRST: collect all pending events
-        while (SDL_PollEvent(&sdlEvent) != 0)
-        {
-          if ( (sdlEvent.type >= SDL_USEREVENT) && 
-               (sdlEvent.type <= SDL_USEREVENT) )
-          {
-            // we don't want to have multiple events with the same event.type
-            userEventsMap[sdlEvent.type] = sdlEvent;
-          }
-          else
-          {
-            sdlEvents.push_back(sdlEvent);
-          }
-        }
-
-        // SECOND: collect all user events
-        for (std::map<Uint32,SDL_Event>::const_iterator it = userEventsMap.begin(); it != userEventsMap.end(); ++it)
-          sdlEvents.push_back(it->second);
-                
-        // now process the events
-        for (std::vector<SDL_Event>::const_iterator it = sdlEvents.begin(); it != sdlEvents.end(); ++it)
-        {
-          const SDL_Event& sdlEvent = *it;
-          // TODO: lock all viewports here! (use a scoped object)
-
-          if (sdlEvent.type == SDL_QUIT)
-          {
-            // TODO: call exit callbacks here
-            stop = true;
-            break;
-          }
-          else if ((sdlEvent.type == SDL_MOUSEMOTION) ||
-            (sdlEvent.type == SDL_MOUSEBUTTONDOWN) ||
-                   (sdlEvent.type == SDL_MOUSEBUTTONUP))
-          {
-            int scancodeCount = 0;
-            const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
-            bool ctrlPressed(false);
-            bool shiftPressed(false);
-            bool altPressed(false);
-
-            if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL])
-              ctrlPressed = true;
-            if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL])
-              ctrlPressed = true;
-            if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT])
-              shiftPressed = true;
-            if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT])
-              shiftPressed = true;
-            if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT])
-              altPressed = true;
-
-            GuiAdapterMouseEvent dest;
-            ConvertFromPlatform(dest, ctrlPressed, shiftPressed, altPressed, sdlEvent);
-            OnMouseEvent(sdlEvent.window.windowID, dest);
-  #if 0
-            // for reference, how to create trackers
-            if (tracker)
-            {
-              PointerEvent e;
-              e.AddPosition(compositor.GetPixelCenterCoordinates(
-                sdlEvent.button.x, sdlEvent.button.y));
-              tracker->PointerMove(e);
-            }
-  #endif
-          }
-          else if (sdlEvent.type == SDL_MOUSEWHEEL)
-          {
-
-            int scancodeCount = 0;
-            const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
-            bool ctrlPressed(false);
-            bool shiftPressed(false);
-            bool altPressed(false);
-
-            if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL])
-              ctrlPressed = true;
-            if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL])
-              ctrlPressed = true;
-            if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT])
-              shiftPressed = true;
-            if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT])
-              shiftPressed = true;
-            if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT])
-              altPressed = true;
-
-            GuiAdapterWheelEvent dest;
-            ConvertFromPlatform(dest, ctrlPressed, shiftPressed, altPressed, sdlEvent);
-            OnMouseWheelEvent(sdlEvent.window.windowID, dest);
-
-            //KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount);
-
-            //int x, y;
-            //SDL_GetMouseState(&x, &y);
-
-            //if (sdlEvent.wheel.y > 0)
-            //{
-            //  locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers);
-            //}
-            //else if (sdlEvent.wheel.y < 0)
-            //{
-            //  locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers);
-            //}
-          }
-          else if (sdlEvent.type == SDL_WINDOWEVENT &&
-            (sdlEvent.window.event == SDL_WINDOWEVENT_RESIZED ||
-             sdlEvent.window.event == SDL_WINDOWEVENT_SIZE_CHANGED))
-          {
-  #if 0
-            tracker.reset();
-  #endif
-            OnResize(sdlEvent.window.data1, sdlEvent.window.data2);
-          }
-          else if (sdlEvent.type == SDL_KEYDOWN && sdlEvent.key.repeat == 0 /* Ignore key bounce */)
-          {
-            switch (sdlEvent.key.keysym.sym)
-            {
-            case SDLK_f:
-              // window.GetWindow().ToggleMaximize(); //TODO: move to particular handler
-              break;
-
-            // This commented out code was used to debug the context
-            // loss/restoring code (2019-08-10)
-            // case SDLK_k:
-            //   {
-            //     SDL_Window* window = SDL_GetWindowFromID(sdlEvent.window.windowID);
-            //     std::string windowTitle(SDL_GetWindowTitle(window));
-            //     Debug_SetContextToBeKilled(windowTitle);
-            //   }
-            //   break;
-            // case SDLK_l:
-            //   {
-            //     SDL_Window* window = SDL_GetWindowFromID(sdlEvent.window.windowID);
-            //     std::string windowTitle(SDL_GetWindowTitle(window));
-            //     Debug_SetContextToBeRestored(windowTitle);
-            //   }
-            //   break;
-
-            case SDLK_q:
-              stop = true;
-              break;
-
-            default:
-              GuiAdapterKeyboardEvent dest;
-              ConvertFromPlatform(dest, sdlEvent);
-              OnKeyboardEvent(sdlEvent.window.windowID, dest);
-              break;
-            }
-          }
-        
-          OnSdlGenericEvent(sdlEvent);
-        }
-      }
-
-      SDL_Delay(1);
-    }
-  }
-#endif
-}
-
--- a/Applications/Generic/GuiAdapter.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,375 +0,0 @@
-/**
- * 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 <string>
-
-#if ORTHANC_ENABLE_WASM != 1
-# ifdef __EMSCRIPTEN__
-#   error __EMSCRIPTEN__ is defined and ORTHANC_ENABLE_WASM != 1
-# endif
-#endif
-
-#if ORTHANC_ENABLE_WASM == 1
-# ifndef __EMSCRIPTEN__
-#   error __EMSCRIPTEN__ is not defined and ORTHANC_ENABLE_WASM == 1
-# endif
-#endif
-
-#if ORTHANC_ENABLE_WASM == 1
-# include <emscripten/html5.h>
-#else
-# if ORTHANC_ENABLE_SDL == 1
-#   include <SDL.h>
-# endif
-#endif
-
-#include "../../Framework/StoneException.h"
-
-#if ORTHANC_ENABLE_THREADS == 1
-# include "../../Framework/Deprecated/Messages/LockingEmitter.h"
-#endif
-
-#include <vector>
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-
-namespace OrthancStone
-{
-#if ORTHANC_ENABLE_SDL == 1
-  class SdlViewport;
-#endif
-
-#if 0
-
-  /**
-  This interface is used to store the widgets that are controlled by the 
-  GuiAdapter and receive event callbacks.
-  The callbacks may possibly be downcast (using dynamic_cast, for safety) \
-  to the actual widget type
-  */
-  class IGuiAdapterWidget
-  {
-  public:
-    virtual ~IGuiAdapterWidget() {}
-    
-#if #if ORTHANC_ENABLE_SDL == 1
-    /**
-    Returns the SdlViewport that this widget contains. If the underlying 
-    viewport type is *not* SDL, then an error is returned.
-    */
-    virtual SdlViewport& GetSdlViewport() = 0;
-#endif
-  };
-
-#endif
-
-  enum GuiAdapterMouseButtonType
-  {
-    GUIADAPTER_MOUSEBUTTON_LEFT = 0,
-    GUIADAPTER_MOUSEBUTTON_MIDDLE = 1,
-    GUIADAPTER_MOUSEBUTTON_RIGHT = 2
-  };
-
-
-  enum GuiAdapterHidEventType
-  {
-    GUIADAPTER_EVENT_MOUSEDOWN      = 1973,
-    GUIADAPTER_EVENT_MOUSEMOVE      = 1974,
-    GUIADAPTER_EVENT_MOUSEDBLCLICK  = 1975,
-    GUIADAPTER_EVENT_MOUSEUP        = 1976,
-    GUIADAPTER_EVENT_WHEEL          = 1977,
-    GUIADAPTER_EVENT_KEYDOWN        = 1978,
-    GUIADAPTER_EVENT_KEYUP          = 1979,
-  };
-
-  const unsigned int GUIADAPTER_DELTA_PIXEL = 2973;
-  const unsigned int GUIADAPTER_DELTA_LINE  = 2974;
-  const unsigned int GUIADAPTER_DELTA_PAGE  = 2975;
-
-  struct GuiAdapterUiEvent;
-  struct GuiAdapterMouseEvent;
-  struct GuiAdapterWheelEvent;
-  struct GuiAdapterKeyboardEvent;
-
-#if 1
-  typedef bool (*OnMouseEventFunc)    (std::string canvasId, const GuiAdapterMouseEvent* mouseEvent, void* userData);
-  typedef bool (*OnMouseWheelFunc)    (std::string canvasId, const GuiAdapterWheelEvent* wheelEvent, void* userData);
-  typedef bool (*OnKeyDownFunc)       (std::string canvasId, const GuiAdapterKeyboardEvent*   keyEvent,   void* userData);
-  typedef bool (*OnKeyUpFunc)         (std::string canvasId, const GuiAdapterKeyboardEvent*   keyEvent,   void* userData);
-  typedef bool (*OnAnimationFrameFunc)(double time, void* userData);
-  
-#if ORTHANC_ENABLE_SDL == 1
-  typedef bool (*OnSdlEventCallback)  (std::string canvasId, const SDL_Event& sdlEvent, void* userData);
-
-  typedef bool (*OnSdlWindowResizeFunc)(std::string canvasId, 
-                                        const GuiAdapterUiEvent* uiEvent, 
-                                        unsigned int width, 
-                                        unsigned int height, 
-                                        void* userData);
-
-
-#endif
-
-#else
-
-#if ORTHANC_ENABLE_WASM == 1
-  typedef EM_BOOL (*OnMouseEventFunc)(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData);
-  typedef EM_BOOL (*OnMouseWheelFunc)(int eventType, const EmscriptenWheelEvent* wheelEvent, void* userData);
-  typedef EM_BOOL (*OnKeyDownFunc)   (int eventType, const EmscriptenKeyboardEvent* keyEvent, void* userData);
-  typedef EM_BOOL (*OnKeyUpFunc)     (int eventType, const EmscriptenKeyboardEvent* keyEvent, void* userData);
-
-  typedef EM_BOOL (*OnAnimationFrameFunc)(double time, void* userData);
-  typedef EM_BOOL (*OnWindowResizeFunc)(int eventType, const EmscriptenUiEvent* uiEvent, void* userData);
-#else
-  typedef bool    (*OnMouseEventFunc)(int eventType, const SDL_Event* mouseEvent, void* userData);
-  typedef bool    (*OnMouseWheelFunc)(int eventType, const SDL_Event* wheelEvent, void* userData);
-  typedef bool    (*OnKeyDownFunc)   (int eventType, const SDL_Event* keyEvent,   void* userData);
-  typedef bool    (*OnKeyUpFunc)     (int eventType, const SDL_Event* keyEvent,   void* userData);
-
-  typedef bool    (*OnAnimationFrameFunc)(double time, void* userData);
-  typedef bool    (*OnWindowResizeFunc)(int eventType, const GuiAdapterUiEvent* uiEvent, void* userData);
-#endif
-
-#endif
-  struct GuiAdapterMouseEvent
-  {
-    GuiAdapterHidEventType type;
-    //double                   timestamp;
-    //long                     screenX;
-    //long                     screenY;
-    //long                     clientX;
-    //long                     clientY;
-    bool                     ctrlKey;
-    bool                     shiftKey;
-    bool                     altKey;
-    //bool                     metaKey;
-    unsigned short           button;
-    //unsigned short           buttons;
-    //long                     movementX;
-    //long                     movementY;
-    long                     targetX;
-    long                     targetY;
-    // canvasX and canvasY are deprecated - there no longer exists a Module['canvas'] object, so canvasX/Y are no longer reported (register a listener on canvas directly to get canvas coordinates, or translate manually)
-    //long                     canvasX;
-    //long                     canvasY;
-    //long                     padding;
-
-  public:
-    GuiAdapterMouseEvent()
-      : ctrlKey(false),
-        shiftKey(false),
-        altKey(false)
-    {
-    }
-  };
-
-  struct GuiAdapterWheelEvent {
-    GuiAdapterMouseEvent mouse;
-    double deltaX;
-    double deltaY;
-    unsigned long deltaMode;
-  };
-
-  // we don't use any data now
-  struct GuiAdapterUiEvent {};
-
-  // EmscriptenKeyboardEvent
-  struct GuiAdapterKeyboardEvent
-  {
-    GuiAdapterHidEventType type;
-    char sym[32];
-    bool ctrlKey;
-    bool shiftKey;
-    bool altKey;
-  };
-
-  std::ostream& operator<<(std::ostream& os, const GuiAdapterKeyboardEvent& event);
-  std::ostream& operator<<(std::ostream& os, const GuiAdapterMouseEvent& event);
-
-  /*
-    Mousedown event trigger when either the left or right (or middle) mouse is pressed 
-    on the object;
-
-    Mouseup event trigger when either the left or right (or middle) mouse is released
-    above the object after triggered mousedown event and held.
-
-    Click event trigger when the only left mouse button is pressed and released on the
-    same object, requires the Mousedown and Mouseup event happened before Click event.
-
-    The normal expect trigger order: onMousedown >> onMouseup >> onClick
-
-    Testing in Chrome v58, the time between onMouseup and onClick events are around 
-    7ms to 15ms
-
-    FROM: https://codingrepo.com/javascript/2017/05/19/javascript-difference-mousedown-mouseup-click-events/
-  */
-#if ORTHANC_ENABLE_WASM == 1
-  void ConvertFromPlatform(GuiAdapterUiEvent& dest, int eventType, const EmscriptenUiEvent& src);
-
-  void ConvertFromPlatform(GuiAdapterMouseEvent& dest, int eventType, const EmscriptenMouseEvent& src);
-  
-  void ConvertFromPlatform(GuiAdapterWheelEvent& dest, int eventType, const EmscriptenWheelEvent& src);
-
-  void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const EmscriptenKeyboardEvent& src);
-#else
-
-# if ORTHANC_ENABLE_SDL == 1
-  void ConvertFromPlatform(GuiAdapterMouseEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, const SDL_Event& source);
-
-  void ConvertFromPlatform(GuiAdapterWheelEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, const SDL_Event& source);
-
-  void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const SDL_Event& source);
-
-# endif
-
-#endif
-
-  typedef void (*GuiAdapterRunFunc)(void*);
-
-  class GuiAdapter
-  {
-  public:
-    GuiAdapter()
-    {
-      static int instanceCount = 0;
-      ORTHANC_ASSERT(instanceCount == 0);
-      instanceCount = 1;
-    }
-
-    /**
-      emscripten_set_resize_callback("EMSCRIPTEN_EVENT_TARGET_WINDOW", NULL, false, OnWindowResize);
-
-      emscripten_set_wheel_callback("#mycanvas1", widget1_.get(), false, OnXXXMouseWheel);
-      emscripten_set_wheel_callback("#mycanvas2", widget2_.get(), false, OnXXXMouseWheel);
-      emscripten_set_wheel_callback("#mycanvas3", widget3_.get(), false, OnXXXMouseWheel);
-
-      emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyDown); ---> NO!
-      emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyUp);
-
-      emscripten_request_animation_frame_loop(OnAnimationFrame, NULL);
-    
-      SDL:
-      see https://wiki.libsdl.org/SDL_CaptureMouse
-
-    */
-
-    void SetMouseDownCallback       (std::string canvasId, void* userData, bool capture, OnMouseEventFunc   func);
-    void SetMouseDblClickCallback   (std::string canvasId, void* userData, bool capture, OnMouseEventFunc   func);
-    void SetMouseMoveCallback       (std::string canvasId, void* userData, bool capture, OnMouseEventFunc   func);
-    void SetMouseUpCallback         (std::string canvasId, void* userData, bool capture, OnMouseEventFunc   func);
-    void SetWheelCallback           (std::string canvasId, void* userData, bool capture, OnMouseWheelFunc   func);
-    void SetKeyDownCallback         (std::string canvasId, void* userData, bool capture, OnKeyDownFunc      func);
-    void SetKeyUpCallback           (std::string canvasId, void* userData, bool capture, OnKeyUpFunc        func);
-
-#if ORTHANC_ENABLE_SDL == 1
-
-    void SetGenericSdlEventCallback (std::string canvasId, void* userData, bool capture, OnSdlEventCallback func);
-
-    typedef bool (*OnSdlEventCallback)  (std::string canvasId, const SDL_Event& sdlEvent, void* userData);
-
-    // if you pass "#window", then any Window resize will trigger the callback
-    // (this special string is converted to EMSCRIPTEN_EVENT_TARGET_WINDOW in DOM, when DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1)
-    void SetSdlResizeCallback(std::string canvasId, 
-                              void* userData, 
-                              bool capture, 
-                              OnSdlWindowResizeFunc func);
-#endif
-
-    void RequestAnimationFrame(OnAnimationFrameFunc func, void* userData);
-
-    // TODO: implement and call to remove canvases [in SDL, although code should be generic]
-    void SetOnExitCallback();
-
-    /**
-    Under SDL, this function does NOT return until all windows have been closed.
-    Under wasm, it returns without doing anything, since the event loop is managed
-    by the browser.
-    */
-    void Run(GuiAdapterRunFunc func = NULL, void* cookie = NULL);
-
-  private:
-
-#if ORTHANC_ENABLE_SDL == 1
-    /**
-    Gives observers a chance to react based on generic event handlers. This 
-    is used, for instance, when the viewport lock interface is invalidated.
-    */
-    void OnSdlGenericEvent(const SDL_Event& sdlEvent);
-#endif
-
-    /**
-    In SDL, this executes all the registered headers
-    */
-    void OnAnimationFrame();
-
-    //void RequestAnimationFrame(OnAnimationFrameFunc func, void* userData);
-    std::vector<std::pair<OnAnimationFrameFunc, void*> >  
-      animationFrameHandlers_;
-    
-    void OnResize(unsigned int width, unsigned int height);
-
-#if ORTHANC_ENABLE_SDL == 1
-    template<typename Func>
-    struct EventHandlerData
-    {
-      EventHandlerData(std::string canvasName, Func func, void* userData) 
-        : canvasName(canvasName)
-        , func(func)
-        , userData(userData)
-      {
-      }
-
-      std::string canvasName;
-      Func        func;
-      void*       userData;
-    };
-    std::vector<EventHandlerData<OnSdlWindowResizeFunc> > resizeHandlers_;
-    std::vector<EventHandlerData<OnMouseEventFunc  > >    mouseDownHandlers_;
-    std::vector<EventHandlerData<OnMouseEventFunc  > >    mouseDblCickHandlers_;
-    std::vector<EventHandlerData<OnMouseEventFunc  > >    mouseMoveHandlers_;
-    std::vector<EventHandlerData<OnMouseEventFunc  > >    mouseUpHandlers_;
-    std::vector<EventHandlerData<OnMouseWheelFunc  > >    mouseWheelHandlers_;
-    std::vector<EventHandlerData<OnKeyDownFunc > >        keyDownHandlers_;
-    std::vector<EventHandlerData<OnKeyUpFunc > >          keyUpHandlers_;
-    std::vector<EventHandlerData<OnSdlEventCallback > >   sdlEventHandlers_;
-
-    /**
-    This executes all the registered headers if needed (in wasm, the browser
-    deals with this)
-    */
-    void OnMouseEvent(uint32_t windowID, const GuiAdapterMouseEvent& event);
-
-    void OnKeyboardEvent(uint32_t windowID, const GuiAdapterKeyboardEvent& event);
-
-    /**
-    Same remark as OnMouseEvent
-    */
-    void OnMouseWheelEvent(uint32_t windowID, const GuiAdapterWheelEvent& event);
-
-#endif
-
-    /**
-    This executes all the registered headers if needed (in wasm, the browser
-    deals with this)
-    */
-    void ViewportsUpdateSize();
-  };
-}
--- a/Applications/Generic/NativeStoneApplicationContext.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "NativeStoneApplicationContext.h"
-#include "../../Platforms/Generic/OracleWebService.h"
-
-namespace OrthancStone
-{
-  void NativeStoneApplicationContext::GlobalMutexLocker::SetCentralWidget(
-    boost::shared_ptr<Deprecated::IWidget> widget)
-  {
-    that_.centralViewport_.SetCentralWidget(widget);
-  }
-
-
-  void NativeStoneApplicationContext::UpdateThread(NativeStoneApplicationContext* that)
-  {
-    while (!that->stopped_)
-    {
-      {
-        GlobalMutexLocker locker(*that);
-        locker.GetCentralViewport().DoAnimation();
-      }
-      
-      boost::this_thread::sleep(boost::posix_time::milliseconds(that->updateDelayInMs_));
-    }
-  }
-  
-
-  NativeStoneApplicationContext::NativeStoneApplicationContext() :
-    stopped_(true),
-    updateDelayInMs_(100)   // By default, 100ms between each refresh of the content
-  {
-    srand(static_cast<unsigned int>(time(NULL))); 
-  }
-
-
-  void NativeStoneApplicationContext::Start()
-  {
-    boost::recursive_mutex::scoped_lock lock(globalMutex_);
-    
-    if (stopped_ &&
-        centralViewport_.HasAnimation())
-    {
-      stopped_ = false;
-      updateThread_ = boost::thread(UpdateThread, this);
-    }
-  }
-
-
-  void NativeStoneApplicationContext::Stop()
-  {
-    stopped_ = true;
-    
-    if (updateThread_.joinable())
-    {
-      updateThread_.join();
-    }
-  }
-}
--- a/Applications/Generic/NativeStoneApplicationContext.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/**
- * 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 "../../Framework/Deprecated/Viewport/WidgetViewport.h"
-#include "../../Framework/Deprecated/Volumes/ISlicedVolume.h"
-#include "../../Framework/Deprecated/Volumes/IVolumeLoader.h"
-
-#include <list>
-#include <boost/thread.hpp>
-#include "../StoneApplicationContext.h"
-
-namespace OrthancStone
-{
-  class NativeStoneApplicationContext : public StoneApplicationContext
-  {
-  private:
-    static void UpdateThread(NativeStoneApplicationContext* that);
-
-    boost::recursive_mutex    globalMutex_;
-    Deprecated::WidgetViewport  centralViewport_;
-    boost::thread   updateThread_;
-    bool            stopped_;
-    unsigned int    updateDelayInMs_;
-
-  public:
-    class GlobalMutexLocker: public boost::noncopyable
-    {
-    private:
-      NativeStoneApplicationContext&        that_;
-      boost::recursive_mutex::scoped_lock   lock_;
-      
-    public:
-      GlobalMutexLocker(NativeStoneApplicationContext& that) :
-        that_(that),
-        lock_(that.globalMutex_)
-      {
-      }
-
-      void SetCentralWidget(boost::shared_ptr<Deprecated::IWidget> widget);
-
-      Deprecated::IViewport& GetCentralViewport() 
-      {
-        return that_.centralViewport_;
-      }
-
-      void SetUpdateDelay(unsigned int delayInMs)
-      {
-        that_.updateDelayInMs_ = delayInMs;
-      }
-    };
-
-    NativeStoneApplicationContext();
-
-    void Start();
-
-    void Stop();
-  };
-}
--- a/Applications/Generic/NativeStoneApplicationRunner.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,265 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#if ORTHANC_ENABLE_THREADS != 1
-#error this file shall be included only with the ORTHANC_ENABLE_THREADS set to 1
-#endif
-
-#include "NativeStoneApplicationRunner.h"
-
-#include "../../Framework/Deprecated/Toolbox/MessagingToolbox.h"
-#include "../../Platforms/Generic/OracleWebService.h"
-#include "../../Platforms/Generic/OracleDelayedCallExecutor.h"
-#include "NativeStoneApplicationContext.h"
-
-#include <Core/Logging.h>
-#include <Core/HttpClient.h>
-#include <Core/Toolbox.h>
-#include <Core/OrthancException.h>
-#include <Plugins/Samples/Common/OrthancHttpConnection.h>
-
-#include <boost/program_options.hpp>
-
-namespace OrthancStone
-{
-  // Anonymous namespace to avoid clashes against other compilation modules
-  namespace
-  {
-    class LogStatusBar : public Deprecated::IStatusBar
-    {
-    public:
-      virtual void ClearMessage()
-      {
-      }
-
-      virtual void SetMessage(const std::string& message)
-      {
-        LOG(WARNING) << message;
-      }
-    };
-  }
-
-  int NativeStoneApplicationRunner::Execute(int argc,
-                                            char* argv[])
-  {
-    /******************************************************************
-     * Initialize all the subcomponents of Orthanc Stone
-     ******************************************************************/
-
-    Orthanc::Logging::Initialize();
-    Orthanc::Toolbox::InitializeOpenSsl();
-    Orthanc::HttpClient::GlobalInitialize();
-
-    Initialize();
-
-    /******************************************************************
-     * Declare and parse the command-line options of the application
-     ******************************************************************/
-
-    boost::program_options::options_description options;
-
-    { // generic options
-      boost::program_options::options_description generic("Generic options");
-      generic.add_options()
-          ("help", "Display this help and exit")
-          ("verbose", "Be verbose in logs")
-          ("orthanc", boost::program_options::value<std::string>()->
-            default_value("http://localhost:8042/"),
-           "URL to the Orthanc server")
-          ("username", "Username for the Orthanc server")
-          ("password", "Password for the Orthanc server")
-          ("https-verify", boost::program_options::value<bool>()->
-            default_value(true), "Check HTTPS certificates")
-          ;
-
-      options.add(generic);
-    }
-
-    // platform specific options
-    DeclareCommandLineOptions(options);
-    
-    // application specific options
-    application_->DeclareStartupOptions(options);
-
-    boost::program_options::variables_map parameters;
-    bool error = false;
-
-    try
-    {
-      boost::program_options::store(
-        boost::program_options::command_line_parser(argc, argv).
-          options(options).allow_unregistered().run(), parameters);
-      boost::program_options::notify(parameters);
-    }
-    catch (boost::program_options::error& e)
-    {
-      LOG(ERROR) << 
-        "Error while parsing the command-line arguments: " << e.what();
-      error = true;
-    }
-
-
-    /******************************************************************
-     * Configure the application with the command-line parameters
-     ******************************************************************/
-
-    if (error || parameters.count("help"))
-    {
-      std::cout << std::endl;
-
-      std::cout << options << "\n";
-      return error ? -1 : 0;
-    }
-
-    if (parameters.count("https-verify") &&
-        !parameters["https-verify"].as<bool>())
-    {
-      LOG(WARNING) << "Turning off verification of HTTPS certificates (unsafe)";
-      Orthanc::HttpClient::ConfigureSsl(false, "");
-    }
-
-    LOG(ERROR) << "???????? if (parameters.count(\"verbose\"))";
-    if (parameters.count("verbose"))
-    {
-      LOG(ERROR) << "parameters.count(\"verbose\") != 0";
-      Orthanc::Logging::EnableInfoLevel(true);
-      LOG(INFO) << "Verbose logs are enabled";
-    }
-
-    LOG(ERROR) << "???????? if (parameters.count(\"trace\"))";
-    if (parameters.count("trace"))
-    {
-      LOG(ERROR) << "parameters.count(\"trace\") != 0";
-      Orthanc::Logging::EnableTraceLevel(true);
-      VLOG(1) << "Trace logs are enabled";
-    }
-
-    ParseCommandLineOptions(parameters);
-
-    bool success = true;
-    try
-    {
-      /****************************************************************
-       * Initialize the connection to the Orthanc server
-       ****************************************************************/
-
-      Orthanc::WebServiceParameters webServiceParameters;
-
-      if (parameters.count("orthanc"))
-      {
-        webServiceParameters.SetUrl(parameters["orthanc"].as<std::string>());
-      }
-
-      if (parameters.count("username") && parameters.count("password"))
-      {
-        webServiceParameters.SetCredentials(parameters["username"].
-          as<std::string>(),
-          parameters["password"].as<std::string>());
-      }
-
-      LOG(WARNING) << "URL to the Orthanc REST API: " << 
-        webServiceParameters.GetUrl();
-
-      {
-        OrthancPlugins::OrthancHttpConnection orthanc(webServiceParameters);
-        if (!Deprecated::MessagingToolbox::CheckOrthancVersion(orthanc))
-        {
-          LOG(ERROR) << "Your version of Orthanc is incompatible with Stone of "
-            << "Orthanc, please upgrade";
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
-        }
-      }
-
-
-      /****************************************************************
-       * Initialize the application
-       ****************************************************************/
-
-      LOG(WARNING) << "Creating the widgets of the application";
-
-      LogStatusBar statusBar;
-
-      NativeStoneApplicationContext context;
-
-      {
-        // use multiple threads to execute asynchronous tasks like 
-        // download content
-        Deprecated::Oracle oracle(6); 
-        oracle.Start();
-
-        {
-          boost::shared_ptr<Deprecated::OracleWebService> webService
-            (new Deprecated::OracleWebService(oracle, webServiceParameters, context));
-          context.SetWebService(webService);
-          context.SetOrthancBaseUrl(webServiceParameters.GetUrl());
-
-          Deprecated::OracleDelayedCallExecutor delayedExecutor(oracle, context);
-          context.SetDelayedCallExecutor(delayedExecutor);
-
-          application_->Initialize(&context, statusBar, parameters);
-
-          {
-            NativeStoneApplicationContext::GlobalMutexLocker locker(context);
-            locker.SetCentralWidget(application_->GetCentralWidget());
-            locker.GetCentralViewport().SetStatusBar(statusBar);
-          }
-
-          std::string title = application_->GetTitle();
-          if (title.empty())
-          {
-            title = "Stone of Orthanc";
-          }
-
-          /****************************************************************
-           * Run the application
-           ****************************************************************/
-
-          Run(context, title, argc, argv);
-
-          /****************************************************************
-           * Finalize the application
-           ****************************************************************/
-
-          oracle.Stop();
-        }
-      }
-
-      LOG(WARNING) << "The application is stopping";
-      application_->Finalize();
-    }
-    catch (Orthanc::OrthancException& e)
-    {
-      LOG(ERROR) << "EXCEPTION: " << e.What();
-      success = false;
-    }
-
-
-    /******************************************************************
-     * Finalize all the subcomponents of Orthanc Stone
-     ******************************************************************/
-
-    Finalize();
-    Orthanc::HttpClient::GlobalFinalize();
-    Orthanc::Toolbox::FinalizeOpenSsl();
-
-    return (success ? 0 : -1);
-  }
-}
--- a/Applications/Generic/NativeStoneApplicationRunner.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/**
- * 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 "../IStoneApplication.h"
-
-#if ORTHANC_ENABLE_THREADS != 1
-#error this file shall be included only with the ORTHANC_ENABLE_THREADS set to 1
-#endif
-
-namespace OrthancStone
-{
-  class NativeStoneApplicationContext;
-
-  class NativeStoneApplicationRunner
-  {
-  protected:
-    boost::shared_ptr<IStoneApplication>  application_;
-    
-  public:
-    NativeStoneApplicationRunner(boost::shared_ptr<IStoneApplication> application)
-      : application_(application)
-    {
-    }
-    int Execute(int argc,
-                char* argv[]);
-
-    virtual void Initialize() = 0;
-    virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) = 0;
-    virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters) = 0;
-
-    virtual void Run(NativeStoneApplicationContext& context, const std::string& title, int argc, char* argv[]) = 0;
-    virtual void Finalize() = 0;
-  };
-
-}
--- a/Applications/Generic/Scene2DInteractor.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/**
- * 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 "../../Framework/Scene2D/PointerEvent.h"
-#include "../../Framework/Scene2DViewport/ViewportController.h"
-//#include "../../Framework/Scene2D/Internals/CompositorHelper.h"
-#include "GuiAdapter.h"
-
-
-namespace OrthancStone
-{
-
-  class Scene2DInteractor
-  {
-  protected:
-    boost::shared_ptr<ViewportController>       viewportController_;
-//    boost::shared_ptr<ICompositor>              compositor_;
-
-  public:
-    Scene2DInteractor(boost::shared_ptr<ViewportController> viewportController) :
-      viewportController_(viewportController)
-    {}
-
-//    void SetCompositor(boost::shared_ptr<ICompositor> compositor)
-//    {
-//      compositor_ = compositor;
-//    }
-
-    virtual bool OnMouseEvent(const GuiAdapterMouseEvent& guiEvent, const PointerEvent& pointerEvent) = 0; // returns true if it has handled the event
-    virtual bool OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent) = 0; // returns true if it has handled the event
-    virtual bool OnWheelEvent(const GuiAdapterWheelEvent& guiEvent) = 0; // returns true if it has handled the event
-
-  };
-}
--- a/Applications/IStoneApplication.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-/**
- * 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 "StoneApplicationContext.h"
-#include "../Framework/Deprecated/Viewport/WidgetViewport.h"
-
-#include <boost/program_options.hpp>
-#include <json/json.h>
-
-namespace OrthancStone
-{
-#if ORTHANC_ENABLE_QT==1
-  class QStoneMainWindow;
-#endif
-
-  // a StoneApplication is an application that can actually be executed
-  // in multiple environments.  i.e: it can run natively integrated in a QtApplication
-  // or it can be executed as part of a WebPage when compiled into WebAssembly.
-  class IStoneApplication : public boost::noncopyable
-  {
-  protected:
-    StoneApplicationContext* context_;
-
-  public:
-    virtual ~IStoneApplication()
-    {
-    }
-
-    virtual void DeclareStartupOptions(boost::program_options::options_description& options) = 0;
-    virtual void Initialize(StoneApplicationContext* context,
-                            Deprecated::IStatusBar& statusBar,
-                            const boost::program_options::variables_map& parameters) = 0;
-
-    /**
-      This method is meant to process messages received from the outside world (i.e. GUI)
-    */
-    virtual void HandleSerializedMessage(const char* data) = 0;
-
-#if ORTHANC_ENABLE_WASM==1
-    virtual void InitializeWasm() {}  // specific initialization when the app is running in WebAssembly.  This is called after the other Initialize()
-#endif
-#if ORTHANC_ENABLE_QT==1
-      virtual QStoneMainWindow* CreateQtMainWindow() = 0;
-#endif
-
-    virtual std::string GetTitle() const = 0;
-    
-    virtual void SetCentralWidget(boost::shared_ptr<Deprecated::IWidget> widget) = 0;
-    
-    virtual boost::shared_ptr<Deprecated::IWidget> GetCentralWidget() = 0;
-    
-    virtual void Finalize() = 0;
-  };
-}
--- a/Applications/Qt/QCairoWidget.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,238 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "QCairoWidget.h"
-
-#include <QPainter>
-#include <QPaintEvent>
-
-#include <stdexcept>
-
-
-QCairoWidget::StoneObserver::StoneObserver(QCairoWidget& that,
-                                           Deprecated::IViewport& viewport,
-                                           OrthancStone::MessageBroker& broker) :
-  OrthancStone::IObserver(broker),
-  that_(that)
-{
-  // get notified each time the content of the central viewport changes
-  viewport.RegisterObserverCallback(
-    new OrthancStone::Callable<StoneObserver, Deprecated::IViewport::ViewportChangedMessage>
-    (*this, &StoneObserver::OnViewportChanged));
-}
-
-
-QCairoWidget::QCairoWidget(QWidget *parent) :
-  QWidget(parent),
-  context_(NULL)
-{
-  setFocusPolicy(Qt::StrongFocus); // catch keyPressEvents
-}
-
-
-void QCairoWidget::SetContext(OrthancStone::NativeStoneApplicationContext& context)
-{
-  context_ = &context;
-
-  {
-    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
-    observer_.reset(new StoneObserver(*this,
-                                      locker.GetCentralViewport(),
-                                      locker.GetMessageBroker()));
-  }
-}
-
-
-void QCairoWidget::paintEvent(QPaintEvent* /*event*/)
-{
-  QPainter painter(this);
-
-  if (image_.get() != NULL &&
-      context_ != NULL)
-  {
-    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
-    Deprecated::IViewport& viewport = locker.GetCentralViewport();
-    Orthanc::ImageAccessor a;
-    surface_.GetWriteableAccessor(a);
-    viewport.Render(a);
-    painter.drawImage(0, 0, *image_);
-  }
-  else
-  {
-    painter.fillRect(rect(), Qt::red);
-  }
-}
-
-OrthancStone::KeyboardModifiers GetKeyboardModifiers(QInputEvent* event)
-{
-  Qt::KeyboardModifiers qtModifiers = event->modifiers();
-  int stoneModifiers = static_cast<int>(OrthancStone::KeyboardModifiers_None);
-  if ((qtModifiers & Qt::AltModifier) != 0)
-  {
-    stoneModifiers |= static_cast<int>(OrthancStone::KeyboardModifiers_Alt);
-  }
-  if ((qtModifiers & Qt::ControlModifier) != 0)
-  {
-    stoneModifiers |= static_cast<int>(OrthancStone::KeyboardModifiers_Control);
-  }
-  if ((qtModifiers & Qt::ShiftModifier) != 0)
-  {
-    stoneModifiers |= static_cast<int>(OrthancStone::KeyboardModifiers_Shift);
-  }
-  return static_cast<OrthancStone::KeyboardModifiers>(stoneModifiers);
-}
-
-void QCairoWidget::mousePressEvent(QMouseEvent* event)
-{
-  OrthancStone::KeyboardModifiers stoneModifiers = GetKeyboardModifiers(event);
-
-  OrthancStone::MouseButton button;
-
-  switch (event->button())
-  {
-    case Qt::LeftButton:
-      button = OrthancStone::MouseButton_Left;
-      break;
-
-    case Qt::RightButton:
-      button = OrthancStone::MouseButton_Right;
-      break;
-
-    case Qt::MiddleButton:
-      button = OrthancStone::MouseButton_Middle;
-      break;
-
-    default:
-      return;  // Unsupported button
-  }
-
-  {
-    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
-    locker.GetCentralViewport().MouseDown(button, event->pos().x(), event->pos().y(), stoneModifiers, std::vector<Deprecated::Touch>());
-  }
-}
-
-
-void QCairoWidget::mouseReleaseEvent(QMouseEvent* /*eventNotUsed*/)
-{
-  OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
-  locker.GetCentralViewport().MouseLeave();
-}
-
-
-void QCairoWidget::mouseMoveEvent(QMouseEvent* event)
-{
-  OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
-  locker.GetCentralViewport().MouseMove(event->pos().x(), event->pos().y(), std::vector<Deprecated::Touch>());
-}
-
-
-void QCairoWidget::wheelEvent(QWheelEvent * event)
-{
-  OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
-
-  OrthancStone::KeyboardModifiers stoneModifiers = GetKeyboardModifiers(event);
-
-  if (event->orientation() == Qt::Vertical)
-  {
-    if (event->delta() < 0)  // TODO: compare direction with SDL and make sure we send the same directions
-    {
-       locker.GetCentralViewport().MouseWheel(OrthancStone::MouseWheelDirection_Up, event->pos().x(), event->pos().y(), stoneModifiers);
-    }
-    else
-    {
-      locker.GetCentralViewport().MouseWheel(OrthancStone::MouseWheelDirection_Down, event->pos().x(), event->pos().y(), stoneModifiers);
-    }
-  }
-}
-
-void QCairoWidget::keyPressEvent(QKeyEvent *event)
-{
-  using namespace OrthancStone;
-
-  OrthancStone::KeyboardModifiers stoneModifiers = GetKeyboardModifiers(event);
-
-  OrthancStone::KeyboardKeys keyType = OrthancStone::KeyboardKeys_Generic;
-  char keyChar = event->text()[0].toLatin1();
-
-#define CASE_QT_KEY_TO_ORTHANC(qt, o) case qt: keyType = o; break;
-  if (keyChar == 0)
-  {
-    switch (event->key())
-    {
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_Up, KeyboardKeys_Up);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_Down, KeyboardKeys_Down);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_Left, KeyboardKeys_Left);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_Right, KeyboardKeys_Right);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F1, KeyboardKeys_F1);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F2, KeyboardKeys_F2);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F3, KeyboardKeys_F3);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F4, KeyboardKeys_F4);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F5, KeyboardKeys_F5);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F6, KeyboardKeys_F6);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F7, KeyboardKeys_F7);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F8, KeyboardKeys_F8);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F9, KeyboardKeys_F9);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F10, KeyboardKeys_F10);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F11, KeyboardKeys_F11);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F12, KeyboardKeys_F12);
-    default:
-      break;
-    }
-  }
-  else if (keyChar == 127)
-  {
-    switch (event->key())
-    {
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_Delete, KeyboardKeys_Delete);
-      CASE_QT_KEY_TO_ORTHANC(Qt::Key_Backspace, KeyboardKeys_Backspace);
-    default:
-      break;
-    }
-  }
-
-  {
-    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);    
-    locker.GetCentralViewport().KeyPressed(keyType, keyChar, stoneModifiers);
-  }
-}
-
-
-void QCairoWidget::resizeEvent(QResizeEvent* event)
-{
-  grabGesture(Qt::PanGesture);
-  QWidget::resizeEvent(event);
-
-  if (event)
-  {
-    surface_.SetSize(event->size().width(), event->size().height(), true);
-
-    image_.reset(new QImage(reinterpret_cast<uchar*>(surface_.GetBuffer()),
-                            event->size().width(), 
-                            event->size().height(),
-                            surface_.GetPitch(),
-                            QImage::Format_RGB32));
-
-    {
-      OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
-      locker.GetCentralViewport().SetSize(event->size().width(), event->size().height());
-    }
-  }
-}
--- a/Applications/Qt/QCairoWidget.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
-4 * 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 "../../Applications/Generic/NativeStoneApplicationContext.h"
-#include "../../Framework/Wrappers/CairoSurface.h"
-#include "../../Framework/Deprecated/Widgets/IWidget.h"
-
-#include <QWidget>
-#include <memory>
-#include <cassert>
-
-class QCairoWidget : public QWidget
-{
-  Q_OBJECT
-
-private:
-  class StoneObserver : public OrthancStone::IObserver
-  {
-  private:
-    QCairoWidget& that_;
-    
-  public:
-    StoneObserver(QCairoWidget& that,
-                  Deprecated::IViewport& viewport,
-                  OrthancStone::MessageBroker& broker);
-
-    void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message)
-    {
-      that_.OnViewportChanged();
-    }
-  };
-  
-  std::unique_ptr<QImage>         image_;
-  OrthancStone::CairoSurface    surface_;
-  OrthancStone::NativeStoneApplicationContext* context_;
-  std::unique_ptr<StoneObserver>  observer_;
-
-protected:
-  virtual void paintEvent(QPaintEvent *event);
-
-  virtual void resizeEvent(QResizeEvent *event);
-
-  virtual void mouseMoveEvent(QMouseEvent *event);
-
-  virtual void mousePressEvent(QMouseEvent *event);
-
-  virtual void mouseReleaseEvent(QMouseEvent *event);
-
-  virtual void wheelEvent(QWheelEvent *event);
-
-  virtual void keyPressEvent(QKeyEvent *event);
-
-public:
-  explicit QCairoWidget(QWidget *parent);
- 
-  void SetContext(OrthancStone::NativeStoneApplicationContext& context);
-
-  void OnViewportChanged()
-  {
-    update();  // schedule a repaint (handled by Qt)
-    emit ContentChanged();
-  }
-
-signals:
-  void ContentChanged();
-                                               
-public slots:
-
-};
--- a/Applications/Qt/QStoneMainWindow.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "QStoneMainWindow.h"
-
-namespace OrthancStone
-{
-
-  QStoneMainWindow::QStoneMainWindow(NativeStoneApplicationContext& context,
-                                     QWidget *parent) :
-    QMainWindow(parent),
-    context_(context),
-    cairoCentralWidget_(NULL)
-  {
-  }
-
-  void QStoneMainWindow::SetCentralStoneWidget(QCairoWidget& centralWidget)
-  {
-    cairoCentralWidget_ = &centralWidget;
-    cairoCentralWidget_->SetContext(context_);
-  }
-
-  QStoneMainWindow::~QStoneMainWindow()
-  {
-  }
-}
--- a/Applications/Qt/QStoneMainWindow.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/**
- * 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 <QMainWindow>
-
-#include "QCairoWidget.h"
-#include "../Generic/NativeStoneApplicationContext.h"
-
-namespace OrthancStone
-{
-  class QStoneMainWindow : public QMainWindow
-  {
-    Q_OBJECT
-
-  private:
-    OrthancStone::NativeStoneApplicationContext& context_;
-    QCairoWidget          *cairoCentralWidget_;
-
-  protected:  // you must inherit this class
-    QStoneMainWindow(NativeStoneApplicationContext& context, QWidget *parent = 0);
-    void SetCentralStoneWidget(QCairoWidget& centralWidget);
-
-  public:
-    virtual ~QStoneMainWindow();
-  };
-
-}
--- a/Applications/Qt/QtStoneApplicationRunner.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#if ORTHANC_ENABLE_QT != 1
-#error this file shall be included only with the ORTHANC_ENABLE_QT set to 1
-#endif
-
-#include "QtStoneApplicationRunner.h"
-#include <boost/program_options.hpp>
-#include <QApplication>
-
-#include "../../Framework/Deprecated/Toolbox/MessagingToolbox.h"
-
-#include <Core/Logging.h>
-#include <Core/HttpClient.h>
-#include <Core/Toolbox.h>
-#include <Plugins/Samples/Common/OrthancHttpConnection.h>
-#include "../../Platforms/Generic/OracleWebService.h"
-
-
-namespace OrthancStone
-{
-  void QtStoneApplicationRunner::Initialize()
-  {
-  }
-
-  void QtStoneApplicationRunner::DeclareCommandLineOptions(boost::program_options::options_description& options)
-  {
-  }
-
-  void QtStoneApplicationRunner::Run(NativeStoneApplicationContext& context, const std::string& title, int argc, char* argv[])
-  {
-    context.Start();
-
-    QApplication qtApplication(argc, argv);
-    window_.reset(application_.CreateQtMainWindow());
-
-    window_->show();
-    qtApplication.exec();
-
-    context.Stop();
-  }
-
-  void QtStoneApplicationRunner::Finalize()
-  {
-  }
-
-
-}
--- a/Applications/Qt/QtStoneApplicationRunner.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/**
- * 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 "../Generic/NativeStoneApplicationRunner.h"
-#include "QStoneMainWindow.h"
-
-#if ORTHANC_ENABLE_QT != 1
-#error this file shall be included only with the ORTHANC_ENABLE_QT set to 1
-#endif
-
-namespace OrthancStone
-{
-  class QtStoneApplicationRunner : public NativeStoneApplicationRunner
-  {
-  protected:
-    std::unique_ptr<QStoneMainWindow> window_;
-
-  public:
-    QtStoneApplicationRunner(MessageBroker& broker,
-                             IStoneApplication& application)
-      : NativeStoneApplicationRunner(broker, application)
-    {
-    }
-
-
-    virtual void Initialize();
-
-    virtual void DeclareCommandLineOptions(boost::program_options::options_description& options);
-    virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters) {}
-    virtual void Run(NativeStoneApplicationContext& context, const std::string& title, int argc, char* argv[]);
-    virtual void Finalize();
-  };
-
-}
--- a/Applications/Samples/Deprecated/BasicPetCtFusionApplication.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,202 +0,0 @@
-/**
- * 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 "SampleInteractor.h"
-
-#include <Core/Logging.h>
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class BasicPetCtFusionApplication : public SampleApplicationBase
-    {
-    private:
-      class Interactor : public SampleInteractor
-      {
-      public:
-        static void SetStyle(LayeredSceneWidget& widget,
-                             bool ct,
-                             bool pet)
-        {
-          if (ct)
-          {
-            RenderStyle style;
-            style.windowing_ = ImageWindowing_Bone;
-            widget.SetLayerStyle(0, style);
-          }
-          else
-          {
-            RenderStyle style;
-            style.visible_ = false;
-            widget.SetLayerStyle(0, style);
-          }
-
-          if (ct && pet)
-          {
-            RenderStyle style;
-            style.applyLut_ = true;
-            style.alpha_ = 0.5;
-            widget.SetLayerStyle(1, style);
-          }
-          else if (pet)
-          {
-            RenderStyle style;
-            style.applyLut_ = true;
-            widget.SetLayerStyle(1, style);
-          }
-          else
-          {
-            RenderStyle style;
-            style.visible_ = false;
-            widget.SetLayerStyle(1, style);
-          }
-        }
-
-
-        static bool IsVisible(LayeredSceneWidget& widget,
-                              size_t layer)
-        {
-          RenderStyle style = widget.GetLayerStyle(layer);
-          return style.visible_;
-        }
-
-
-        static void ToggleInterpolation(LayeredSceneWidget& widget,
-                                        size_t layer)
-        {
-          RenderStyle style = widget.GetLayerStyle(layer);
-         
-          if (style.interpolation_ == ImageInterpolation_Bilinear)
-          {
-            style.interpolation_ = ImageInterpolation_Nearest;
-          }
-          else
-          {
-            style.interpolation_ = ImageInterpolation_Bilinear;
-          }
-
-          widget.SetLayerStyle(layer, style);
-        }
-
-
-        Interactor(VolumeImage& volume,
-                   VolumeProjection projection, 
-                   bool reverse) :
-          SampleInteractor(volume, projection, reverse)
-        {
-        }
-
-
-        virtual void KeyPressed(WorldSceneWidget& widget,
-                                char key,
-                                KeyboardModifiers modifiers,
-                                IStatusBar* statusBar)
-        {
-          LayeredSceneWidget& layered = dynamic_cast<LayeredSceneWidget&>(widget);
-
-          switch (key)
-          {
-            case 'c':
-              // Toggle the visibility of the CT layer
-              SetStyle(layered, !IsVisible(layered, 0), IsVisible(layered, 1));
-              break;
-
-            case 'p':
-              // Toggle the visibility of the PET layer
-              SetStyle(layered, IsVisible(layered, 0), !IsVisible(layered, 1));
-              break;
-
-            case 'i':
-            {
-              // Toggle on/off the interpolation
-              ToggleInterpolation(layered, 0);
-              ToggleInterpolation(layered, 1);
-              break;
-            }
-
-            default:
-              break;
-          }
-        }
-      };
-
-
-    public:
-      virtual void DeclareCommandLineOptions(boost::program_options::options_description& options)
-      {
-        boost::program_options::options_description generic("Sample options");
-        generic.add_options()
-          ("ct", boost::program_options::value<std::string>(), 
-           "Orthanc ID of the CT series")
-          ("pet", boost::program_options::value<std::string>(), 
-           "Orthanc ID of the PET series")
-          ("threads", boost::program_options::value<unsigned int>()->default_value(3), 
-           "Number of download threads for the CT series")
-          ;
-
-        options.add(generic);    
-      }
-
-      virtual void Initialize(BasicApplicationContext& context,
-                              IStatusBar& statusBar,
-                              const boost::program_options::variables_map& parameters)
-      {
-        using namespace OrthancStone;
-
-        if (parameters.count("ct") != 1 ||
-            parameters.count("pet") != 1)
-        {
-          LOG(ERROR) << "The series ID is missing";
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-        }
-
-        std::string ct = parameters["ct"].as<std::string>();
-        std::string pet = parameters["pet"].as<std::string>();
-        unsigned int threads = parameters["threads"].as<unsigned int>();
-
-        VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads);
-        VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1);
-
-        // Take the PET volume as the reference for the slices
-        std::unique_ptr<Interactor> interactor(new Interactor(petVolume, VolumeProjection_Axial, false /* don't reverse normal */));
-
-        std::unique_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget);
-        widget->AddLayer(new VolumeImage::LayerFactory(ctVolume));
-        widget->AddLayer(new VolumeImage::LayerFactory(petVolume));
-        widget->SetSlice(interactor->GetCursor().GetCurrentSlice());
-        widget->SetInteractor(*interactor);
-
-        Interactor::SetStyle(*widget, true, true);   // Initially, show both CT and PET layers
-
-        context.AddInteractor(interactor.release());
-        context.SetCentralWidget(widget.release());
-
-        statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode");
-        statusBar.SetMessage("Use the key \"c\" to show/hide the CT layer");
-        statusBar.SetMessage("Use the key \"p\" to show/hide the PET layer");
-        statusBar.SetMessage("Use the key \"i\" to toggle the smoothing of the images");
-      }
-    };
-  }
-}
--- a/Applications/Samples/Deprecated/CMakeLists.txt	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,292 +0,0 @@
-# Usage (Linux):
-# to build the WASM samples
-# source ~/Downloads/emsdk/emsdk_env.sh && cmake -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc -DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON
-# to build the Qt samples
-
-cmake_minimum_required(VERSION 2.8.3)
-project(OrthancStone)
-
-include(../../../Resources/CMake/OrthancStoneParameters.cmake)
-
-set(ENABLE_STONE_DEPRECATED ON)  # Need deprecated classes for these samples
-set(EMSCRIPTEN_SET_LLVM_WASM_BACKEND ON)
-
-include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
-DownloadPackage(
-  "a24b8136b8f3bb93f166baf97d9328de"
-  "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip"
-  "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83")
-
-set(ORTHANC_STONE_APPLICATION_RESOURCES
-  UBUNTU_FONT  ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf
-  )
-
-if (OPENSSL_NO_CAPIENG)
-add_definitions(-DOPENSSL_NO_CAPIENG=1)
-endif()
-
-
-# the following block has been borrowed from orthanc/**/Compiler.cmake
-if (MSVC_MULTIPLE_PROCESSES)
-# "If you omit the processMax argument in the /MP option, the
-# compiler obtains the number of effective processors from the
-# operating system, and then creates one process per effective
-# processor"
-# https://blog.kitware.com/cmake-building-with-all-your-cores/
-# https://docs.microsoft.com/en-us/cpp/build/reference/mp-build-with-multiple-processes
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
-endif()
-
-#set(ENABLE_DCMTK ON)
-
-set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application")
-set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application")
-set(ENABLE_WASM OFF CACHE BOOL "Target WASM application")
-
-if (ENABLE_WASM)
-  #####################################################################
-  ## Configuration of the Emscripten compiler for WebAssembly target
-  #####################################################################
-
-  set(WASM_FLAGS "-s WASM=1")
-  set(WASM_FLAGS "${WASM_FLAGS} -s STRICT=1") # drops support for all deprecated build options
-  set(WASM_FLAGS "${WASM_FLAGS} -s FILESYSTEM=1") # if we don't include it, gen_uuid.c fails to build because srand, getpid(), ... are not defined
-  set(WASM_FLAGS "${WASM_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") # actually enable exception catching 
-  set(WASM_FLAGS "${WASM_FLAGS} -s ERROR_ON_MISSING_LIBRARIES=1")
-  
-  if (CMAKE_BUILD_TYPE MATCHES DEBUG)
-    set(WASM_FLAGS "${WASM_FLAGS} -g4") # generate debug information
-    set(WASM_FLAGS "${WASM_FLAGS} -s ASSERTIONS=2") # more runtime checks
-  else()
-    set(WASM_FLAGS "${WASM_FLAGS} -Os") # optimize for web (speed and size)
-  endif()
-
-  set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module")
-
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}")
-
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${WASM_FLAGS}")  # not always clear which flags are for the compiler and which one are for the linker -> pass them all to the linker too
-  # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Applications/Samples/samples-library.js")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"'")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_STACK=128000000")
-
-  add_definitions(-DORTHANC_ENABLE_WASM=1)
-  set(ORTHANC_SANDBOXED ON)
-
-elseif (ENABLE_QT OR ENABLE_SDL)
-
-  set(ENABLE_NATIVE ON)
-  set(ORTHANC_SANDBOXED OFF)
-  set(ENABLE_CRYPTO_OPTIONS ON)
-  set(ENABLE_GOOGLE_TEST ON)
-  set(ENABLE_WEB_CLIENT ON)
-
-else()
-  set(ENABLE_NATIVE ON)
-  set(ENABLE_OPENGL OFF)
-  
-endif()
-
-
-#####################################################################
-## Configuration for Orthanc
-#####################################################################
-
-# include(../../Resources/CMake/Version.cmake)
-
-if (ORTHANC_STONE_VERSION STREQUAL "mainline")
-  set(ORTHANC_FRAMEWORK_VERSION "mainline")
-  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
-else()
-  set(ORTHANC_FRAMEWORK_VERSION "1.4.1")
-  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
-endif()
-
-set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")")
-set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"")
-set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
-
-add_definitions(
-  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
-  )
-
-
-#####################################################################
-## Build a static library containing the Orthanc Stone framework
-#####################################################################
-
-
-LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
-
-include(../../../Resources/CMake/OrthancStoneConfiguration.cmake)
-
-add_library(OrthancStone STATIC
-  ${ORTHANC_STONE_SOURCES}
-  )
-
-#####################################################################
-## Build all the sample applications
-#####################################################################
-
-include_directories(${ORTHANC_STONE_ROOT})
-
-# files common to all samples
-list(APPEND SAMPLE_APPLICATIONS_SOURCES
-  ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleInteractor.h
-  ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleApplicationBase.h
-  )
-
-if (ENABLE_QT)
-  list(APPEND SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleQtApplicationRunner.h
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindow.cpp
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.cpp
-    )
-
-  ORTHANC_QT_WRAP_UI(SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindow.ui
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.ui
-    )
-
-  ORTHANC_QT_WRAP_CPP(SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.h
-    ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.h
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindow.h
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.h
-    )
-endif()
-
-if (ENABLE_NATIVE)
-  list(APPEND SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleMainNative.cpp
-    )
-
-elseif (ENABLE_WASM)
-
-  list(APPEND SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleMainWasm.cpp
-    ${STONE_WASM_SOURCES}
-    )
-endif()
-
-
-macro(BuildSingleFileSample Target Header Sample)
-  add_executable(${Target}
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/${Header}
-    ${SAMPLE_APPLICATIONS_SOURCES}
-    )
-  set_target_properties(${Target} PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=${Sample})
-  target_link_libraries(${Target} OrthancStone)
-endmacro()
-
-
-if (ENABLE_SDL)
-  #BuildSingleFileSample(OrthancStoneEmpty EmptyApplication.h 1)
-  #BuildSingleFileSample(OrthancStoneTestPattern TestPatternApplication.h 2)
-  BuildSingleFileSample(OrthancStoneSingleFrame SingleFrameApplication.h 3)
-  #BuildSingleFileSample(OrthancStoneSingleVolume SingleVolumeApplication.h 4)
-  #BuildSingleFileSample(OrthancStoneBasicPetCtFusion 5)
-  #BuildSingleFileSample(OrthancStoneSynchronizedSeries 6)
-  #BuildSingleFileSample(OrthancStoneLayoutPetCtFusion 7)
-  BuildSingleFileSample(OrthancStoneSimpleViewerSingleFile SimpleViewerApplicationSingleFile.h 8)  # we keep that one just as a sample before we convert another sample to this pattern
-  BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9)
-endif()
-  
-##### SimpleViewer sample (Qt and WASM only) #######
-
-if (ENABLE_QT OR ENABLE_WASM)
-
-    if (ENABLE_QT)
-      list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.cpp
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/mainQt.cpp
-        )
-
-      ORTHANC_QT_WRAP_UI(SIMPLE_VIEWER_APPLICATION_SOURCES
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui
-        )
-
-      ORTHANC_QT_WRAP_CPP(SIMPLE_VIEWER_APPLICATION_SOURCES
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.h
-        )
-
-elseif (ENABLE_WASM)
-        list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES
-            ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Wasm/mainWasm.cpp
-            ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp
-            ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h
-            ${STONE_WASM_SOURCES}
-          )
-    endif()
-
-    add_executable(OrthancStoneSimpleViewer
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/AppStatus.h
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.cpp
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.h
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.cpp
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.h
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.cpp
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.h
-      ${SIMPLE_VIEWER_APPLICATION_SOURCES}
-      )
-    target_link_libraries(OrthancStoneSimpleViewer OrthancStone)
-
-    BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9)
-endif()
-
-#####################################################################
-## Build the unit tests
-#####################################################################
-
-if (ENABLE_NATIVE)
-  add_executable(UnitTests
-    ${GOOGLE_TEST_SOURCES}
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/GenericToolboxTests.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/ImageToolboxTests.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/PixelTestPatternsTests.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStrategy.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStructureSet.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/UnitTestsMain.cpp
-    )
-
-  target_link_libraries(UnitTests OrthancStone)
-
-  add_custom_command(
-    TARGET UnitTests
-    POST_BUILD
-    COMMAND ${CMAKE_COMMAND} -E copy
-      "${ORTHANC_STONE_ROOT}/UnitTestsSources/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json" 
-      "$<TARGET_FILE_DIR:UnitTests>/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json"
-    )
-
-endif()
-
-#####################################################################
-## Generate the documentation if Doxygen is present
-#####################################################################
-
-find_package(Doxygen)
-if (DOXYGEN_FOUND)
-  configure_file(
-    ${ORTHANC_STONE_ROOT}/Resources/OrthancStone.doxygen
-    ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen
-    @ONLY)
-
-  add_custom_target(doc
-    ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen
-    COMMENT "Generating documentation with Doxygen" VERBATIM
-    )
-else()
-  message("Doxygen not found. The documentation will not be built.")
-endif()
--- a/Applications/Samples/Deprecated/CMakeLists.txt.old	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,248 +0,0 @@
-# Usage: see README file
-
-cmake_minimum_required(VERSION 2.8.3)
-
-# Automatically link Qt executables to qtmain target on Windows
-# ("OLD" == do not link)
-if(POLICY CMP0020)
-  cmake_policy(SET CMP0020 OLD)
-endif()
-
-# Only interpret if() arguments as variables or keywords when unquoted.
-# NEW = do NOT dereference *quoted* variables
-if(POLICY CMP0054)
-  cmake_policy(SET CMP0054 NEW)
-endif()
-
-project(OrthancStone)
-
-include(../../Resources/CMake/OrthancStoneParameters.cmake)
-
-#set(ENABLE_DCMTK ON)
-
-set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application")
-set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application")
-set(ENABLE_WASM OFF CACHE BOOL "Target WASM application")
-
-# TODO: replace or compute STONE_SOURCES_DIR from CMAKE_CURRENT_LIST_FILE
-
-if (ENABLE_WASM)
-  #####################################################################
-  ## Configuration of the Emscripten compiler for WebAssembly target
-  #####################################################################
-
-  set(WASM_FLAGS "-s WASM=1 -O0 -g0")
-  message("*****************************************************************************")
-  message("WARNING: optimizations are disabled in emcc!!! Enable them for production use")
-  message("*****************************************************************************")
-  set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module")
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js  -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
-
-  # Handling of memory
-  #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")  # Resize
-  #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912")  # 512MB
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"' -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=536870912 -s TOTAL_STACK=128000000")  # 512MB + resize
-  #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=1073741824")  # 1GB + resize
-
-  # To debug exceptions
-  #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DEMANGLE_SUPPORT=1 -s ASSERTIONS=2")
-
-  add_definitions(-DORTHANC_ENABLE_WASM=1)
-  set(ORTHANC_SANDBOXED ON)
-
-elseif (ENABLE_QT OR ENABLE_SDL)
-
-  set(ENABLE_NATIVE ON)
-  set(ORTHANC_SANDBOXED OFF)
-  set(ENABLE_CRYPTO_OPTIONS ON)
-  set(ENABLE_GOOGLE_TEST ON)
-  set(ENABLE_WEB_CLIENT ON)
-
-endif()
-
-#####################################################################
-## Configuration for Orthanc
-#####################################################################
-
-# include(../../Resources/CMake/Version.cmake)
-
-if (ORTHANC_STONE_VERSION STREQUAL "mainline")
-  set(ORTHANC_FRAMEWORK_VERSION "mainline")
-  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
-else()
-  set(ORTHANC_FRAMEWORK_VERSION "1.4.1")
-  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
-endif()
-
-set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")")
-set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"")
-set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
-
-#####################################################################
-## Build a static library containing the Orthanc Stone framework
-#####################################################################
-
-LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
-
-include(../../Resources/CMake/OrthancStoneConfiguration.cmake)
-
-add_library(OrthancStone STATIC
-  ${ORTHANC_STONE_SOURCES}
-  )
-
-#####################################################################
-## Build all the sample applications
-#####################################################################
-
-include_directories(${ORTHANC_STONE_ROOT})
-
-# files common to all samples
-list(APPEND SAMPLE_APPLICATIONS_SOURCES
-  ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleInteractor.h
-  ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleApplicationBase.h
-  )
-
-if (ENABLE_QT)
-  list(APPEND SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleQtApplicationRunner.h
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.cpp
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.cpp
-    )
-
-  ORTHANC_QT_WRAP_UI(SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.ui
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.ui
-    )
-
-  ORTHANC_QT_WRAP_CPP(SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.h
-    ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.h
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.h
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.h
-    )
-endif()
-
-if (ENABLE_NATIVE)
-  list(APPEND SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleMainNative.cpp
-    )
-
-elseif (ENABLE_WASM)
-
-  list(APPEND SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleMainWasm.cpp
-    ${STONE_WASM_SOURCES}
-    )
-endif()
-
-
-macro(BuildSingleFileSample Target Header Sample)
-  add_executable(${Target}
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/${Header}
-    ${SAMPLE_APPLICATIONS_SOURCES}
-    )
-  set_target_properties(${Target} PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=${Sample})
-  target_link_libraries(${Target} OrthancStone)
-  
-  if (ENABLE_QT AND (CMAKE_SYSTEM_NAME STREQUAL "Windows"))
-    message("(ENABLE_QT and (CMAKE_SYSTEM_NAME matches \"Windows\")) is true")
-    add_custom_command(
-      TARGET ${Target} POST_BUILD
-      COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Core> $<TARGET_FILE_DIR:${Target}>
-      COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Widgets> $<TARGET_FILE_DIR:${Target}>
-      COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Gui> $<TARGET_FILE_DIR:${Target}>
-    )
-  endif()
-endmacro()
-
-#BuildSingleFileSample(OrthancStoneEmpty EmptyApplication.h 1)
-#BuildSingleFileSample(OrthancStoneTestPattern TestPatternApplication.h 2)
-BuildSingleFileSample(OrthancStoneSingleFrame SingleFrameApplication.h 3)
-#BuildSingleFileSample(OrthancStoneSingleVolume SingleVolumeApplication.h 4)
-#BuildSingleFileSample(OrthancStoneBasicPetCtFusion 5)
-#BuildSingleFileSample(OrthancStoneSynchronizedSeries 6)
-#BuildSingleFileSample(OrthancStoneLayoutPetCtFusion 7)
-BuildSingleFileSample(OrthancStoneSimpleViewerSingleFile SimpleViewerApplicationSingleFile.h 8)  # we keep that one just as a sample before we convert another sample to this pattern
-BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9)
-
-##### SimpleViewer sample (Qt and WASM only) #######
-
-if (ENABLE_QT OR ENABLE_WASM)
-
-      # GenerateCodeFromFlatBufferSchema("${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ApplicationCommands.fbs")
-
-      list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES ${FLATC_AUTOGENERATED_SOURCES})
-      message(STATUS "SIMPLE_VIEWER_APPLICATION_SOURCES = ${SIMPLE_VIEWER_APPLICATION_SOURCES}")
-      message(STATUS "FLATC_AUTOGENERATED_SOURCES = ${FLATC_AUTOGENERATED_SOURCES}")
-
-    if (ENABLE_QT)
-      list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.cpp
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.ui
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/mainQt.cpp
-        )
-
-      ORTHANC_QT_WRAP_UI(SIMPLE_VIEWER_APPLICATION_SOURCES
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.ui
-        )
-
-      ORTHANC_QT_WRAP_CPP(SIMPLE_VIEWER_APPLICATION_SOURCES
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.h
-        )
-
-elseif (ENABLE_WASM)
-        list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES
-            ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/mainWasm.cpp
-            ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp
-            ${STONE_WASM_SOURCES}
-          )
-    endif()
-
-    add_executable(OrthancStoneSimpleViewer
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/AppStatus.h
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Messages.h
-      ${SIMPLE_VIEWER_APPLICATION_SOURCES}
-      )
-    target_link_libraries(OrthancStoneSimpleViewer OrthancStone)
-
-endif()
-
-#####################################################################
-## Build the unit tests
-#####################################################################
-
-if (ENABLE_NATIVE)
-  add_executable(UnitTests
-    ${GOOGLE_TEST_SOURCES}
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestExceptions.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/UnitTestsMain.cpp
-    )
-
-  target_link_libraries(UnitTests OrthancStone)
-endif()
-
-#####################################################################
-## Generate the documentation if Doxygen is present
-#####################################################################
-
-find_package(Doxygen)
-if (DOXYGEN_FOUND)
-  configure_file(
-    ${ORTHANC_STONE_ROOT}/Resources/OrthancStone.doxygen
-    ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen
-    @ONLY)
-
-  add_custom_target(doc
-    ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen
-    COMMENT "Generating documentation with Doxygen" VERBATIM
-    )
-else()
-  message("Doxygen not found. The documentation will not be built.")
-endif()
--- a/Applications/Samples/Deprecated/EmptyApplication.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/**
- * 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 "SampleApplicationBase.h"
-
-#include "../../../Framework/Widgets/EmptyWidget.h"
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class EmptyApplication : public SampleApplicationBase
-    {
-    public:
-      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
-      {
-        boost::program_options::options_description generic("Sample options");
-        generic.add_options()
-          ("red", boost::program_options::value<int>()->default_value(255), "Background color: red channel")
-          ("green", boost::program_options::value<int>()->default_value(0), "Background color: green channel")
-          ("blue", boost::program_options::value<int>()->default_value(0), "Background color: blue channel")
-          ;
-
-        options.add(generic);    
-      }
-
-      virtual void Initialize(IStatusBar& statusBar,
-                              const boost::program_options::variables_map& parameters)
-      {
-        int red = parameters["red"].as<int>();
-        int green = parameters["green"].as<int>();
-        int blue = parameters["blue"].as<int>();
-
-        context_->SetCentralWidget(new EmptyWidget(red, green, blue));
-      }
-    };
-  }
-}
--- a/Applications/Samples/Deprecated/LayoutPetCtFusionApplication.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,398 +0,0 @@
-/**
- * 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 "SampleInteractor.h"
-
-#include "../../../Framework/Layers/ReferenceLineFactory.h"
-#include "../../../Framework/Layers/DicomStructureSetSlicer.h"
-#include "../../../Framework/Widgets/LayoutWidget.h"
-
-#include <Core/Logging.h>
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class LayoutPetCtFusionApplication : 
-      public SampleApplicationBase,
-      public LayeredSceneWidget::ISliceObserver,
-      public WorldSceneWidget::IWorldObserver
-    {
-    private:
-      class Interactor : public SampleInteractor
-      {
-      private:
-        LayoutPetCtFusionApplication& that_;
-
-      public:
-        Interactor(LayoutPetCtFusionApplication& that,
-                   VolumeImage& volume,
-                   VolumeProjection projection, 
-                   bool reverse) :
-          SampleInteractor(volume, projection, reverse),
-          that_(that)
-        {
-        }
-
-        virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
-                                                            const SliceGeometry& slice,
-                                                            const ViewportGeometry& view,
-                                                            MouseButton button,
-                                                            double x,
-                                                            double y,
-                                                            IStatusBar* statusBar)
-        {
-          if (button == MouseButton_Left)
-          {
-            // Center the sibling views over the clicked point
-            Vector p = slice.MapSliceToWorldCoordinates(x, y);
-
-            if (statusBar != NULL)
-            {
-              char buf[64];
-              sprintf(buf, "Click on coordinates (%.02f,%.02f,%.02f) in cm", p[0] / 10.0, p[1] / 10.0, p[2] / 10.0);
-              statusBar->SetMessage(buf);
-            }
-
-            that_.interactorAxial_->LookupSliceContainingPoint(*that_.ctAxial_, p);
-            that_.interactorCoronal_->LookupSliceContainingPoint(*that_.ctCoronal_, p);
-            that_.interactorSagittal_->LookupSliceContainingPoint(*that_.ctSagittal_, p);
-          }
-
-          return NULL;
-        }
-
-        virtual void KeyPressed(WorldSceneWidget& widget,
-                                char key,
-                                KeyboardModifiers modifiers,
-                                IStatusBar* statusBar)
-        {
-          if (key == 's')
-          {
-            that_.FitContent();
-          }
-        }
-      };
-
-      bool                 processingEvent_;
-      Interactor*          interactorAxial_;
-      Interactor*          interactorCoronal_;
-      Interactor*          interactorSagittal_;
-      LayeredSceneWidget*  ctAxial_;
-      LayeredSceneWidget*  ctCoronal_;
-      LayeredSceneWidget*  ctSagittal_;
-      LayeredSceneWidget*  petAxial_;
-      LayeredSceneWidget*  petCoronal_;
-      LayeredSceneWidget*  petSagittal_;
-      LayeredSceneWidget*  fusionAxial_;
-      LayeredSceneWidget*  fusionCoronal_;
-      LayeredSceneWidget*  fusionSagittal_;
-
-
-      void FitContent()
-      {
-        petAxial_->FitContent();
-        petCoronal_->FitContent();
-        petSagittal_->FitContent();
-      }
-
-
-      void AddLayer(LayeredSceneWidget& widget,
-                    VolumeImage& volume,
-                    bool isCt)
-      {
-        size_t layer;
-        widget.AddLayer(layer, new VolumeImage::LayerFactory(volume));
-
-        if (isCt)
-        {
-          RenderStyle style; 
-          style.windowing_ = ImageWindowing_Bone;
-          widget.SetLayerStyle(layer, style);
-        }
-        else
-        {
-          RenderStyle style; 
-          style.applyLut_ = true;
-          style.alpha_ = (layer == 0 ? 1.0f : 0.5f);
-          widget.SetLayerStyle(layer, style);
-        }
-      }
-
-
-      void ConnectSiblingLocations(LayeredSceneWidget& axial,
-                                   LayeredSceneWidget& coronal,
-                                   LayeredSceneWidget& sagittal)
-      {
-        ReferenceLineFactory::Configure(axial, coronal);
-        ReferenceLineFactory::Configure(axial, sagittal);
-        ReferenceLineFactory::Configure(coronal, sagittal);
-      }
-
-
-      void SynchronizeView(const WorldSceneWidget& source,
-                           const ViewportGeometry& view,
-                           LayeredSceneWidget& widget1,
-                           LayeredSceneWidget& widget2,
-                           LayeredSceneWidget& widget3)
-      {
-        if (&source == &widget1 ||
-            &source == &widget2 ||
-            &source == &widget3)
-        {
-          if (&source != &widget1)
-          {
-            widget1.SetView(view);
-          }
-
-          if (&source != &widget2)
-          {
-            widget2.SetView(view);
-          }
-
-          if (&source != &widget3)
-          {
-            widget3.SetView(view);
-          }
-        }
-      }
-
-
-      void SynchronizeSlice(const LayeredSceneWidget& source,
-                            const SliceGeometry& slice,
-                            LayeredSceneWidget& widget1,
-                            LayeredSceneWidget& widget2,
-                            LayeredSceneWidget& widget3)
-      {
-        if (&source == &widget1 ||
-            &source == &widget2 ||
-            &source == &widget3)
-        {
-          if (&source != &widget1)
-          {
-            widget1.SetSlice(slice);
-          }
-
-          if (&source != &widget2)
-          {
-            widget2.SetSlice(slice);
-          }
-
-          if (&source != &widget3)
-          {
-            widget3.SetSlice(slice);
-          }
-        }
-      }
-
-
-      LayeredSceneWidget* CreateWidget()
-      {
-        std::unique_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget);
-        widget->Register(dynamic_cast<WorldSceneWidget::IWorldObserver&>(*this));
-        widget->Register(dynamic_cast<LayeredSceneWidget::ISliceObserver&>(*this));
-        return widget.release();
-      }
-
-
-      void CreateLayout(BasicApplicationContext& context)
-      {
-        std::unique_ptr<OrthancStone::LayoutWidget> layout(new OrthancStone::LayoutWidget);
-        layout->SetBackgroundCleared(true);
-        //layout->SetBackgroundColor(255,0,0);
-        layout->SetPadding(5);
-
-        OrthancStone::LayoutWidget& layoutA = dynamic_cast<OrthancStone::LayoutWidget&>
-          (layout->AddWidget(new OrthancStone::LayoutWidget));
-        layoutA.SetPadding(0, 0, 0, 0, 5);
-        layoutA.SetVertical();
-        petAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutA.AddWidget(CreateWidget()));
-        OrthancStone::LayoutWidget& layoutA2 = dynamic_cast<OrthancStone::LayoutWidget&>
-          (layoutA.AddWidget(new OrthancStone::LayoutWidget));
-        layoutA2.SetPadding(0, 0, 0, 0, 5);
-        petSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutA2.AddWidget(CreateWidget()));
-        petCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutA2.AddWidget(CreateWidget()));
-
-        OrthancStone::LayoutWidget& layoutB = dynamic_cast<OrthancStone::LayoutWidget&>
-          (layout->AddWidget(new OrthancStone::LayoutWidget));
-        layoutB.SetPadding(0, 0, 0, 0, 5);
-        layoutB.SetVertical();
-        ctAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutB.AddWidget(CreateWidget()));
-        OrthancStone::LayoutWidget& layoutB2 = dynamic_cast<OrthancStone::LayoutWidget&>
-          (layoutB.AddWidget(new OrthancStone::LayoutWidget));
-        layoutB2.SetPadding(0, 0, 0, 0, 5);
-        ctSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutB2.AddWidget(CreateWidget()));
-        ctCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutB2.AddWidget(CreateWidget()));
-
-        OrthancStone::LayoutWidget& layoutC = dynamic_cast<OrthancStone::LayoutWidget&>
-          (layout->AddWidget(new OrthancStone::LayoutWidget));
-        layoutC.SetPadding(0, 0, 0, 0, 5);
-        layoutC.SetVertical();
-        fusionAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutC.AddWidget(CreateWidget()));
-        OrthancStone::LayoutWidget& layoutC2 = dynamic_cast<OrthancStone::LayoutWidget&>
-          (layoutC.AddWidget(new OrthancStone::LayoutWidget));
-        layoutC2.SetPadding(0, 0, 0, 0, 5);
-        fusionSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutC2.AddWidget(CreateWidget()));
-        fusionCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutC2.AddWidget(CreateWidget()));
-  
-        context.SetCentralWidget(layout.release());
-      }
-
-
-    public:
-      virtual void DeclareCommandLineOptions(boost::program_options::options_description& options)
-      {
-        boost::program_options::options_description generic("Sample options");
-        generic.add_options()
-          ("ct", boost::program_options::value<std::string>(), 
-           "Orthanc ID of the CT series")
-          ("pet", boost::program_options::value<std::string>(), 
-           "Orthanc ID of the PET series")
-          ("rt", boost::program_options::value<std::string>(), 
-           "Orthanc ID of the DICOM RT-STRUCT series (optional)")
-          ("threads", boost::program_options::value<unsigned int>()->default_value(3), 
-           "Number of download threads for the CT series")
-          ;
-
-        options.add(generic);    
-      }
-
-      virtual void Initialize(BasicApplicationContext& context,
-                              IStatusBar& statusBar,
-                              const boost::program_options::variables_map& parameters)
-      {
-        using namespace OrthancStone;
-
-        processingEvent_ = true;
-
-        if (parameters.count("ct") != 1 ||
-            parameters.count("pet") != 1)
-        {
-          LOG(ERROR) << "The series ID is missing";
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-        }
-
-        std::string ct = parameters["ct"].as<std::string>();
-        std::string pet = parameters["pet"].as<std::string>();
-        unsigned int threads = parameters["threads"].as<unsigned int>();
-
-        VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads);
-        VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1);
-
-        // Take the PET volume as the reference for the slices
-        interactorAxial_ = &dynamic_cast<Interactor&>
-          (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Axial, false)));
-        interactorCoronal_ = &dynamic_cast<Interactor&>
-          (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Coronal, false)));
-        interactorSagittal_ = &dynamic_cast<Interactor&>
-          (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Sagittal, true)));
-
-        CreateLayout(context);
-
-        AddLayer(*ctAxial_, ctVolume, true);
-        AddLayer(*ctCoronal_, ctVolume, true);
-        AddLayer(*ctSagittal_, ctVolume, true);
-
-        AddLayer(*petAxial_, petVolume, false);
-        AddLayer(*petCoronal_, petVolume, false);
-        AddLayer(*petSagittal_, petVolume, false);
-
-        AddLayer(*fusionAxial_, ctVolume, true);
-        AddLayer(*fusionAxial_, petVolume, false);
-        AddLayer(*fusionCoronal_, ctVolume, true);
-        AddLayer(*fusionCoronal_, petVolume, false);
-        AddLayer(*fusionSagittal_, ctVolume, true);
-        AddLayer(*fusionSagittal_, petVolume, false);
-
-        if (parameters.count("rt") == 1)
-        {
-          DicomStructureSet& rtStruct = context.AddStructureSet(parameters["rt"].as<std::string>());
-
-          Vector p = rtStruct.GetStructureCenter(0);
-          interactorAxial_->GetCursor().LookupSliceContainingPoint(p);
-
-          ctAxial_->AddLayer(new DicomStructureSetSlicer(rtStruct));
-          petAxial_->AddLayer(new DicomStructureSetSlicer(rtStruct));
-          fusionAxial_->AddLayer(new DicomStructureSetSlicer(rtStruct));
-        }        
-
-        ConnectSiblingLocations(*ctAxial_, *ctCoronal_, *ctSagittal_); 
-        ConnectSiblingLocations(*petAxial_, *petCoronal_, *petSagittal_); 
-        ConnectSiblingLocations(*fusionAxial_, *fusionCoronal_, *fusionSagittal_); 
-
-        interactorAxial_->AddWidget(*ctAxial_);
-        interactorAxial_->AddWidget(*petAxial_);
-        interactorAxial_->AddWidget(*fusionAxial_);
-        
-        interactorCoronal_->AddWidget(*ctCoronal_);
-        interactorCoronal_->AddWidget(*petCoronal_);
-        interactorCoronal_->AddWidget(*fusionCoronal_);
-        
-        interactorSagittal_->AddWidget(*ctSagittal_);
-        interactorSagittal_->AddWidget(*petSagittal_);
-        interactorSagittal_->AddWidget(*fusionSagittal_);
-
-        processingEvent_ = false;
-
-        statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode");
-        statusBar.SetMessage("Use the key \"s\" to reinitialize the layout");
-      }
-
-      virtual void NotifySizeChange(const WorldSceneWidget& source,
-                                    ViewportGeometry& view)
-      {
-        view.FitContent();
-      }
-
-      virtual void NotifyViewChange(const WorldSceneWidget& source,
-                                    const ViewportGeometry& view)
-      {
-        if (!processingEvent_)  // Avoid reentrant calls
-        {
-          processingEvent_ = true;
-
-          SynchronizeView(source, view, *ctAxial_, *petAxial_, *fusionAxial_);
-          SynchronizeView(source, view, *ctCoronal_, *petCoronal_, *fusionCoronal_);
-          SynchronizeView(source, view, *ctSagittal_, *petSagittal_, *fusionSagittal_);
-          
-          processingEvent_ = false;
-        }
-      }
-
-      virtual void NotifySliceContentChange(const LayeredSceneWidget& source,
-                                     const SliceGeometry& slice)
-      {
-        if (!processingEvent_)  // Avoid reentrant calls
-        {
-          processingEvent_ = true;
-
-          SynchronizeSlice(source, slice, *ctAxial_, *petAxial_, *fusionAxial_);
-          SynchronizeSlice(source, slice, *ctCoronal_, *petCoronal_, *fusionCoronal_);
-          SynchronizeSlice(source, slice, *ctSagittal_, *petSagittal_, *fusionSagittal_);
-          
-          processingEvent_ = false;
-        }
-      }
-    };
-  }
-}
--- a/Applications/Samples/Deprecated/Qt/SampleMainWindow.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "SampleMainWindow.h"
-
-/**
- * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as
- * this makes CMake unable to detect when the UI file changes.
- **/
-#include <ui_SampleMainWindow.h>
-#include "../../../Applications/Samples/SampleApplicationBase.h"
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-
-    SampleMainWindow::SampleMainWindow(
-      OrthancStone::NativeStoneApplicationContext& context,
-      OrthancStone::Samples::SampleSingleCanvasApplicationBase& stoneSampleApplication,
-      QWidget *parent) :
-      QStoneMainWindow(context, parent),
-      ui_(new Ui::SampleMainWindow),
-      stoneSampleApplication_(stoneSampleApplication)
-    {
-      ui_->setupUi(this);
-      SetCentralStoneWidget(*ui_->cairoCentralWidget);
-    }
-
-    SampleMainWindow::~SampleMainWindow()
-    {
-      delete ui_;
-    }
-
-  }
-}
--- a/Applications/Samples/Deprecated/Qt/SampleMainWindow.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/**
- * 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 "../../../Qt/QCairoWidget.h"
-#include "../../../Qt/QStoneMainWindow.h"
-
-namespace Ui 
-{
-  class SampleMainWindow;
-}
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-
-    class SampleSingleCanvasApplicationBase;
-
-    class SampleMainWindow : public QStoneMainWindow
-    {
-      Q_OBJECT
-
-    private:
-      Ui::SampleMainWindow*   ui_;
-      SampleSingleCanvasApplicationBase&  stoneSampleApplication_;
-
-    public:
-      explicit SampleMainWindow(OrthancStone::NativeStoneApplicationContext& context, SampleSingleCanvasApplicationBase& stoneSampleApplication, QWidget *parent = 0);
-      ~SampleMainWindow();
-    };
-  }
-}
--- a/Applications/Samples/Deprecated/Qt/SampleMainWindow.ui	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>SampleMainWindow</class>
- <widget class="QMainWindow" name="SampleMainWindow">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>903</width>
-    <height>634</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>500</width>
-    <height>300</height>
-   </size>
-  </property>
-  <property name="baseSize">
-   <size>
-    <width>500</width>
-    <height>300</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Stone of Orthanc</string>
-  </property>
-  <property name="layoutDirection">
-   <enum>Qt::LeftToRight</enum>
-  </property>
-  <widget class="QWidget" name="centralwidget">
-   <property name="sizePolicy">
-    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-     <horstretch>0</horstretch>
-     <verstretch>0</verstretch>
-    </sizepolicy>
-   </property>
-   <property name="layoutDirection">
-    <enum>Qt::LeftToRight</enum>
-   </property>
-   <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0">
-    <property name="sizeConstraint">
-     <enum>QLayout::SetDefaultConstraint</enum>
-    </property>
-    <item>
-     <widget class="QCairoWidget" name="cairoCentralWidget">
-      <property name="minimumSize">
-       <size>
-        <width>0</width>
-        <height>500</height>
-       </size>
-      </property>
-     </widget>
-    </item>
-   </layout>
-  </widget>
-  <widget class="QMenuBar" name="menubar">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>0</y>
-     <width>903</width>
-     <height>22</height>
-    </rect>
-   </property>
-   <widget class="QMenu" name="menuTest">
-    <property name="title">
-     <string>Test</string>
-    </property>
-   </widget>
-   <addaction name="menuTest"/>
-  </widget>
-  <widget class="QStatusBar" name="statusbar"/>
- </widget>
- <customwidgets>
-  <customwidget>
-   <class>QCairoWidget</class>
-   <extends>QGraphicsView</extends>
-   <header location="global">QCairoWidget.h</header>
-  </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
--- a/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "SampleMainWindow.h"
-
-/**
- * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as
- * this makes CMake unable to detect when the UI file changes.
- **/
-#include <ui_SampleMainWindowWithButtons.h>
-#include "../../../Applications/Samples/SampleApplicationBase.h"
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-
-    SampleMainWindowWithButtons::SampleMainWindowWithButtons(
-      OrthancStone::NativeStoneApplicationContext& context,
-      OrthancStone::Samples::SampleSingleCanvasWithButtonsApplicationBase& stoneSampleApplication,
-      QWidget *parent) :
-      QStoneMainWindow(context, parent),
-      ui_(new Ui::SampleMainWindowWithButtons),
-      stoneSampleApplication_(stoneSampleApplication)
-    {
-      ui_->setupUi(this);
-      SetCentralStoneWidget(*ui_->cairoCentralWidget);
-
-#if QT_VERSION >= 0x050000
-      connect(ui_->toolButton1, &QToolButton::clicked, this, &SampleMainWindowWithButtons::tool1Clicked);
-      connect(ui_->toolButton2, &QToolButton::clicked, this, &SampleMainWindowWithButtons::tool2Clicked);
-      connect(ui_->pushButton1, &QPushButton::clicked, this, &SampleMainWindowWithButtons::pushButton1Clicked);
-      connect(ui_->pushButton1, &QPushButton::clicked, this, &SampleMainWindowWithButtons::pushButton2Clicked);
-#else
-      connect(ui_->toolButton1, SIGNAL(clicked()), this, SLOT(tool1Clicked()));
-      connect(ui_->toolButton2, SIGNAL(clicked()), this, SLOT(tool2Clicked()));
-      connect(ui_->pushButton1, SIGNAL(clicked()), this, SLOT(pushButton1Clicked()));
-      connect(ui_->pushButton1, SIGNAL(clicked()), this, SLOT(pushButton2Clicked()));
-#endif
-
-      std::string pushButton1Name;
-      std::string pushButton2Name;
-      std::string tool1Name;
-      std::string tool2Name;
-      stoneSampleApplication_.GetButtonNames(pushButton1Name, pushButton2Name, tool1Name, tool2Name);
-
-      ui_->toolButton1->setText(QString::fromStdString(tool1Name));
-      ui_->toolButton2->setText(QString::fromStdString(tool2Name));
-      ui_->pushButton1->setText(QString::fromStdString(pushButton1Name));
-      ui_->pushButton2->setText(QString::fromStdString(pushButton2Name));
-    }
-
-    SampleMainWindowWithButtons::~SampleMainWindowWithButtons()
-    {
-      delete ui_;
-    }
-
-    void SampleMainWindowWithButtons::tool1Clicked()
-    {
-      stoneSampleApplication_.OnTool1Clicked();
-    }
-
-    void SampleMainWindowWithButtons::tool2Clicked()
-    {
-      stoneSampleApplication_.OnTool2Clicked();
-    }
-
-    void SampleMainWindowWithButtons::pushButton1Clicked()
-    {
-      stoneSampleApplication_.OnPushButton1Clicked();
-    }
-
-    void SampleMainWindowWithButtons::pushButton2Clicked()
-    {
-      stoneSampleApplication_.OnPushButton2Clicked();
-    }
-
-  }
-}
--- a/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/**
- * 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 "../../../Qt/QCairoWidget.h"
-#include "../../../Qt/QStoneMainWindow.h"
-
-namespace Ui 
-{
-  class SampleMainWindowWithButtons;
-}
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-
-    class SampleSingleCanvasWithButtonsApplicationBase;
-
-    class SampleMainWindowWithButtons : public QStoneMainWindow
-    {
-      Q_OBJECT
-
-    private:
-      Ui::SampleMainWindowWithButtons*   ui_;
-      SampleSingleCanvasWithButtonsApplicationBase&  stoneSampleApplication_;
-
-    public:
-      explicit SampleMainWindowWithButtons(OrthancStone::NativeStoneApplicationContext& context, SampleSingleCanvasWithButtonsApplicationBase& stoneSampleApplication, QWidget *parent = 0);
-      ~SampleMainWindowWithButtons();
-
-    private slots:
-      void tool1Clicked();
-      void tool2Clicked();
-      void pushButton1Clicked();
-      void pushButton2Clicked();
-    };
-  }
-}
--- a/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.ui	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,130 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>SampleMainWindowWithButtons</class>
- <widget class="QMainWindow" name="SampleMainWindowWithButtons">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>903</width>
-    <height>634</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>500</width>
-    <height>300</height>
-   </size>
-  </property>
-  <property name="baseSize">
-   <size>
-    <width>500</width>
-    <height>300</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Stone of Orthanc</string>
-  </property>
-  <property name="layoutDirection">
-   <enum>Qt::LeftToRight</enum>
-  </property>
-  <widget class="QWidget" name="centralwidget">
-   <property name="sizePolicy">
-    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-     <horstretch>0</horstretch>
-     <verstretch>0</verstretch>
-    </sizepolicy>
-   </property>
-   <property name="layoutDirection">
-    <enum>Qt::LeftToRight</enum>
-   </property>
-   <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0">
-    <property name="sizeConstraint">
-     <enum>QLayout::SetDefaultConstraint</enum>
-    </property>
-    <item>
-     <widget class="QCairoWidget" name="cairoCentralWidget">
-      <property name="minimumSize">
-       <size>
-        <width>0</width>
-        <height>500</height>
-       </size>
-      </property>
-     </widget>
-    </item>
-    <item>
-     <widget class="QGroupBox" name="horizontalGroupBox">
-      <property name="minimumSize">
-       <size>
-        <width>0</width>
-        <height>100</height>
-       </size>
-      </property>
-      <property name="maximumSize">
-       <size>
-        <width>16777215</width>
-        <height>100</height>
-       </size>
-      </property>
-      <layout class="QHBoxLayout" name="horizontalLayout">
-       <item>
-        <widget class="QToolButton" name="toolButton1">
-         <property name="text">
-          <string>tool1</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QToolButton" name="toolButton2">
-         <property name="text">
-          <string>tool2</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QPushButton" name="pushButton1">
-         <property name="text">
-          <string>action1</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QPushButton" name="pushButton2">
-         <property name="text">
-          <string>action2</string>
-         </property>
-        </widget>
-       </item>
-      </layout>
-     </widget>
-    </item>
-   </layout>
-  </widget>
-  <widget class="QMenuBar" name="menubar">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>0</y>
-     <width>903</width>
-     <height>22</height>
-    </rect>
-   </property>
-   <widget class="QMenu" name="menuTest">
-    <property name="title">
-     <string>Test</string>
-    </property>
-   </widget>
-   <addaction name="menuTest"/>
-  </widget>
-  <widget class="QStatusBar" name="statusbar"/>
- </widget>
- <customwidgets>
-  <customwidget>
-   <class>QCairoWidget</class>
-   <extends>QGraphicsView</extends>
-   <header location="global">QCairoWidget.h</header>
-  </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
--- a/Applications/Samples/Deprecated/Qt/SampleQtApplicationRunner.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/**
- * 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 "../../../Qt/QtStoneApplicationRunner.h"
-
-#if ORTHANC_ENABLE_QT != 1
-#error this file shall be included only with the ORTHANC_ENABLE_QT set to 1
-#endif
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class SampleQtApplicationRunner : public OrthancStone::QtStoneApplicationRunner
-    {
-    protected:
-      virtual void InitializeMainWindow(OrthancStone::NativeStoneApplicationContext& context)
-      {
-        window_.reset(application_.CreateQtMainWindow());
-      }
-    public:
-      SampleQtApplicationRunner(MessageBroker& broker,
-                                SampleApplicationBase& application)
-        : OrthancStone::QtStoneApplicationRunner(broker, application)
-      {
-      }
-
-    };
-  }
-}
--- a/Applications/Samples/Deprecated/SampleApplicationBase.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-/**
- * 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 "../../../Applications/IStoneApplication.h"
-#include "../../../Framework/Deprecated/Widgets/WorldSceneWidget.h"
-
-#if ORTHANC_ENABLE_WASM==1
-#include "../../../Platforms/Wasm/WasmPlatformApplicationAdapter.h"
-#include "../../../Platforms/Wasm/Defaults.h"
-#endif
-
-#if ORTHANC_ENABLE_QT==1
-#include "Qt/SampleMainWindow.h"
-#include "Qt/SampleMainWindowWithButtons.h"
-#endif
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class SampleApplicationBase : public IStoneApplication
-    {
-    private:
-      boost::shared_ptr<Deprecated::IWidget>  mainWidget_;
-
-    public:
-      virtual void Initialize(StoneApplicationContext* context,
-                              Deprecated::IStatusBar& statusBar,
-                              const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE
-      {
-      }
-
-      virtual std::string GetTitle() const ORTHANC_OVERRIDE
-      {
-        return "Stone of Orthanc - Sample";
-      }
-
-      /**
-       * In the basic samples, the commands are handled by the platform adapter and NOT
-       * by the application handler
-      */
-      virtual void HandleSerializedMessage(const char* data) ORTHANC_OVERRIDE {};
-
-
-      virtual void Finalize() ORTHANC_OVERRIDE {}
-
-      virtual void SetCentralWidget(boost::shared_ptr<Deprecated::IWidget> widget) ORTHANC_OVERRIDE
-      {
-        mainWidget_ = widget;
-      }
-
-      virtual boost::shared_ptr<Deprecated::IWidget> GetCentralWidget() ORTHANC_OVERRIDE
-      {
-        return mainWidget_;
-      }
-
-#if ORTHANC_ENABLE_WASM==1
-      // default implementations for a single canvas named "canvas" in the HTML and an emtpy WasmApplicationAdapter
-
-      virtual void InitializeWasm() ORTHANC_OVERRIDE
-      {
-        AttachWidgetToWasmViewport("canvas", mainWidget_);
-      }
-
-      virtual WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(MessageBroker& broker)
-      {
-        return new WasmPlatformApplicationAdapter(broker, *this);
-      }
-#endif
-
-    };
-
-    // this application actually works in Qt and WASM
-    class SampleSingleCanvasWithButtonsApplicationBase : public SampleApplicationBase
-    {
-public:
-      virtual void OnPushButton1Clicked() {}
-      virtual void OnPushButton2Clicked() {}
-      virtual void OnTool1Clicked() {}
-      virtual void OnTool2Clicked() {}
-
-      virtual void GetButtonNames(std::string& pushButton1,
-                                  std::string& pushButton2,
-                                  std::string& tool1,
-                                  std::string& tool2
-                                  ) {
-        pushButton1 = "action1";
-        pushButton2 = "action2";
-        tool1 = "tool1";
-        tool2 = "tool2";
-      }
-
-#if ORTHANC_ENABLE_QT==1
-      virtual QStoneMainWindow* CreateQtMainWindow() {
-        return new SampleMainWindowWithButtons(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this);
-      }
-#endif
-
-    };
-
-    // this application actually works in SDL and WASM
-    class SampleSingleCanvasApplicationBase : public SampleApplicationBase
-    {
-public:
-
-#if ORTHANC_ENABLE_QT==1
-      virtual QStoneMainWindow* CreateQtMainWindow() {
-        return new SampleMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this);
-      }
-#endif
-    };
-  }
-}
--- a/Applications/Samples/Deprecated/SampleInteractor.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-/**
- * 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 "SampleApplicationBase.h"
-
-#include "../../../Framework/Widgets/LayeredSceneWidget.h"
-#include "../../../Framework/Widgets/IWorldSceneInteractor.h"
-#include "../../../Framework/Toolbox/ParallelSlicesCursor.h"
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    /**
-     * This is a basic mouse interactor for sample applications. It
-     * contains a set of parallel slices in the 3D space. The mouse
-     * wheel events make the widget change the slice that is
-     * displayed.
-     **/
-    class SampleInteractor : public IWorldSceneInteractor
-    {
-    private:
-      ParallelSlicesCursor   cursor_;
-
-    public:
-      SampleInteractor(VolumeImage& volume,
-                       VolumeProjection projection, 
-                       bool reverse)
-      {
-        std::unique_ptr<ParallelSlices> slices(volume.GetGeometry(projection, reverse));
-        cursor_.SetGeometry(*slices);
-      }
-
-      SampleInteractor(ISeriesLoader& series, 
-                       bool reverse)
-      {
-        if (reverse)
-        {
-          std::unique_ptr<ParallelSlices> slices(series.GetGeometry().Reverse());
-          cursor_.SetGeometry(*slices);
-        }
-        else
-        {
-          cursor_.SetGeometry(series.GetGeometry());
-        }
-      }
-
-      SampleInteractor(const ParallelSlices& slices)
-      {
-        cursor_.SetGeometry(slices);
-      }
-
-      ParallelSlicesCursor& GetCursor()
-      {
-        return cursor_;
-      }
-
-      void AddWidget(LayeredSceneWidget& widget)
-      {
-        widget.SetInteractor(*this);
-        widget.SetSlice(cursor_.GetCurrentSlice());
-      }
-
-      virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
-                                                          const ViewportGeometry& view,
-                                                          MouseButton button,
-                                                          double x,
-                                                          double y,
-                                                          IStatusBar* statusBar)
-      {
-        return NULL;
-      }
-
-      virtual void MouseOver(CairoContext& context,
-                             WorldSceneWidget& widget,
-                             const ViewportGeometry& view,
-                             double x,
-                             double y,
-                             IStatusBar* statusBar)
-      {
-      }
-
-      virtual void MouseWheel(WorldSceneWidget& widget,
-                              MouseWheelDirection direction,
-                              KeyboardModifiers modifiers,
-                              IStatusBar* statusBar)
-      {
-        if (cursor_.ApplyWheelEvent(direction, modifiers))
-        {
-          dynamic_cast<LayeredSceneWidget&>(widget).SetSlice(cursor_.GetCurrentSlice());
-        }
-      }
-
-      virtual void KeyPressed(WorldSceneWidget& widget,
-                              char key,
-                              KeyboardModifiers modifiers,
-                              IStatusBar* statusBar)
-      {
-      }
-
-      void LookupSliceContainingPoint(LayeredSceneWidget& widget,
-                                      const Vector& p)
-      {
-        if (cursor_.LookupSliceContainingPoint(p))
-        {
-          widget.SetSlice(cursor_.GetCurrentSlice());
-        }
-      }
-    };
-  }
-}
--- a/Applications/Samples/Deprecated/SampleList.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-// The macro "ORTHANC_STONE_SAMPLE" must be set by the CMake script
-
-#if ORTHANC_STONE_SAMPLE == 1
-#include "EmptyApplication.h"
-typedef OrthancStone::Samples::EmptyApplication SampleApplication;
-
-#elif ORTHANC_STONE_SAMPLE == 2
-#include "TestPatternApplication.h"
-typedef OrthancStone::Samples::TestPatternApplication SampleApplication;
-
-#elif ORTHANC_STONE_SAMPLE == 3
-#include "SingleFrameApplication.h"
-typedef OrthancStone::Samples::SingleFrameApplication SampleApplication;
-
-#elif ORTHANC_STONE_SAMPLE == 4
-#include "SingleVolumeApplication.h"
-typedef OrthancStone::Samples::SingleVolumeApplication SampleApplication;
-
-#elif ORTHANC_STONE_SAMPLE == 5
-#include "BasicPetCtFusionApplication.h"
-typedef OrthancStone::Samples::BasicPetCtFusionApplication SampleApplication;
-
-#elif ORTHANC_STONE_SAMPLE == 6
-#include "SynchronizedSeriesApplication.h"
-typedef OrthancStone::Samples::SynchronizedSeriesApplication SampleApplication;
-
-#elif ORTHANC_STONE_SAMPLE == 7
-#include "LayoutPetCtFusionApplication.h"
-typedef OrthancStone::Samples::LayoutPetCtFusionApplication SampleApplication;
-
-#elif ORTHANC_STONE_SAMPLE == 8
-#include "SimpleViewerApplicationSingleFile.h"
-typedef OrthancStone::Samples::SimpleViewerApplication SampleApplication;
-
-#elif ORTHANC_STONE_SAMPLE == 9
-#include "SingleFrameEditorApplication.h"
-typedef OrthancStone::Samples::SingleFrameEditorApplication SampleApplication;
-
-#else
-#error Please set the ORTHANC_STONE_SAMPLE macro
-#endif
--- a/Applications/Samples/Deprecated/SampleMainNative.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "SampleList.h"
-#if ORTHANC_ENABLE_SDL==1
-#include "../../Sdl/SdlStoneApplicationRunner.h"
-#endif
-#if ORTHANC_ENABLE_QT==1
-#include "Qt/SampleQtApplicationRunner.h"
-#endif
-
-int main(int argc, char* argv[]) 
-{
-  boost::shared_ptr<SampleApplication> sampleStoneApplication(new SampleApplication);
-
-#if ORTHANC_ENABLE_SDL==1
-  OrthancStone::SdlStoneApplicationRunner sdlApplicationRunner(sampleStoneApplication);
-  return sdlApplicationRunner.Execute(argc, argv);
-#endif
-  
-#if ORTHANC_ENABLE_QT==1
-  OrthancStone::Samples::SampleQtApplicationRunner qtAppRunner(sampleStoneApplication);
-  return qtAppRunner.Execute(argc, argv);
-#endif
-}
-
--- a/Applications/Samples/Deprecated/SampleMainWasm.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "Platforms/Wasm/WasmWebService.h"
-#include "Platforms/Wasm/WasmViewport.h"
-
-#include <emscripten/emscripten.h>
-
-#include "SampleList.h"
-
-
-OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker) 
-{
-  return new SampleApplication(broker);
-}
-
-OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application)
-{
-  return dynamic_cast<SampleApplication*>(application)->CreateWasmApplicationAdapter(broker);
-}
\ No newline at end of file
--- a/Applications/Samples/Deprecated/Samples-status.md	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-Executable versions
-================
-Generic options
-----------------------
-```
-("help", "Display this help and exit")
-("verbose", "Be verbose in logs")
-("orthanc", boost::program_options::value<std::string>()
-  ->default_value("http://localhost:8042/"),
-  "URL to the Orthanc server")
-("username", "Username for the Orthanc server")
-("password", "Password for the Orthanc server")
-("https-verify", boost::program_options::value<bool>()
-  ->default_value(true), "Check HTTPS certificates")
-```
-OrthancStoneSimpleViewer
--------------------------------------
-- Options:
-    ```
-    - "studyId", std::string, "Orthanc ID of the study"
-    ```
-- study loading works OK
-- Invert does not work:
-```
-void SimpleViewerApplication::ExecuteAction(SimpleViewerApplication::Actions action)
-  {
-    // TODO
-  }
-```
-
-OrthancStoneSimpleViewerSingleFile
--------------------------------------
-- Options:
-    ```
-    - "studyId", std::string, "Orthanc ID of the study"
-    ```
-
-Study loading works.
-
-The `line` and `circle` buttons work and call this:
-```
-virtual void OnTool1Clicked()
-{
-  currentTool_ = Tools_LineMeasure;
-}
-
-virtual void OnTool2Clicked()
-{
-  currentTool_ = Tools_CircleMeasure;
-}
-```
-The `action1` and `action2` buttons are not connected
-
-The following is displayed in the console at launch time:
-```
-W0313 12:20:12.790449 NativeStoneApplicationRunner.cpp:55] Use the key "s" to reinitialize the layout
-W0313 12:20:12.790449 NativeStoneApplicationRunner.cpp:55] Use the key "n" to go to next image in the main viewport
-```
-However, when looking at `MainWidgetInteractor::KeyPressed` (`SimpleViewerApplicationSingleFile.h:169`), only the following is processed:
-- 's': reset layout
-- 'l': select line tool
-- 'c': select circle tool
-
-OrthancStoneSingleFrame
--------------------------------------
-```
-generic.add_options()
-("instance", boost::program_options::value<std::string>(), 
-"Orthanc ID of the instance")
-("frame", boost::program_options::value<unsigned int>()
-  ->default_value(0),
-"Number of the frame, for multi-frame DICOM instances")
-("smooth", boost::program_options::value<bool>()
-  ->default_value(true), 
-"Enable bilinear interpolation to smooth the image");
-```
-only key handled in `KeyPressed` is `s` to call `widget.FitContent()`
-
-
-OrthancStoneSingleFrameEditor
--------------------------------------
-```
-generic.add_options()
-("instance", boost::program_options::value<std::string>(),
-"Orthanc ID of the instance")
-("frame", boost::program_options::value<unsigned int>()
-  ->default_value(0),
-"Number of the frame, for multi-frame DICOM instances");
-```
-Available commands in `KeyPressed` (`SingleFrameEditorApplication.h:280`): 
-- 'a' widget.FitContent()
-- 'c' Crop tool
-- 'm' Mask tool
-- 'd' dump to json and diplay result (?)
-- 'e' export current view to Dicom with dummy tags (?)
-- 'i' wdiget.SwitchInvert
-- 't' Move tool
-- 'n' switch between nearest and bilinear interpolation
-- 'r' Rotate tool
-- 's' Resize tool
-- 'w' Windowing tool
-- 'ctrl+y' redo
-- 'ctrl+z' undo
--- a/Applications/Samples/Deprecated/SimpleViewer/AppStatus.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-#pragma once
-
-#include <string>
-
-
-namespace SimpleViewer
-{
-  struct AppStatus
-  {
-    std::string patientId;
-    std::string studyDescription;
-    std::string currentInstanceIdInMainViewport;
-    // note: if you add members here, update the serialization code below and deserialization in simple-viewer.ts -> onAppStatusUpdated()
-
-
-    AppStatus()
-    {
-    }
-
-    void ToJson(Json::Value &output) const
-    {
-      output["patientId"] = patientId;
-      output["studyDescription"] = studyDescription;
-      output["currentInstanceIdInMainViewport"] = currentInstanceIdInMainViewport;
-    }
-  };
-}
--- a/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "MainWidgetInteractor.h"
-
-#include "SimpleViewerApplication.h"
-
-namespace SimpleViewer {
-
-  Deprecated::IWorldSceneMouseTracker* MainWidgetInteractor::CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
-                                                                    const Deprecated::ViewportGeometry& view,
-                                                                    MouseButton button,
-                                                                    KeyboardModifiers modifiers,
-                                                                    int viewportX,
-                                                                    int viewportY,
-                                                                    double x,
-                                                                    double y,
-                                                                    Deprecated::IStatusBar* statusBar,
-                                                                    const std::vector<Deprecated::Touch>& displayTouches)
-  {
-    if (button == MouseButton_Left)
-    {
-      if (application_.GetCurrentTool() == Tool_LineMeasure)
-      {
-        return new Deprecated::LineMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(),
-                                      x, y, 255, 0, 0, application_.GetFont());
-      }
-      else if (application_.GetCurrentTool() == Tool_CircleMeasure)
-      {
-        return new Deprecated::CircleMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(),
-                                        x, y, 255, 0, 0, application_.GetFont());
-      }
-      else if (application_.GetCurrentTool() == Tool_Crop)
-      {
-        // TODO
-      }
-      else if (application_.GetCurrentTool() == Tool_Windowing)
-      {
-        // TODO
-      }
-      else if (application_.GetCurrentTool() == Tool_Zoom)
-      {
-        // TODO
-      }
-      else if (application_.GetCurrentTool() == Tool_Pan)
-      {
-        // TODO
-      }
-    }
-    return NULL;
-  }
-
-  void MainWidgetInteractor::MouseOver(CairoContext& context,
-                                       Deprecated::WorldSceneWidget& widget,
-                                       const Deprecated::ViewportGeometry& view,
-                                       double x,
-                                       double y,
-                                       Deprecated::IStatusBar* statusBar)
-  {
-    if (statusBar != NULL)
-    {
-      Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
-
-      char buf[64];
-      sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)",
-              p[0] / 10.0, p[1] / 10.0, p[2] / 10.0);
-      statusBar->SetMessage(buf);
-    }
-  }
-
-  void MainWidgetInteractor::MouseWheel(Deprecated::WorldSceneWidget& widget,
-                                        MouseWheelDirection direction,
-                                        KeyboardModifiers modifiers,
-                                        Deprecated::IStatusBar* statusBar)
-  {
-  }
-
-  void MainWidgetInteractor::KeyPressed(Deprecated::WorldSceneWidget& widget,
-                                        KeyboardKeys key,
-                                        char keyChar,
-                                        KeyboardModifiers modifiers,
-                                        Deprecated::IStatusBar* statusBar)
-  {
-    switch (keyChar)
-    {
-    case 's':
-      widget.FitContent();
-      break;
-
-    default:
-      break;
-    }
-  }
-}
--- a/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-/**
- * 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 "../../../../Framework/Deprecated/Widgets/IWorldSceneInteractor.h"
-
-using namespace OrthancStone;
-
-namespace SimpleViewer {
-
-  class SimpleViewerApplication;
-
-  class MainWidgetInteractor : public Deprecated::IWorldSceneInteractor
-  {
-  private:
-    SimpleViewerApplication&  application_;
-
-  public:
-    MainWidgetInteractor(SimpleViewerApplication&  application) :
-      application_(application)
-    {
-    }
-
-    /**
-        WorldSceneWidget: 
-    */
-    virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
-                                                                    const Deprecated::ViewportGeometry& view,
-                                                                    MouseButton button,
-                                                                    KeyboardModifiers modifiers,
-                                                                    int viewportX,
-                                                                    int viewportY,
-                                                                    double x,
-                                                                    double y,
-                                                                    Deprecated::IStatusBar* statusBar,
-                                                                    const std::vector<Deprecated::Touch>& displayTouches);
-
-    virtual void MouseOver(CairoContext& context,
-                           Deprecated::WorldSceneWidget& widget,
-                           const Deprecated::ViewportGeometry& view,
-                           double x,
-                           double y,
-                           Deprecated::IStatusBar* statusBar);
-
-    virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
-                            MouseWheelDirection direction,
-                            KeyboardModifiers modifiers,
-                            Deprecated::IStatusBar* statusBar);
-
-    virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
-                            KeyboardKeys key,
-                            char keyChar,
-                            KeyboardModifiers modifiers,
-                            Deprecated::IStatusBar* statusBar);
-  };
-
-
-}
--- a/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "SimpleViewerMainWindow.h"
-
-/**
- * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as
- * this makes CMake unable to detect when the UI file changes.
- **/
-#include <ui_SimpleViewerMainWindow.h>
-#include "../../SimpleViewerApplication.h"
-
-
-namespace SimpleViewer
-{
-  template<typename T, typename U>
-  bool ExecuteCommand(U* handler, const T& command)
-  {
-    std::string serializedCommand = StoneSerialize(command);
-    StoneDispatchToHandler(serializedCommand, handler);
-  }
-
-  SimpleViewerMainWindow::SimpleViewerMainWindow(
-    OrthancStone::NativeStoneApplicationContext& context,
-    SimpleViewerApplication& stoneApplication,
-    QWidget *parent) :
-    QStoneMainWindow(context, parent),
-    ui_(new Ui::SimpleViewerMainWindow),
-    stoneApplication_(stoneApplication)
-  {
-    ui_->setupUi(this);
-    SetCentralStoneWidget(*ui_->cairoCentralWidget);
-
-#if QT_VERSION >= 0x050000
-    connect(ui_->toolButtonCrop, &QToolButton::clicked, this, &SimpleViewerMainWindow::cropClicked);
-    connect(ui_->pushButtonUndoCrop, &QToolButton::clicked, this, &SimpleViewerMainWindow::undoCropClicked);
-    connect(ui_->toolButtonLine, &QToolButton::clicked, this, &SimpleViewerMainWindow::lineClicked);
-    connect(ui_->toolButtonCircle, &QToolButton::clicked, this, &SimpleViewerMainWindow::circleClicked);
-    connect(ui_->toolButtonWindowing, &QToolButton::clicked, this, &SimpleViewerMainWindow::windowingClicked);
-    connect(ui_->pushButtonRotate, &QPushButton::clicked, this, &SimpleViewerMainWindow::rotateClicked);
-    connect(ui_->pushButtonInvert, &QPushButton::clicked, this, &SimpleViewerMainWindow::invertClicked);
-#else
-    connect(ui_->toolButtonCrop, SIGNAL(clicked()), this, SLOT(cropClicked()));
-    connect(ui_->toolButtonLine, SIGNAL(clicked()), this, SLOT(lineClicked()));
-    connect(ui_->toolButtonCircle, SIGNAL(clicked()), this, SLOT(circleClicked()));
-    connect(ui_->toolButtonWindowing, SIGNAL(clicked()), this, SLOT(windowingClicked()));
-    connect(ui_->pushButtonUndoCrop, SIGNAL(clicked()), this, SLOT(undoCropClicked()));
-    connect(ui_->pushButtonRotate, SIGNAL(clicked()), this, SLOT(rotateClicked()));
-    connect(ui_->pushButtonInvert, SIGNAL(clicked()), this, SLOT(invertClicked()));
-#endif
-  }
-
-  SimpleViewerMainWindow::~SimpleViewerMainWindow()
-  {
-    delete ui_;
-  }
-
-  void SimpleViewerMainWindow::cropClicked()
-  {
-    stoneApplication_.ExecuteCommand(SelectTool(Tool_Crop));
-  }
-
-  void SimpleViewerMainWindow::undoCropClicked()
-  {
-    stoneApplication_.ExecuteCommand(Action(ActionType_UndoCrop));
-  }
-
-  void SimpleViewerMainWindow::lineClicked()
-  {
-    stoneApplication_.ExecuteCommand(SelectTool(Tool_LineMeasure));
-  }
-
-  void SimpleViewerMainWindow::circleClicked()
-  {
-    stoneApplication_.ExecuteCommand(SelectTool(Tool_CircleMeasure));
-  }
-
-  void SimpleViewerMainWindow::windowingClicked()
-  {
-    stoneApplication_.ExecuteCommand(SelectTool(Tool_Windowing));
-  }
-
-  void SimpleViewerMainWindow::rotateClicked()
-  {
-    stoneApplication_.ExecuteCommand(Action(ActionType_Rotate));
-  }
-
-  void SimpleViewerMainWindow::invertClicked()
-  {
-    stoneApplication_.ExecuteCommand(Action(ActionType_Invert));
-  }
-}
--- a/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/**
- * 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 <Applications/Qt/QCairoWidget.h>
-#include <Applications/Qt/QStoneMainWindow.h>
-
-namespace Ui 
-{
-  class SimpleViewerMainWindow;
-}
-
-using namespace OrthancStone;
-
-namespace SimpleViewer
-{
-  class SimpleViewerApplication;
-
-  class SimpleViewerMainWindow : public QStoneMainWindow
-  {
-    Q_OBJECT
-
-  private:
-    Ui::SimpleViewerMainWindow*   ui_;
-    SimpleViewerApplication&      stoneApplication_;
-
-  public:
-    explicit SimpleViewerMainWindow(OrthancStone::NativeStoneApplicationContext& context, SimpleViewerApplication& stoneApplication, QWidget *parent = 0);
-    ~SimpleViewerMainWindow();
-
-  private slots:
-    void cropClicked();
-    void undoCropClicked();
-    void rotateClicked();
-    void windowingClicked();
-    void lineClicked();
-    void circleClicked();
-    void invertClicked();
-  };
-}
--- a/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,151 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>SimpleViewerMainWindow</class>
- <widget class="QMainWindow" name="SimpleViewerMainWindow">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>903</width>
-    <height>634</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>500</width>
-    <height>300</height>
-   </size>
-  </property>
-  <property name="baseSize">
-   <size>
-    <width>500</width>
-    <height>300</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Stone of Orthanc</string>
-  </property>
-  <property name="layoutDirection">
-   <enum>Qt::LeftToRight</enum>
-  </property>
-  <widget class="QWidget" name="centralwidget">
-   <property name="sizePolicy">
-    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-     <horstretch>0</horstretch>
-     <verstretch>0</verstretch>
-    </sizepolicy>
-   </property>
-   <property name="layoutDirection">
-    <enum>Qt::LeftToRight</enum>
-   </property>
-   <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0">
-    <property name="sizeConstraint">
-     <enum>QLayout::SetDefaultConstraint</enum>
-    </property>
-    <item>
-     <widget class="QCairoWidget" name="cairoCentralWidget">
-      <property name="minimumSize">
-       <size>
-        <width>0</width>
-        <height>500</height>
-       </size>
-      </property>
-     </widget>
-    </item>
-    <item>
-     <widget class="QGroupBox" name="horizontalGroupBox">
-      <property name="minimumSize">
-       <size>
-        <width>0</width>
-        <height>100</height>
-       </size>
-      </property>
-      <property name="maximumSize">
-       <size>
-        <width>16777215</width>
-        <height>100</height>
-       </size>
-      </property>
-      <layout class="QHBoxLayout" name="horizontalLayout">
-       <item>
-        <widget class="QToolButton" name="toolButtonWindowing">
-         <property name="text">
-          <string>windowing</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QToolButton" name="toolButtonCrop">
-         <property name="text">
-          <string>crop</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QPushButton" name="pushButtonUndoCrop">
-         <property name="text">
-          <string>undo crop</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QToolButton" name="toolButtonLine">
-         <property name="text">
-          <string>line</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QToolButton" name="toolButtonCircle">
-         <property name="text">
-          <string>circle</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QPushButton" name="pushButtonRotate">
-         <property name="text">
-          <string>rotate</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QPushButton" name="pushButtonInvert">
-         <property name="text">
-          <string>invert</string>
-         </property>
-        </widget>
-       </item>
-      </layout>
-     </widget>
-    </item>
-   </layout>
-  </widget>
-  <widget class="QMenuBar" name="menubar">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>0</y>
-     <width>903</width>
-     <height>22</height>
-    </rect>
-   </property>
-   <widget class="QMenu" name="menuTest">
-    <property name="title">
-     <string>Test</string>
-    </property>
-   </widget>
-   <addaction name="menuTest"/>
-  </widget>
-  <widget class="QStatusBar" name="statusbar"/>
- </widget>
- <customwidgets>
-  <customwidget>
-   <class>QCairoWidget</class>
-   <extends>QGraphicsView</extends>
-   <header location="global">QCairoWidget.h</header>
-  </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
--- a/Applications/Samples/Deprecated/SimpleViewer/Qt/mainQt.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-#include "Applications/Qt/QtStoneApplicationRunner.h"
-
-#include "../../SimpleViewerApplication.h"
-#include "Framework/Messages/MessageBroker.h"
-
-
-int main(int argc, char* argv[]) 
-{
-  OrthancStone::MessageBroker broker;
-  SimpleViewer::SimpleViewerApplication stoneApplication(broker);
-
-  OrthancStone::QtStoneApplicationRunner qtAppRunner(broker, stoneApplication);
-  return qtAppRunner.Execute(argc, argv);
-}
--- a/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,225 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "SimpleViewerApplication.h"
-
-#if ORTHANC_ENABLE_QT == 1
-#  include "Qt/SimpleViewerMainWindow.h"
-#endif
-
-#if ORTHANC_ENABLE_WASM == 1
-#  include <Platforms/Wasm/WasmViewport.h>
-#endif
-
-namespace SimpleViewer
-{
-
-  void SimpleViewerApplication::Initialize(StoneApplicationContext* context,
-                                           Deprecated::IStatusBar& statusBar,
-                                           const boost::program_options::variables_map& parameters)
-  {
-    context_ = context;
-    statusBar_ = &statusBar;
-
-    {// initialize viewports and layout
-      mainLayout_ = new Deprecated::LayoutWidget("main-layout");
-      mainLayout_->SetPadding(10);
-      mainLayout_->SetBackgroundCleared(true);
-      mainLayout_->SetBackgroundColor(0, 0, 0);
-      mainLayout_->SetHorizontal();
-
-      thumbnailsLayout_ = new Deprecated::LayoutWidget("thumbnail-layout");
-      thumbnailsLayout_->SetPadding(10);
-      thumbnailsLayout_->SetBackgroundCleared(true);
-      thumbnailsLayout_->SetBackgroundColor(50, 50, 50);
-      thumbnailsLayout_->SetVertical();
-
-      mainWidget_ = new Deprecated::SliceViewerWidget(IObserver::GetBroker(), "main-viewport");
-      //mainWidget_->RegisterObserver(*this);
-
-      // hierarchy
-      mainLayout_->AddWidget(thumbnailsLayout_);
-      mainLayout_->AddWidget(mainWidget_);
-
-      // sources
-      smartLoader_.reset(new Deprecated::SmartLoader(IObserver::GetBroker(), context->GetOrthancApiClient()));
-      smartLoader_->SetImageQuality(Deprecated::SliceImageQuality_FullPam);
-
-      mainLayout_->SetTransmitMouseOver(true);
-      mainWidgetInteractor_.reset(new MainWidgetInteractor(*this));
-      mainWidget_->SetInteractor(*mainWidgetInteractor_);
-      thumbnailInteractor_.reset(new ThumbnailInteractor(*this));
-    }
-
-    statusBar.SetMessage("Use the key \"s\" to reinitialize the layout");
-    statusBar.SetMessage("Use the key \"n\" to go to next image in the main viewport");
-
-
-    if (parameters.count("studyId") < 1)
-    {
-      LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc";
-      context->GetOrthancApiClient().GetJsonAsync("/studies", new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyListReceived));
-    }
-    else
-    {
-      SelectStudy(parameters["studyId"].as<std::string>());
-    }
-  }
-
-
-  void SimpleViewerApplication::DeclareStartupOptions(boost::program_options::options_description& options)
-  {
-    boost::program_options::options_description generic("Sample options");
-    generic.add_options()
-        ("studyId", boost::program_options::value<std::string>(),
-         "Orthanc ID of the study")
-        ;
-
-    options.add(generic);
-  }
-
-  void SimpleViewerApplication::OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
-  {
-    const Json::Value& response = message.GetJson();
-
-    if (response.isArray() &&
-        response.size() >= 1)
-    {
-      SelectStudy(response[0].asString());
-    }
-  }
-  void SimpleViewerApplication::OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
-  {
-    const Json::Value& response = message.GetJson();
-
-    if (response.isObject() && response["Series"].isArray())
-    {
-      for (size_t i=0; i < response["Series"].size(); i++)
-      {
-        context_->GetOrthancApiClient().GetJsonAsync("/series/" + response["Series"][(int)i].asString(), new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnSeriesReceived));
-      }
-    }
-  }
-
-  void SimpleViewerApplication::OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
-  {
-    const Json::Value& response = message.GetJson();
-
-    if (response.isObject() &&
-        response["Instances"].isArray() &&
-        response["Instances"].size() > 0)
-    {
-      // keep track of all instances IDs
-      const std::string& seriesId = response["ID"].asString();
-      seriesTags_[seriesId] = response;
-      instancesIdsPerSeriesId_[seriesId] = std::vector<std::string>();
-      for (size_t i = 0; i < response["Instances"].size(); i++)
-      {
-        const std::string& instanceId = response["Instances"][static_cast<int>(i)].asString();
-        instancesIdsPerSeriesId_[seriesId].push_back(instanceId);
-      }
-
-      // load the first instance in the thumbnail
-      LoadThumbnailForSeries(seriesId, instancesIdsPerSeriesId_[seriesId][0]);
-
-      // if this is the first thumbnail loaded, load the first instance in the mainWidget
-      if (mainWidget_->GetLayerCount() == 0)
-      {
-        smartLoader_->SetFrameInWidget(*mainWidget_, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
-      }
-    }
-  }
-
-  void SimpleViewerApplication::LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId)
-  {
-    LOG(INFO) << "Loading thumbnail for series " << seriesId;
-    
-    Deprecated::SliceViewerWidget* thumbnailWidget = 
-      new Deprecated::SliceViewerWidget(IObserver::GetBroker(), "thumbnail-series-" + seriesId);
-    thumbnails_.push_back(thumbnailWidget);
-    thumbnailsLayout_->AddWidget(thumbnailWidget);
-    
-    thumbnailWidget->RegisterObserverCallback(
-      new Callable<SimpleViewerApplication, Deprecated::SliceViewerWidget::GeometryChangedMessage>
-      (*this, &SimpleViewerApplication::OnWidgetGeometryChanged));
-    
-    smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0);
-    thumbnailWidget->SetInteractor(*thumbnailInteractor_);
-  }
-
-  void SimpleViewerApplication::SelectStudy(const std::string& studyId)
-  {
-    context_->GetOrthancApiClient().GetJsonAsync("/studies/" + studyId, new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyReceived));
-  }
-
-  void SimpleViewerApplication::OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message)
-  {
-    // TODO: The "const_cast" could probably be replaced by "mainWidget_"
-    const_cast<Deprecated::SliceViewerWidget&>(message.GetOrigin()).FitContent();
-  }
-
-  void SimpleViewerApplication::SelectSeriesInMainViewport(const std::string& seriesId)
-  {
-    smartLoader_->SetFrameInWidget(*mainWidget_, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
-  }
-
-  bool SimpleViewerApplication::Handle(const StoneSampleCommands::SelectTool& value)
-  {
-    currentTool_ = value.tool;
-    return true;
-  }
-
-  bool SimpleViewerApplication::Handle(const StoneSampleCommands::Action& value)
-  {
-    switch (value.type)
-    {
-    case ActionType_Invert:
-      // TODO
-      break;
-    case ActionType_UndoCrop:
-      // TODO
-      break;
-    case ActionType_Rotate:
-      // TODO
-      break;
-    default:
-      throw std::runtime_error("Action type not supported");
-    }
-    return true;
-  }
-
-#if ORTHANC_ENABLE_QT==1
-  QStoneMainWindow* SimpleViewerApplication::CreateQtMainWindow()
-  {
-    return new SimpleViewerMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this);
-  }
-#endif
-
-#if ORTHANC_ENABLE_WASM==1
-  void SimpleViewerApplication::InitializeWasm() {
-
-    AttachWidgetToWasmViewport("canvasThumbnails", thumbnailsLayout_);
-    AttachWidgetToWasmViewport("canvasMain", mainWidget_);
-  }
-#endif
-
-
-}
--- a/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,175 +0,0 @@
-/**
- * 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
-
- /*
- This header contains the command definitions for the sample applications
- */
-#include "Applications/Samples/StoneSampleCommands_generated.hpp"
-using namespace StoneSampleCommands;
-
-#include "Applications/IStoneApplication.h"
-
-#include "../../../../Framework/Deprecated/Layers/CircleMeasureTracker.h"
-#include "../../../../Framework/Deprecated/Layers/LineMeasureTracker.h"
-#include "../../../../Framework/Deprecated/SmartLoader.h"
-#include "../../../../Framework/Deprecated/Widgets/LayoutWidget.h"
-#include "../../../../Framework/Deprecated/Widgets/SliceViewerWidget.h"
-#include "../../../../Framework/Messages/IObserver.h"
-
-#if ORTHANC_ENABLE_WASM==1
-#include "Platforms/Wasm/WasmPlatformApplicationAdapter.h"
-#include "Platforms/Wasm/Defaults.h"
-#endif
-
-#if ORTHANC_ENABLE_QT==1
-#include "Qt/SimpleViewerMainWindow.h"
-#endif
-
-#include <Core/Images/Font.h>
-#include <Core/Logging.h>
-
-#include "ThumbnailInteractor.h"
-#include "MainWidgetInteractor.h"
-#include "AppStatus.h"
-
-using namespace OrthancStone;
-
-
-namespace SimpleViewer
-{
-
-  class SimpleViewerApplication
-    : public IStoneApplication
-    , public IObserver
-    , public IObservable
-    , public StoneSampleCommands::IHandler
-  {
-  public:
-
-    struct StatusUpdatedMessage : public IMessage
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-
-      const AppStatus& status_;
-
-      StatusUpdatedMessage(const AppStatus& status)
-        : status_(status)
-      {
-      }
-    };
-
-  private:
-    Tool                                currentTool_;
-
-    std::unique_ptr<MainWidgetInteractor> mainWidgetInteractor_;
-    std::unique_ptr<ThumbnailInteractor>  thumbnailInteractor_;
-    Deprecated::LayoutWidget*                       mainLayout_;
-    Deprecated::LayoutWidget*                       thumbnailsLayout_;
-    Deprecated::SliceViewerWidget*                  mainWidget_;
-    std::vector<Deprecated::SliceViewerWidget*>     thumbnails_;
-    std::map<std::string, std::vector<std::string> > instancesIdsPerSeriesId_;
-    std::map<std::string, Json::Value>  seriesTags_;
-    unsigned int                        currentInstanceIndex_;
-    Deprecated::WidgetViewport*       wasmViewport1_;
-    Deprecated::WidgetViewport*       wasmViewport2_;
-
-    Deprecated::IStatusBar*                         statusBar_;
-    std::unique_ptr<Deprecated::SmartLoader>          smartLoader_;
-
-    Orthanc::Font                       font_;
-
-  public:
-    SimpleViewerApplication(MessageBroker& broker) :
-      IObserver(broker),
-      IObservable(broker),
-      currentTool_(StoneSampleCommands::Tool_LineMeasure),
-      mainLayout_(NULL),
-      currentInstanceIndex_(0),
-      wasmViewport1_(NULL),
-      wasmViewport2_(NULL)
-    {
-      font_.LoadFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
-    }
-
-    virtual void Finalize() ORTHANC_OVERRIDE {}
-    virtual Deprecated::IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainLayout_;}
-
-    virtual void DeclareStartupOptions(boost::program_options::options_description& options) ORTHANC_OVERRIDE;
-    virtual void Initialize(StoneApplicationContext* context,
-                            Deprecated::IStatusBar& statusBar,
-                            const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE;
-
-    void OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message);
-
-    void OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message);
-
-    void OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message);
-
-    void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId);
-
-    void SelectStudy(const std::string& studyId);
-
-    void OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message);
-
-    void SelectSeriesInMainViewport(const std::string& seriesId);
-
-
-    Tool GetCurrentTool() const
-    {
-      return currentTool_;
-    }
-
-    const Orthanc::Font& GetFont() const
-    {
-      return font_;
-    }
-
-    // ExecuteAction method was empty (its body was a single "TODO" comment)
-    virtual bool Handle(const SelectTool& value) ORTHANC_OVERRIDE;
-    virtual bool Handle(const Action& value) ORTHANC_OVERRIDE;
-
-    template<typename T>
-    bool ExecuteCommand(const T& cmd)
-    {
-      std::string cmdStr = StoneSampleCommands::StoneSerialize(cmd);
-      return StoneSampleCommands::StoneDispatchToHandler(cmdStr, this);
-    }
-
-    virtual void HandleSerializedMessage(const char* data) ORTHANC_OVERRIDE
-    {
-      StoneSampleCommands::StoneDispatchToHandler(data, this);
-    }
-
-    virtual std::string GetTitle() const ORTHANC_OVERRIDE {return "SimpleViewer";}
-
-#if ORTHANC_ENABLE_WASM==1
-    virtual void InitializeWasm() ORTHANC_OVERRIDE;
-#endif
-
-#if ORTHANC_ENABLE_QT==1
-    virtual QStoneMainWindow* CreateQtMainWindow();
-#endif
-  };
-
-
-}
--- a/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "ThumbnailInteractor.h"
-
-#include "SimpleViewerApplication.h"
-
-namespace SimpleViewer {
-
-  Deprecated::IWorldSceneMouseTracker* ThumbnailInteractor::CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
-                                                                   const Deprecated::ViewportGeometry& view,
-                                                                   MouseButton button,
-                                                                   KeyboardModifiers modifiers,
-                                                                   int viewportX,
-                                                                   int viewportY,
-                                                                   double x,
-                                                                   double y,
-                                                                   Deprecated::IStatusBar* statusBar,
-                                                                   const std::vector<Deprecated::Touch>& displayTouches)
-  {
-    if (button == MouseButton_Left)
-    {
-      statusBar->SetMessage("selected thumbnail " + widget.GetName());
-      std::string seriesId = widget.GetName().substr(strlen("thumbnail-series-"));
-      application_.SelectSeriesInMainViewport(seriesId);
-    }
-    return NULL;
-  }
-}
--- a/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-/**
- * 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 "../../../../Framework/Deprecated/Widgets/IWorldSceneInteractor.h"
-
-using namespace OrthancStone;
-
-namespace SimpleViewer {
-
-  class SimpleViewerApplication;
-
-  class ThumbnailInteractor : public Deprecated::IWorldSceneInteractor
-  {
-  private:
-    SimpleViewerApplication&  application_;
-  public:
-    ThumbnailInteractor(SimpleViewerApplication&  application) :
-      application_(application)
-    {
-    }
-
-    virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
-                                                                    const Deprecated::ViewportGeometry& view,
-                                                                    MouseButton button,
-                                                                    KeyboardModifiers modifiers,
-                                                                    int viewportX,
-                                                                    int viewportY,
-                                                                    double x,
-                                                                    double y,
-                                                                    Deprecated::IStatusBar* statusBar,
-                                                                    const std::vector<Deprecated::Touch>& displayTouches);
-
-    virtual void MouseOver(CairoContext& context,
-                           Deprecated::WorldSceneWidget& widget,
-                           const Deprecated::ViewportGeometry& view,
-                           double x,
-                           double y,
-                           Deprecated::IStatusBar* statusBar)
-    {}
-
-    virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
-                            MouseWheelDirection direction,
-                            KeyboardModifiers modifiers,
-                            Deprecated::IStatusBar* statusBar)
-    {}
-
-    virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
-                            KeyboardKeys key,
-                            char keyChar,
-                            KeyboardModifiers modifiers,
-                            Deprecated::IStatusBar* statusBar)
-    {}
-
-  };
-
-
-}
--- a/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "SimpleViewerWasmApplicationAdapter.h"
-
-namespace SimpleViewer
-{
-
-  SimpleViewerWasmApplicationAdapter::SimpleViewerWasmApplicationAdapter(MessageBroker &broker, SimpleViewerApplication &application)
-      : WasmPlatformApplicationAdapter(broker, application),
-        viewerApplication_(application)
-  {
-    application.RegisterObserverCallback(new Callable<SimpleViewerWasmApplicationAdapter, SimpleViewerApplication::StatusUpdatedMessage>(*this, &SimpleViewerWasmApplicationAdapter::OnStatusUpdated));
-  }
-
-  void SimpleViewerWasmApplicationAdapter::OnStatusUpdated(const SimpleViewerApplication::StatusUpdatedMessage &message)
-  {
-    Json::Value statusJson;
-    message.status_.ToJson(statusJson);
-
-    Json::Value event;
-    event["event"] = "appStatusUpdated";
-    event["data"] = statusJson;
-
-    Json::StreamWriterBuilder builder;
-    std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
-    std::ostringstream outputStr;
-
-    writer->write(event, &outputStr);
-
-    NotifyStatusUpdateFromCppToWebWithString(outputStr.str());
-  }
-
-} // namespace SimpleViewer
\ No newline at end of file
--- a/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/**
- * 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 <string>
-#include <Framework/Messages/IObserver.h>
-#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h>
-
-#include "../../SimpleViewerApplication.h"
-
-namespace SimpleViewer {
-
-  class SimpleViewerWasmApplicationAdapter : public WasmPlatformApplicationAdapter
-    {
-      SimpleViewerApplication&  viewerApplication_;
-
-    public:
-      SimpleViewerWasmApplicationAdapter(MessageBroker& broker, SimpleViewerApplication& application);
-
-    private:
-      void OnStatusUpdated(const SimpleViewerApplication::StatusUpdatedMessage& message);
-
-    };
-
-}
\ No newline at end of file
--- a/Applications/Samples/Deprecated/SimpleViewer/Wasm/mainWasm.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "Platforms/Wasm/WasmWebService.h"
-#include "Platforms/Wasm/WasmViewport.h"
-
-#include <emscripten/emscripten.h>
-
-#include "../../SimpleViewerApplication.h"
-#include "SimpleViewerWasmApplicationAdapter.h"
-
-
-OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker) {
-  
-  return new SimpleViewer::SimpleViewerApplication(broker);
-}
-
-OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, IStoneApplication* application)
-{
-  return new SimpleViewer::SimpleViewerWasmApplicationAdapter(broker, *(dynamic_cast<SimpleViewer::SimpleViewerApplication*>(application)));
-}
--- a/Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.html	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-<!doctype html>
-
-<html lang="us">
-  <head>
-    <meta charset="utf-8" />
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
-    <!-- Disable pinch zoom on mobile devices -->
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
-    <meta name="HandheldFriendly" content="true" />
-
-    <title>Simple Viewer</title>
-    <link href="styles.css" rel="stylesheet" />
-
-<body>
-  <div id="breadcrumb">
-    <span id="label-patient-id"></span>
-    <span id="label-study-description"></span>
-    <span id="label-series-description"></span>
-  </div>
-  <div style="height: calc(100% - 50px)">
-    <div style="width: 20%; height: 100%; display: inline-block">
-      <canvas id="canvasThumbnails"></canvas>
-    </div>
-    <div style="width: 70%; height: 100%; display: inline-block">
-      <canvas id="canvasMain"></canvas>
-    </div>
-  </div>
-  <div id="toolbox" style="height: 50px">
-    <button tool-selector="line-measure" class="tool-selector">line</button>
-    <button tool-selector="circle-measure" class="tool-selector">circle</button>
-    <button tool-selector="crop" class="tool-selector">crop</button>
-    <button tool-selector="windowing" class="tool-selector">windowing</button>
-    <button tool-selector="zoom" class="tool-selector">zoom</button>
-    <button tool-selector="pan" class="tool-selector">pan</button>
-    <button action-trigger="rotate-left" class="action-trigger">rotate left</button>
-    <button action-trigger="rotate-right" class="action-trigger">rotate right</button>
-    <button action-trigger="invert" class="action-trigger">invert</button>
-  </div>
-  <script type="text/javascript" src="app-simple-viewer.js"></script>
-</body>
-
-</html>
\ No newline at end of file
--- a/Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-import wasmApplicationRunner = require('../../../../Platforms/Wasm/wasm-application-runner');
-
-wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSimpleViewer", "/orthanc");
-
-function SelectTool(toolName: string) {
-  var command = {
-    command: "selectTool:" + toolName,
-    commandType: "generic-no-arg-command",
-    args: {
-    }                                                                                                                       
-  };
-  wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command));
-}
-
-function PerformAction(actionName: string) {
-  var command = {
-    command: "action:" + actionName,
-    commandType: "generic-no-arg-command",
-    args: {
-    }
-  };
-  wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command));
-}
-
-class SimpleViewerUI {
-
-  private _labelPatientId: HTMLSpanElement;
-  private _labelStudyDescription: HTMLSpanElement;
-
-  public constructor() {
-    // install "SelectTool" handlers
-    document.querySelectorAll("[tool-selector]").forEach((e) => {
-      (e as HTMLButtonElement).addEventListener("click", () => {
-        SelectTool(e.attributes["tool-selector"].value);
-      });
-    });
-
-    // install "PerformAction" handlers
-    document.querySelectorAll("[action-trigger]").forEach((e) => {
-      (e as HTMLButtonElement).addEventListener("click", () => {
-        PerformAction(e.attributes["action-trigger"].value);
-      });
-    });
-
-    // connect all ui elements to members
-    this._labelPatientId = document.getElementById("label-patient-id") as HTMLSpanElement;
-    this._labelStudyDescription = document.getElementById("label-study-description") as HTMLSpanElement;
-  }
-
-  public onAppStatusUpdated(status: any) {
-    this._labelPatientId.innerText = status["patientId"];
-    this._labelStudyDescription.innerText = status["studyDescription"];
-    // this.highlighThumbnail(status["currentInstanceIdInMainViewport"]);
-  }
-
-}
-
-var ui = new SimpleViewerUI();
-
-// this method is called "from the C++ code" when the StoneApplication is updated.
-// it can be used to update the UI of the application
-function UpdateWebApplicationWithString(statusUpdateMessageString: string) {
-  console.log("updating web application with string: ", statusUpdateMessageString);
-  let statusUpdateMessage = JSON.parse(statusUpdateMessageString);
-
-  if ("event" in statusUpdateMessage) {
-    let eventName = statusUpdateMessage["event"];
-    if (eventName == "appStatusUpdated") {
-      ui.onAppStatusUpdated(statusUpdateMessage["data"]);
-    }
-  }
-}
-
-function UpdateWebApplicationWithSerializedMessage(statusUpdateMessageString: string) {
-  console.log("updating web application with serialized message: ", statusUpdateMessageString);
-  console.log("<not supported in the simple viewer!>");
-}
-
-// make it available to other js scripts in the application
-(<any> window).UpdateWebApplicationWithString = UpdateWebApplicationWithString;
-(<any> window).UpdateWebApplicationWithSerializedMessage = UpdateWebApplicationWithSerializedMessage;
--- a/Applications/Samples/Deprecated/SimpleViewer/Wasm/styles.css	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-html, body {
-    width: 100%;
-    height: 100%;
-    margin: 0px;
-    border: 0;
-    overflow: hidden; /*  Disable scrollbars */
-    display: block;  /* No floating content on sides */
-    background-color: black;
-    color: white;
-    font-family: Arial, Helvetica, sans-serif;
-}
-
-canvas {
-    left:0px;
-    top:0px;
-}
-
-#canvas-group {
-    padding:5px;
-    background-color: grey;
-}
-
-#status-group {
-    padding:5px;
-}
-
-#worklist-group {
-    padding:5px;
-}
-
-.vsol-button {
-    height: 40px;
-}
-
-#thumbnails-group ul li {
-    display: inline;
-    list-style: none;
-}
-
-.thumbnail {
-    width: 100px;
-    height: 100px;
-    padding: 3px;
-}
-
-.thumbnail-selected {
-    border-width: 1px;
-    border-color: red;
-    border-style: solid;
-}
-
-#template-thumbnail-li {
-    display: none !important;
-}
\ No newline at end of file
--- a/Applications/Samples/Deprecated/SimpleViewer/Wasm/tsconfig-simple-viewer.json	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-{
-    "extends" : "../../Web/tsconfig-samples",
-    "compilerOptions": {
-    },
-    "include" : [
-        "simple-viewer.ts",
-        "../../build-wasm/ApplicationCommands_generated.ts"
-    ]
-}
--- a/Applications/Samples/Deprecated/SimpleViewerApplicationSingleFile.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,461 +0,0 @@
-/**
- * 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 "SampleApplicationBase.h"
-
-#include "../../../Framework/Deprecated/Layers/CircleMeasureTracker.h"
-#include "../../../Framework/Deprecated/Layers/LineMeasureTracker.h"
-#include "../../../Framework/Deprecated/SmartLoader.h"
-#include "../../../Framework/Deprecated/Widgets/LayoutWidget.h"
-#include "../../../Framework/Deprecated/Widgets/SliceViewerWidget.h"
-#include "../../../Framework/Messages/IObserver.h"
-
-#if ORTHANC_ENABLE_WASM==1
-#include "../../../Platforms/Wasm/WasmPlatformApplicationAdapter.h"
-#include "../../../Platforms/Wasm/Defaults.h"
-#endif
-
-#include <Core/Images/Font.h>
-#include <Core/Logging.h>
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class SimpleViewerApplication :
-      public SampleSingleCanvasWithButtonsApplicationBase,
-      public ObserverBase<SimpleViewerApplication>
-    {
-    private:
-      class ThumbnailInteractor : public Deprecated::IWorldSceneInteractor
-      {
-      private:
-        SimpleViewerApplication&  application_;
-
-      public:
-        ThumbnailInteractor(SimpleViewerApplication&  application) :
-          application_(application)
-        {
-        }
-
-        virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
-                                                            const Deprecated::ViewportGeometry& view,
-                                                            MouseButton button,
-                                                            KeyboardModifiers modifiers,
-                                                            int viewportX,
-                                                            int viewportY,
-                                                            double x,
-                                                            double y,
-                                                            Deprecated::IStatusBar* statusBar,
-                                                            const std::vector<Deprecated::Touch>& displayTouches)
-        {
-          if (button == MouseButton_Left)
-          {
-            statusBar->SetMessage("selected thumbnail " + widget.GetName());
-            std::string seriesId = widget.GetName().substr(strlen("thumbnail-series-"));
-            application_.SelectSeriesInMainViewport(seriesId);
-          }
-          return NULL;
-        }
-
-        virtual void MouseOver(CairoContext& context,
-                               Deprecated::WorldSceneWidget& widget,
-                               const Deprecated::ViewportGeometry& view,
-                               double x,
-                               double y,
-                               Deprecated::IStatusBar* statusBar)
-        {
-        }
-
-        virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
-                                MouseWheelDirection direction,
-                                KeyboardModifiers modifiers,
-                                Deprecated::IStatusBar* statusBar)
-        {
-        }
-
-        virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
-                                KeyboardKeys key,
-                                char keyChar,
-                                KeyboardModifiers modifiers,
-                                Deprecated::IStatusBar* statusBar)
-        {
-        }
-      };
-
-      class MainWidgetInteractor : public Deprecated::IWorldSceneInteractor
-      {
-      private:
-        SimpleViewerApplication&  application_;
-        
-      public:
-        MainWidgetInteractor(SimpleViewerApplication&  application) :
-          application_(application)
-        {
-        }
-        
-        virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
-                                                            const Deprecated::ViewportGeometry& view,
-                                                            MouseButton button,
-                                                            KeyboardModifiers modifiers,
-                                                            int viewportX,
-                                                            int viewportY,
-                                                            double x,
-                                                            double y,
-                                                            Deprecated::IStatusBar* statusBar,
-                                                            const std::vector<Deprecated::Touch>& displayTouches)
-        {
-          if (button == MouseButton_Left)
-          {
-            if (application_.currentTool_ == Tool_LineMeasure)
-            {
-              return new Deprecated::LineMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(),
-                                            x, y, 255, 0, 0, application_.GetFont());
-            }
-            else if (application_.currentTool_ == Tool_CircleMeasure)
-            {
-              return new Deprecated::CircleMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(),
-                                              x, y, 255, 0, 0, application_.GetFont());
-            }
-          }
-          return NULL;
-        }
-
-        virtual void MouseOver(CairoContext& context,
-                               Deprecated::WorldSceneWidget& widget,
-                               const Deprecated::ViewportGeometry& view,
-                               double x,
-                               double y,
-                               Deprecated::IStatusBar* statusBar)
-        {
-          if (statusBar != NULL)
-          {
-            Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
-            
-            char buf[64];
-            sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)",
-                    p[0] / 10.0, p[1] / 10.0, p[2] / 10.0);
-            statusBar->SetMessage(buf);
-          }
-        }
-
-        virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
-                                MouseWheelDirection direction,
-                                KeyboardModifiers modifiers,
-                                Deprecated::IStatusBar* statusBar)
-        {
-        }
-
-        virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
-                                KeyboardKeys key,
-                                char keyChar,
-                                KeyboardModifiers modifiers,
-                                Deprecated::IStatusBar* statusBar)
-        {
-          switch (keyChar)
-          {
-            case 's':
-              widget.FitContent();
-              break;
-
-            case 'l':
-              application_.currentTool_ = Tool_LineMeasure;
-              break;
-
-            case 'c':
-              application_.currentTool_ = Tool_CircleMeasure;
-              break;
-
-            default:
-              break;
-          }
-        }
-      };
-
-
-#if ORTHANC_ENABLE_WASM==1
-      class SimpleViewerApplicationAdapter : public WasmPlatformApplicationAdapter
-      {
-        SimpleViewerApplication&  viewerApplication_;
-
-      public:
-        SimpleViewerApplicationAdapter(SimpleViewerApplication& application)
-          : WasmPlatformApplicationAdapter(application),
-            viewerApplication_(application)
-        {
-        }
-
-        virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input) 
-        {
-          if (input == "select-tool:line-measure")
-          {
-            viewerApplication_.currentTool_ = Tool_LineMeasure;
-            NotifyStatusUpdateFromCppToWebWithString("currentTool=line-measure");
-          }
-          else if (input == "select-tool:circle-measure")
-          {
-            viewerApplication_.currentTool_ = Tool_CircleMeasure;
-            NotifyStatusUpdateFromCppToWebWithString("currentTool=circle-measure");
-          }
-
-          output = "ok";
-        }
-
-        virtual void NotifySerializedMessageFromCppToWeb(const std::string& statusUpdateMessage) 
-        {
-          UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str());
-        }
-
-        virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage) 
-        {
-          UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str());
-        }
-
-      };
-#endif
-      enum Tool {
-        Tool_LineMeasure,
-        Tool_CircleMeasure
-      };
-
-      Tool                                 currentTool_;
-      std::unique_ptr<MainWidgetInteractor>  mainWidgetInteractor_;
-      std::unique_ptr<ThumbnailInteractor>   thumbnailInteractor_;
-      Deprecated::LayoutWidget*                        mainLayout_;
-      Deprecated::LayoutWidget*                        thumbnailsLayout_;
-      std::vector<boost::shared_ptr<Deprecated::SliceViewerWidget> >      thumbnails_;
-
-      std::map<std::string, std::vector<std::string> > instancesIdsPerSeriesId_;
-      std::map<std::string, Json::Value> seriesTags_;
-
-      unsigned int                         currentInstanceIndex_;
-      Deprecated::WidgetViewport*        wasmViewport1_;
-      Deprecated::WidgetViewport*        wasmViewport2_;
-
-      Deprecated::IStatusBar*                          statusBar_;
-      std::unique_ptr<Deprecated::SmartLoader>           smartLoader_;
-
-      Orthanc::Font                        font_;
-
-    public:
-      SimpleViewerApplication() :
-        currentTool_(Tool_LineMeasure),
-        mainLayout_(NULL),
-        currentInstanceIndex_(0),
-        wasmViewport1_(NULL),
-        wasmViewport2_(NULL)
-      {
-        font_.LoadFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
-//        DeclareIgnoredMessage(MessageType_Widget_ContentChanged);
-      }
-
-      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
-      {
-        boost::program_options::options_description generic("Sample options");
-        generic.add_options()
-          ("studyId", boost::program_options::value<std::string>(),
-           "Orthanc ID of the study")
-          ;
-
-        options.add(generic);
-      }
-
-      virtual void Initialize(StoneApplicationContext* context,
-                              Deprecated::IStatusBar& statusBar,
-                              const boost::program_options::variables_map& parameters)
-      {
-        using namespace OrthancStone;
-
-        context_ = context;
-        statusBar_ = &statusBar;
-
-        {// initialize viewports and layout
-          mainLayout_ = new Deprecated::LayoutWidget("main-layout");
-          mainLayout_->SetPadding(10);
-          mainLayout_->SetBackgroundCleared(true);
-          mainLayout_->SetBackgroundColor(0, 0, 0);
-          mainLayout_->SetHorizontal();
-
-          boost::shared_ptr<Deprecated::LayoutWidget> thumbnailsLayout_(new Deprecated::LayoutWidget("thumbnail-layout"));
-          thumbnailsLayout_->SetPadding(10);
-          thumbnailsLayout_->SetBackgroundCleared(true);
-          thumbnailsLayout_->SetBackgroundColor(50, 50, 50);
-          thumbnailsLayout_->SetVertical();
-
-          boost::shared_ptr<Deprecated::SliceViewerWidget> widget
-            (new Deprecated::SliceViewerWidget("main-viewport"));
-          SetCentralWidget(widget);
-          //mainWidget_->RegisterObserver(*this);
-
-          // hierarchy
-          mainLayout_->AddWidget(thumbnailsLayout_);
-          mainLayout_->AddWidget(widget);
-
-          // sources
-          smartLoader_.reset(new Deprecated::SmartLoader(context->GetOrthancApiClient()));
-          smartLoader_->SetImageQuality(Deprecated::SliceImageQuality_FullPam);
-
-          mainLayout_->SetTransmitMouseOver(true);
-          mainWidgetInteractor_.reset(new MainWidgetInteractor(*this));
-          widget->SetInteractor(*mainWidgetInteractor_);
-          thumbnailInteractor_.reset(new ThumbnailInteractor(*this));
-        }
-
-        statusBar.SetMessage("Use the key \"s\" to reinitialize the layout");
-        statusBar.SetMessage("Use the key \"n\" to go to next image in the main viewport");
-
-
-        if (parameters.count("studyId") < 1)
-        {
-          LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc";
-          context->GetOrthancApiClient()->GetJsonAsync(
-            "/studies",
-            new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
-            (GetSharedObserver(), &SimpleViewerApplication::OnStudyListReceived));
-        }
-        else
-        {
-          SelectStudy(parameters["studyId"].as<std::string>());
-        }
-      }
-
-      void OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
-      {
-        const Json::Value& response = message.GetJson();
-
-        if (response.isArray() &&
-            response.size() >= 1)
-        {
-          SelectStudy(response[0].asString());
-        }
-      }
-      
-      void OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
-      {
-        const Json::Value& response = message.GetJson();
-
-        if (response.isObject() && response["Series"].isArray())
-        {
-          for (size_t i=0; i < response["Series"].size(); i++)
-          {
-            context_->GetOrthancApiClient()->GetJsonAsync(
-              "/series/" + response["Series"][(int)i].asString(),
-              new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
-              (GetSharedObserver(), &SimpleViewerApplication::OnSeriesReceived));
-          }
-        }
-      }
-
-      void OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
-      {
-        const Json::Value& response = message.GetJson();
-
-        if (response.isObject() &&
-            response["Instances"].isArray() &&
-            response["Instances"].size() > 0)
-        {
-          // keep track of all instances IDs
-          const std::string& seriesId = response["ID"].asString();
-          seriesTags_[seriesId] = response;
-          instancesIdsPerSeriesId_[seriesId] = std::vector<std::string>();
-          for (size_t i = 0; i < response["Instances"].size(); i++)
-          {
-            const std::string& instanceId = response["Instances"][static_cast<int>(i)].asString();
-            instancesIdsPerSeriesId_[seriesId].push_back(instanceId);
-          }
-
-          // load the first instance in the thumbnail
-          LoadThumbnailForSeries(seriesId, instancesIdsPerSeriesId_[seriesId][0]);
-
-          // if this is the first thumbnail loaded, load the first instance in the mainWidget
-          Deprecated::SliceViewerWidget& widget = dynamic_cast<Deprecated::SliceViewerWidget&>(*GetCentralWidget());
-          if (widget.GetLayerCount() == 0)
-          {
-            smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
-          }
-        }
-      }
-
-      void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId)
-      {
-        LOG(INFO) << "Loading thumbnail for series " << seriesId;
-        boost::shared_ptr<Deprecated::SliceViewerWidget> thumbnailWidget(new Deprecated::SliceViewerWidget("thumbnail-series-" + seriesId));
-        thumbnails_.push_back(thumbnailWidget);
-        thumbnailsLayout_->AddWidget(thumbnailWidget);
-        Register<Deprecated::SliceViewerWidget::GeometryChangedMessage>(*thumbnailWidget, &SimpleViewerApplication::OnWidgetGeometryChanged);
-        smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0);
-        thumbnailWidget->SetInteractor(*thumbnailInteractor_);
-      }
-
-      void SelectStudy(const std::string& studyId)
-      {
-        LOG(INFO) << "Selecting study: " << studyId;
-        context_->GetOrthancApiClient()->GetJsonAsync(
-          "/studies/" + studyId, new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
-          (GetSharedObserver(), &SimpleViewerApplication::OnStudyReceived));
-      }
-
-      void OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message)
-      {
-        // TODO: The "const_cast" could probably be replaced by "mainWidget"
-        const_cast<Deprecated::SliceViewerWidget&>(message.GetOrigin()).FitContent();
-      }
-
-      void SelectSeriesInMainViewport(const std::string& seriesId)
-      {
-        Deprecated::SliceViewerWidget& widget = dynamic_cast<Deprecated::SliceViewerWidget&>(*GetCentralWidget());
-        smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
-      }
-
-      const Orthanc::Font& GetFont() const
-      {
-        return font_;
-      }
-      
-      virtual void OnPushButton1Clicked() {}
-      virtual void OnPushButton2Clicked() {}
-      virtual void OnTool1Clicked() { currentTool_ = Tool_LineMeasure;}
-      virtual void OnTool2Clicked() { currentTool_ = Tool_CircleMeasure;}
-
-      virtual void GetButtonNames(std::string& pushButton1,
-                                  std::string& pushButton2,
-                                  std::string& tool1,
-                                  std::string& tool2)
-      {
-        tool1 = "line";
-        tool2 = "circle";
-        pushButton1 = "action1";
-        pushButton2 = "action2";
-      }
-
-#if ORTHANC_ENABLE_WASM==1
-      virtual void InitializeWasm()
-      {
-        AttachWidgetToWasmViewport("canvas", thumbnailsLayout_);
-        AttachWidgetToWasmViewport("canvas2", widget);
-      }
-#endif
-
-    };
-  }
-}
--- a/Applications/Samples/Deprecated/SingleFrameApplication.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,268 +0,0 @@
-/**
- * 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 "SampleApplicationBase.h"
-
-#include "../../../Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.h"
-#include "../../../Framework/Deprecated/Widgets/SliceViewerWidget.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-#include <boost/math/constants/constants.hpp>
-
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class SingleFrameApplication :
-      public SampleSingleCanvasApplicationBase,
-      public ObserverBase<SingleFrameApplication>
-    {
-    private:
-      class Interactor : public Deprecated::IWorldSceneInteractor
-      {
-      private:
-        SingleFrameApplication&  application_;
-        
-      public:
-        Interactor(SingleFrameApplication&  application) :
-          application_(application)
-        {
-        }
-        
-        virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
-                                                                        const Deprecated::ViewportGeometry& view,
-                                                                        MouseButton button,
-                                                                        KeyboardModifiers modifiers,
-                                                                        int viewportX,
-                                                                        int viewportY,
-                                                                        double x,
-                                                                        double y,
-                                                                        Deprecated::IStatusBar* statusBar,
-                                                                        const std::vector<Deprecated::Touch>& displayTouches)
-        {
-          return NULL;
-        }
-
-        virtual void MouseOver(CairoContext& context,
-                               Deprecated::WorldSceneWidget& widget,
-                               const Deprecated::ViewportGeometry& view,
-                               double x,
-                               double y,
-                               Deprecated::IStatusBar* statusBar)
-        {
-          if (statusBar != NULL)
-          {
-            Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
-            
-            char buf[64];
-            sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", 
-                    p[0] / 10.0, p[1] / 10.0, p[2] / 10.0);
-            statusBar->SetMessage(buf);
-          }
-        }
-
-        virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
-                                MouseWheelDirection direction,
-                                KeyboardModifiers modifiers,
-                                Deprecated::IStatusBar* statusBar)
-        {
-          int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1);
-          
-          switch (direction)
-          {
-            case MouseWheelDirection_Up:
-              application_.OffsetSlice(-scale);
-              break;
-
-            case MouseWheelDirection_Down:
-              application_.OffsetSlice(scale);
-              break;
-
-            default:
-              break;
-          }
-        }
-
-        virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
-                                KeyboardKeys key,
-                                char keyChar,
-                                KeyboardModifiers modifiers,
-                                Deprecated::IStatusBar* statusBar)
-        {
-          switch (keyChar)
-          {
-            case 's':
-              widget.FitContent();
-              break;
-
-            default:
-              break;
-          }
-        }
-      };
-
-
-      void OffsetSlice(int offset)
-      {
-        if (source_)
-        {
-          int slice = static_cast<int>(slice_) + offset;
-
-          if (slice < 0)
-          {
-            slice = 0;
-          }
-
-          if (slice >= static_cast<int>(source_->GetSlicesCount()))
-          {
-            slice = static_cast<int>(source_->GetSlicesCount()) - 1;
-          }
-
-          if (slice != static_cast<int>(slice_)) 
-          {
-            SetSlice(slice);
-          }   
-        }
-      }
-
-
-      void SetSlice(size_t index)
-      {
-        if (source_ &&
-            index < source_->GetSlicesCount())
-        {
-          slice_ = static_cast<unsigned int>(index);
-          
-#if 1
-          widget_->SetSlice(source_->GetSlice(slice_).GetGeometry());
-#else
-          // TEST for scene extents - Rotate the axes
-          double a = 15.0 / 180.0 * boost::math::constants::pi<double>();
-
-#if 1
-          Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0);
-          Vector y; GeometryToolbox::AssignVector(y, -sin(a), cos(a), 0);
-#else
-          // Flip the normal
-          Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0);
-          Vector y; GeometryToolbox::AssignVector(y, sin(a), -cos(a), 0);
-#endif
-          
-          SliceGeometry s(source_->GetSlice(slice_).GetGeometry().GetOrigin(), x, y);
-          widget_->SetSlice(s);
-#endif
-        }
-      }
-        
-      
-      void OnMainWidgetGeometryReady(const Deprecated::IVolumeSlicer::GeometryReadyMessage& message)
-      {
-        // Once the geometry of the series is downloaded from Orthanc,
-        // display its middle slice, and adapt the viewport to fit this
-        // slice
-        if (source_ &&
-            source_.get() == &message.GetOrigin())
-        {
-          SetSlice(source_->GetSlicesCount() / 2);
-        }
-
-        widget_->FitContent();
-      }
-
-      boost::shared_ptr<Deprecated::SliceViewerWidget>  widget_;
-      std::unique_ptr<Interactor>         mainWidgetInteractor_;
-      boost::shared_ptr<Deprecated::DicomSeriesVolumeSlicer> source_;
-      unsigned int                      slice_;
-
-    public:
-      SingleFrameApplication() :
-        slice_(0)
-      {
-      }
-      
-      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
-      {
-        boost::program_options::options_description generic("Sample options");
-        generic.add_options()
-          ("instance", boost::program_options::value<std::string>(), 
-           "Orthanc ID of the instance")
-          ("frame", boost::program_options::value<unsigned int>()->default_value(0),
-           "Number of the frame, for multi-frame DICOM instances")
-          ("smooth", boost::program_options::value<bool>()->default_value(true), 
-           "Enable bilinear interpolation to smooth the image")
-          ;
-
-        options.add(generic);    
-      }
-
-      virtual void Initialize(StoneApplicationContext* context,
-                              Deprecated::IStatusBar& statusBar,
-                              const boost::program_options::variables_map& parameters)
-      {
-        using namespace OrthancStone;
-
-        context_ = context;
-
-        statusBar.SetMessage("Use the key \"s\" to reinitialize the layout");
-
-        if (parameters.count("instance") != 1)
-        {
-          LOG(ERROR) << "The instance ID is missing";
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-        }
-
-        std::string instance = parameters["instance"].as<std::string>();
-        int frame = parameters["frame"].as<unsigned int>();
-
-        widget_.reset(new Deprecated::SliceViewerWidget("main-widget"));
-        SetCentralWidget(widget_);
-
-        boost::shared_ptr<Deprecated::DicomSeriesVolumeSlicer> layer(new Deprecated::DicomSeriesVolumeSlicer);
-        layer->Connect(context->GetOrthancApiClient());
-        source_ = layer;
-
-        layer->LoadFrame(instance, frame);
-        Register<Deprecated::IVolumeSlicer::GeometryReadyMessage>(*layer, &SingleFrameApplication::OnMainWidgetGeometryReady);
-        widget_->AddLayer(layer);
-
-        Deprecated::RenderStyle s;
-
-        if (parameters["smooth"].as<bool>())
-        {
-          s.interpolation_ = ImageInterpolation_Bilinear;
-        }
-
-        widget_->SetLayerStyle(0, s);
-        widget_->SetTransmitMouseOver(true);
-
-        mainWidgetInteractor_.reset(new Interactor(*this));
-        widget_->SetInteractor(*mainWidgetInteractor_);
-      }
-    };
-
-
-  }
-}
--- a/Applications/Samples/Deprecated/SingleFrameEditorApplication.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,531 +0,0 @@
-/**
- * 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 "SampleApplicationBase.h"
-
-#include "../../../Framework/Radiography/RadiographyLayerCropTracker.h"
-#include "../../../Framework/Radiography/RadiographyLayerMaskTracker.h"
-#include "../../../Framework/Radiography/RadiographyLayerMoveTracker.h"
-#include "../../../Framework/Radiography/RadiographyLayerResizeTracker.h"
-#include "../../../Framework/Radiography/RadiographyLayerRotateTracker.h"
-#include "../../../Framework/Radiography/RadiographyMaskLayer.h"
-#include "../../../Framework/Radiography/RadiographyScene.h"
-#include "../../../Framework/Radiography/RadiographySceneCommand.h"
-#include "../../../Framework/Radiography/RadiographySceneReader.h"
-#include "../../../Framework/Radiography/RadiographySceneWriter.h"
-#include "../../../Framework/Radiography/RadiographyWidget.h"
-#include "../../../Framework/Radiography/RadiographyWindowingTracker.h"
-#include "../../../Framework/Toolbox/TextRenderer.h"
-
-#include <Core/HttpClient.h>
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-#include <Core/Images/PngWriter.h>
-#include <Core/Images/PngReader.h>
-
-
-// Export using PAM is faster than using PNG, but requires Orthanc
-// core >= 1.4.3
-#define EXPORT_USING_PAM  1
-
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class RadiographyEditorInteractor :
-        public Deprecated::IWorldSceneInteractor,
-        public ObserverBase<RadiographyEditorInteractor>
-    {
-    private:
-      enum Tool
-      {
-        Tool_Move,
-        Tool_Rotate,
-        Tool_Crop,
-        Tool_Resize,
-        Tool_Mask,
-        Tool_Windowing
-      };
-
-
-      StoneApplicationContext*  context_;
-      UndoRedoStack             undoRedoStack_;
-      Tool                      tool_;
-      RadiographyMaskLayer*     maskLayer_;
-
-
-      static double GetHandleSize()
-      {
-        return 10.0;
-      }
-
-
-    public:
-      RadiographyEditorInteractor() :
-        context_(NULL),
-        tool_(Tool_Move),
-        maskLayer_(NULL)
-      {
-      }
-
-      void SetContext(StoneApplicationContext& context)
-      {
-        context_ = &context;
-      }
-
-      void SetMaskLayer(RadiographyMaskLayer* maskLayer)
-      {
-        maskLayer_ = maskLayer;
-      }
-      virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& worldWidget,
-                                                                      const Deprecated::ViewportGeometry& view,
-                                                                      MouseButton button,
-                                                                      KeyboardModifiers modifiers,
-                                                                      int viewportX,
-                                                                      int viewportY,
-                                                                      double x,
-                                                                      double y,
-                                                                      Deprecated::IStatusBar* statusBar,
-                                                                      const std::vector<Deprecated::Touch>& displayTouches)
-      {
-        RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
-
-        if (button == MouseButton_Left)
-        {
-          size_t selected;
-
-          if (tool_ == Tool_Windowing)
-          {
-            return new RadiographyWindowingTracker(
-                  undoRedoStack_,
-                  widget.GetScene(),
-                  widget,
-                  OrthancStone::ImageInterpolation_Nearest,
-                  viewportX, viewportY,
-                  RadiographyWindowingTracker::Action_DecreaseWidth,
-                  RadiographyWindowingTracker::Action_IncreaseWidth,
-                  RadiographyWindowingTracker::Action_DecreaseCenter,
-                  RadiographyWindowingTracker::Action_IncreaseCenter);
-          }
-          else if (!widget.LookupSelectedLayer(selected))
-          {
-            // No layer is currently selected
-            size_t layer;
-            if (widget.GetScene().LookupLayer(layer, x, y))
-            {
-              widget.Select(layer);
-            }
-
-            return NULL;
-          }
-          else if (tool_ == Tool_Crop ||
-                   tool_ == Tool_Resize ||
-                   tool_ == Tool_Mask)
-          {
-            RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected);
-            
-            ControlPoint controlPoint;
-            if (accessor.GetLayer().LookupControlPoint(controlPoint, x, y, view.GetZoom(), GetHandleSize()))
-            {
-              switch (tool_)
-              {
-              case Tool_Crop:
-                return new RadiographyLayerCropTracker
-                    (undoRedoStack_, widget.GetScene(), view, selected, controlPoint);
-
-              case Tool_Mask:
-                return new RadiographyLayerMaskTracker
-                    (undoRedoStack_, widget.GetScene(), view, selected, controlPoint);
-
-              case Tool_Resize:
-                return new RadiographyLayerResizeTracker
-                    (undoRedoStack_, widget.GetScene(), selected, controlPoint,
-                     (modifiers & KeyboardModifiers_Shift));
-
-              default:
-                throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-              }
-            }
-            else
-            {
-              size_t layer;
-
-              if (widget.GetScene().LookupLayer(layer, x, y))
-              {
-                widget.Select(layer);
-              }
-              else
-              {
-                widget.Unselect();
-              }
-
-              return NULL;
-            }
-          }
-          else
-          {
-            size_t layer;
-
-            if (widget.GetScene().LookupLayer(layer, x, y))
-            {
-              if (layer == selected)
-              {
-                switch (tool_)
-                {
-                case Tool_Move:
-                  return new RadiographyLayerMoveTracker
-                      (undoRedoStack_, widget.GetScene(), layer, x, y,
-                       (modifiers & KeyboardModifiers_Shift));
-
-                case Tool_Rotate:
-                  return new RadiographyLayerRotateTracker
-                      (undoRedoStack_, widget.GetScene(), view, layer, x, y,
-                       (modifiers & KeyboardModifiers_Shift));
-
-                default:
-                  break;
-                }
-
-                return NULL;
-              }
-              else
-              {
-                widget.Select(layer);
-                return NULL;
-              }
-            }
-            else
-            {
-              widget.Unselect();
-              return NULL;
-            }
-          }
-        }
-        else
-        {
-          return NULL;
-        }
-        return NULL;
-      }
-
-      virtual void MouseOver(CairoContext& context,
-                             Deprecated::WorldSceneWidget& worldWidget,
-                             const Deprecated::ViewportGeometry& view,
-                             double x,
-                             double y,
-                             Deprecated::IStatusBar* statusBar)
-      {
-        RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
-
-#if 0
-        if (statusBar != NULL)
-        {
-          char buf[64];
-          sprintf(buf, "X = %.02f Y = %.02f (in cm)", x / 10.0, y / 10.0);
-          statusBar->SetMessage(buf);
-        }
-#endif
-
-        size_t selected;
-
-        if (widget.LookupSelectedLayer(selected) &&
-            (tool_ == Tool_Crop ||
-             tool_ == Tool_Resize ||
-             tool_ == Tool_Mask))
-        {
-          RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected);
-
-          ControlPoint controlPoint;
-          if (accessor.GetLayer().LookupControlPoint(controlPoint, x, y, view.GetZoom(), GetHandleSize()))
-          {
-            double z = 1.0 / view.GetZoom();
-
-            context.SetSourceColor(255, 0, 0);
-            cairo_t* cr = context.GetObject();
-            cairo_set_line_width(cr, 2.0 * z);
-            cairo_move_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y - GetHandleSize() * z);
-            cairo_line_to(cr, controlPoint.x + GetHandleSize() * z, controlPoint.y - GetHandleSize() * z);
-            cairo_line_to(cr, controlPoint.x + GetHandleSize() * z, controlPoint.y + GetHandleSize() * z);
-            cairo_line_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y + GetHandleSize() * z);
-            cairo_line_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y - GetHandleSize() * z);
-            cairo_stroke(cr);
-          }
-        }
-      }
-
-      virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
-                              MouseWheelDirection direction,
-                              KeyboardModifiers modifiers,
-                              Deprecated::IStatusBar* statusBar)
-      {
-      }
-
-      virtual void KeyPressed(Deprecated::WorldSceneWidget& worldWidget,
-                              KeyboardKeys key,
-                              char keyChar,
-                              KeyboardModifiers modifiers,
-                              Deprecated::IStatusBar* statusBar)
-      {
-        RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
-
-        switch (keyChar)
-        {
-        case 'a':
-          widget.FitContent();
-          break;
-
-        case 'c':
-          tool_ = Tool_Crop;
-          break;
-
-        case 'm':
-          tool_ = Tool_Mask;
-          widget.Select(1);
-          break;
-
-        case 'd':
-        {
-          // dump to json and reload
-          Json::Value snapshot;
-          RadiographySceneWriter writer;
-          writer.Write(snapshot, widget.GetScene());
-
-          LOG(INFO) << "JSON export was successful: "
-                    << snapshot.toStyledString();
-
-          boost::shared_ptr<RadiographyScene> scene(new RadiographyScene);
-          RadiographySceneReader reader(*scene, *context_->GetOrthancApiClient());
-          reader.Read(snapshot);
-
-          widget.SetScene(scene);
-        };break;
-
-        case 'e':
-        {
-          Orthanc::DicomMap tags;
-
-          // Minimal set of tags to generate a valid CR image
-          tags.SetValue(Orthanc::DICOM_TAG_ACCESSION_NUMBER, "NOPE", false);
-          tags.SetValue(Orthanc::DICOM_TAG_BODY_PART_EXAMINED, "PELVIS", false);
-          tags.SetValue(Orthanc::DICOM_TAG_INSTANCE_NUMBER, "1", false);
-          //tags.SetValue(Orthanc::DICOM_TAG_LATERALITY, "", false);
-          tags.SetValue(Orthanc::DICOM_TAG_MANUFACTURER, "OSIMIS", false);
-          tags.SetValue(Orthanc::DICOM_TAG_MODALITY, "CR", false);
-          tags.SetValue(Orthanc::DICOM_TAG_PATIENT_BIRTH_DATE, "20000101", false);
-          tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ID, "hello", false);
-          tags.SetValue(Orthanc::DICOM_TAG_PATIENT_NAME, "HELLO^WORLD", false);
-          tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ORIENTATION, "", false);
-          tags.SetValue(Orthanc::DICOM_TAG_PATIENT_SEX, "M", false);
-          tags.SetValue(Orthanc::DICOM_TAG_REFERRING_PHYSICIAN_NAME, "HOUSE^MD", false);
-          tags.SetValue(Orthanc::DICOM_TAG_SERIES_NUMBER, "1", false);
-          tags.SetValue(Orthanc::DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.1", false);
-          tags.SetValue(Orthanc::DICOM_TAG_STUDY_ID, "STUDY", false);
-          tags.SetValue(Orthanc::DICOM_TAG_VIEW_POSITION, "", false);
-
-          if (context_ != NULL)
-          {
-            widget.GetScene().ExportDicom(*context_->GetOrthancApiClient(),
-                                          tags, std::string(), 0.1, 0.1, widget.IsInverted(),
-                                          false /* autoCrop */, widget.GetInterpolation(), EXPORT_USING_PAM);
-          }
-
-          break;
-        }
-
-        case 'i':
-          widget.SwitchInvert();
-          break;
-
-        case 't':
-          tool_ = Tool_Move;
-          break;
-
-        case 'n':
-        {
-          switch (widget.GetInterpolation())
-          {
-          case ImageInterpolation_Nearest:
-            LOG(INFO) << "Switching to bilinear interpolation";
-            widget.SetInterpolation(ImageInterpolation_Bilinear);
-            break;
-
-          case ImageInterpolation_Bilinear:
-            LOG(INFO) << "Switching to nearest neighbor interpolation";
-            widget.SetInterpolation(ImageInterpolation_Nearest);
-            break;
-
-          default:
-            throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-          }
-          
-          break;
-        }
-
-        case 'r':
-          tool_ = Tool_Rotate;
-          break;
-
-        case 's':
-          tool_ = Tool_Resize;
-          break;
-
-        case 'w':
-          tool_ = Tool_Windowing;
-          break;
-
-        case 'y':
-          if (modifiers & KeyboardModifiers_Control)
-          {
-            undoRedoStack_.Redo();
-            widget.NotifyContentChanged();
-          }
-          break;
-
-        case 'z':
-          if (modifiers & KeyboardModifiers_Control)
-          {
-            undoRedoStack_.Undo();
-            widget.NotifyContentChanged();
-          }
-          break;
-
-        default:
-          break;
-        }
-      }
-    };
-
-
-
-    class SingleFrameEditorApplication :
-        public SampleSingleCanvasApplicationBase,
-        public IObserver
-    {
-    private:
-      boost::shared_ptr<RadiographyScene>   scene_;
-      RadiographyEditorInteractor           interactor_;
-      RadiographyMaskLayer*                 maskLayer_;
-
-    public:
-      virtual ~SingleFrameEditorApplication()
-      {
-        LOG(WARNING) << "Destroying the application";
-      }
-      
-      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
-      {
-        boost::program_options::options_description generic("Sample options");
-        generic.add_options()
-            ("instance", boost::program_options::value<std::string>(),
-             "Orthanc ID of the instance")
-            ("frame", boost::program_options::value<unsigned int>()->default_value(0),
-             "Number of the frame, for multi-frame DICOM instances")
-            ;
-
-        options.add(generic);
-      }
-
-      virtual void Initialize(StoneApplicationContext* context,
-                              Deprecated::IStatusBar& statusBar,
-                              const boost::program_options::variables_map& parameters)
-      {
-        using namespace OrthancStone;
-
-        context_ = context;
-        interactor_.SetContext(*context);
-
-        statusBar.SetMessage("Use the key \"a\" to reinitialize the layout");
-        statusBar.SetMessage("Use the key \"c\" to crop");
-        statusBar.SetMessage("Use the key \"e\" to export DICOM to the Orthanc server");
-        statusBar.SetMessage("Use the key \"f\" to switch full screen");
-        statusBar.SetMessage("Use the key \"i\" to invert contrast");
-        statusBar.SetMessage("Use the key \"m\" to modify the mask");
-        statusBar.SetMessage("Use the key \"n\" to switch between nearest neighbor and bilinear interpolation");
-        statusBar.SetMessage("Use the key \"r\" to rotate objects");
-        statusBar.SetMessage("Use the key \"s\" to resize objects (not applicable to DICOM layers)");
-        statusBar.SetMessage("Use the key \"t\" to move (translate) objects");
-        statusBar.SetMessage("Use the key \"w\" to change windowing");
-        
-        statusBar.SetMessage("Use the key \"ctrl-z\" to undo action");
-        statusBar.SetMessage("Use the key \"ctrl-y\" to redo action");
-
-        if (parameters.count("instance") != 1)
-        {
-          LOG(ERROR) << "The instance ID is missing";
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-        }
-
-        std::string instance = parameters["instance"].as<std::string>();
-        //int frame = parameters["frame"].as<unsigned int>();
-
-        scene_.reset(new RadiographyScene);
-        
-        RadiographyLayer& dicomLayer = scene_->LoadDicomFrame(*context->GetOrthancApiClient(), instance, 0, false, NULL);
-        //scene_->LoadDicomFrame(instance, frame, false); //.SetPan(200, 0);
-        // = scene_->LoadDicomFrame(context->GetOrthancApiClient(), "61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", 0, false, NULL);
-
-#if !defined(ORTHANC_ENABLE_WASM) || ORTHANC_ENABLE_WASM != 1
-        Orthanc::HttpClient::ConfigureSsl(true, "/etc/ssl/certs/ca-certificates.crt");
-#endif
-        
-        //scene_->LoadDicomWebFrame(context->GetWebService());
-        
-        std::vector<Orthanc::ImageProcessing::ImagePoint> mask;
-        mask.push_back(Orthanc::ImageProcessing::ImagePoint(1100, 100));
-        mask.push_back(Orthanc::ImageProcessing::ImagePoint(1100, 1000));
-        mask.push_back(Orthanc::ImageProcessing::ImagePoint(2000, 1000));
-        mask.push_back(Orthanc::ImageProcessing::ImagePoint(2200, 150));
-        mask.push_back(Orthanc::ImageProcessing::ImagePoint(1500, 550));
-        maskLayer_ = dynamic_cast<RadiographyMaskLayer*>(&(scene_->LoadMask(mask, dynamic_cast<RadiographyDicomLayer&>(dicomLayer), 128.0f, NULL)));
-        interactor_.SetMaskLayer(maskLayer_);
-
-        {
-          std::unique_ptr<Orthanc::ImageAccessor> renderedTextAlpha(TextRenderer::Render(Orthanc::EmbeddedResources::UBUNTU_FONT, 100,
-                                                                                    "%öÇaA&#"));
-          RadiographyLayer& layer = scene_->LoadAlphaBitmap(renderedTextAlpha.release(), NULL);
-          dynamic_cast<RadiographyAlphaLayer&>(layer).SetForegroundValue(200.0f * 256.0f);
-        }
-
-        {
-          RadiographyTextLayer::RegisterFont("ubuntu", Orthanc::EmbeddedResources::UBUNTU_FONT);
-          RadiographyLayer& layer = scene_->LoadText("Hello\nworld", "ubuntu", 20, 128, NULL, false);
-          layer.SetResizeable(true);
-        }
-        
-        {
-          RadiographyLayer& layer = scene_->LoadTestBlock(100, 50, NULL);
-          layer.SetResizeable(true);
-          layer.SetPan(0, 200);
-        }
-        
-        boost::shared_ptr<RadiographyWidget> widget(new RadiographyWidget(scene_, "main-widget"));
-        widget->SetTransmitMouseOver(true);
-        widget->SetInteractor(interactor_);
-        SetCentralWidget(widget);
-
-        //scene_->SetWindowing(128, 256);
-      }
-    };
-  }
-}
--- a/Applications/Samples/Deprecated/SingleVolumeApplication.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,277 +0,0 @@
-/**
- * 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 "SampleApplicationBase.h"
-#include "../../../Framework/dev.h"
-#include "../../../Framework/Layers/LineMeasureTracker.h"
-#include "../../../Framework/Layers/CircleMeasureTracker.h"
-
-#include <Core/Toolbox.h>
-#include <Core/Logging.h>
-
-#include <Plugins/Samples/Common/OrthancHttpConnection.h>   // TODO REMOVE
-#include "../../../Framework/Layers/DicomStructureSetSlicer.h"   // TODO REMOVE
-#include "../../../Framework/Toolbox/MessagingToolbox.h"   // TODO REMOVE
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class SingleVolumeApplication : public SampleApplicationBase
-    {
-    private:
-      class Interactor : public VolumeImageInteractor
-      {
-      private:
-        SliceViewerWidget&  widget_;
-        size_t        layer_;
-        
-      protected:
-        virtual void NotifySliceContentChange(const ISlicedVolume& volume,
-                                       const size_t& sliceIndex,
-                                       const Slice& slice)
-        {
-          const OrthancVolumeImage& image = dynamic_cast<const OrthancVolumeImage&>(volume);
-
-          RenderStyle s = widget_.GetLayerStyle(layer_);
-
-          if (image.FitWindowingToRange(s, slice.GetConverter()))
-          {
-            //printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_);
-            widget_.SetLayerStyle(layer_, s);
-          }
-        }
-
-        virtual void MouseOver(CairoContext& context,
-                               WorldSceneWidget& widget,
-                               const ViewportGeometry& view,
-                               double x,
-                               double y,
-                               IStatusBar* statusBar)
-        {
-          const SliceViewerWidget& w = dynamic_cast<const SliceViewerWidget&>(widget);
-          Vector p = w.GetSlice().MapSliceToWorldCoordinates(x, y);
-          printf("%f %f %f\n", p[0], p[1], p[2]);
-        }
-      
-      public:
-        Interactor(OrthancVolumeImage& volume,
-                   SliceViewerWidget& widget,
-                   VolumeProjection projection,
-                   size_t layer) :
-          VolumeImageInteractor(volume, widget, projection),
-          widget_(widget),
-          layer_(layer)
-        {
-        }
-      };
-
-
-    public:
-      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
-      {
-        boost::program_options::options_description generic("Sample options");
-        generic.add_options()
-          ("series", boost::program_options::value<std::string>(), 
-           "Orthanc ID of the series")
-          ("instance", boost::program_options::value<std::string>(), 
-           "Orthanc ID of a multi-frame instance that describes a 3D volume")
-          ("threads", boost::program_options::value<unsigned int>()->default_value(3), 
-           "Number of download threads")
-          ("projection", boost::program_options::value<std::string>()->default_value("axial"), 
-           "Projection of interest (can be axial, sagittal or coronal)")
-          ("reverse", boost::program_options::value<bool>()->default_value(false), 
-           "Reverse the normal direction of the volume")
-          ;
-
-        options.add(generic);    
-      }
-
-      virtual void Initialize(IStatusBar& statusBar,
-                              const boost::program_options::variables_map& parameters)
-      {
-        using namespace OrthancStone;
-
-        if (parameters.count("series") > 1 ||
-            parameters.count("instance") > 1)
-        {
-          LOG(ERROR) << "Only one series or instance is allowed";
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-        }
-
-        if (parameters.count("series") == 1 &&
-            parameters.count("instance") == 1)
-        {
-          LOG(ERROR) << "Cannot specify both a series and an instance";
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-        }
-
-        std::string series;
-        if (parameters.count("series") == 1)
-        {
-          series = parameters["series"].as<std::string>();
-        }
-        
-        std::string instance;
-        if (parameters.count("instance") == 1)
-        {
-          instance = parameters["instance"].as<std::string>();
-        }
-        
-        if (series.empty() &&
-            instance.empty())
-        {
-          LOG(ERROR) << "The series ID or instance ID is missing";
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-        }
-
-        //unsigned int threads = parameters["threads"].as<unsigned int>();
-        //bool reverse = parameters["reverse"].as<bool>();
-
-        std::string tmp = parameters["projection"].as<std::string>();
-        Orthanc::Toolbox::ToLowerCase(tmp);
-
-        VolumeProjection projection;
-        if (tmp == "axial")
-        {
-          projection = VolumeProjection_Axial;
-        }
-        else if (tmp == "sagittal")
-        {
-          projection = VolumeProjection_Sagittal;
-        }
-        else if (tmp == "coronal")
-        {
-          projection = VolumeProjection_Coronal;
-        }
-        else
-        {
-          LOG(ERROR) << "Unknown projection: " << tmp;
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-        }
-
-        std::unique_ptr<SliceViewerWidget> widget(new SliceViewerWidget);
-
-#if 1
-        std::unique_ptr<OrthancVolumeImage> volume(new OrthancVolumeImage(context.GetWebService(), true));
-        if (series.empty())
-        {
-          volume->ScheduleLoadInstance(instance);
-        }
-        else
-        {
-          volume->ScheduleLoadSeries(series);
-        }
-
-        widget->AddLayer(new VolumeImageMPRSlicer(*volume));
-
-        context_->AddInteractor(new Interactor(*volume, *widget, projection, 0));
-        context_->AddSlicedVolume(volume.release());
-
-        if (1)
-        {
-          RenderStyle s;
-          //s.drawGrid_ = true;
-          s.alpha_ = 1;
-          s.windowing_ = ImageWindowing_Bone;
-          widget->SetLayerStyle(0, s);
-        }
-        else
-        {
-          RenderStyle s;
-          s.alpha_ = 1;
-          s.applyLut_ = true;
-          s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET;
-          s.interpolation_ = ImageInterpolation_Bilinear;
-          widget->SetLayerStyle(0, s);
-        }
-#else
-        std::unique_ptr<OrthancVolumeImage> ct(new OrthancVolumeImage(context_->GetWebService(), false));
-        //ct->ScheduleLoadSeries("15a6f44a-ac7b88fe-19c462d9-dddd918e-b01550d8");  // 0178023P
-        //ct->ScheduleLoadSeries("dd069910-4f090474-7d2bba07-e5c10783-f9e4fb1d");
-        //ct->ScheduleLoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // IBA
-        //ct->ScheduleLoadSeries("03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0");  // 0522c0001 TCIA
-        ct->ScheduleLoadSeries("295e8a13-dfed1320-ba6aebb2-9a13e20f-1b3eb953");  // Captain
-        
-        std::unique_ptr<OrthancVolumeImage> pet(new OrthancVolumeImage(context_->GetWebService(), true));
-        //pet->ScheduleLoadSeries("48d2997f-8e25cd81-dd715b64-bd79cdcc-e8fcee53");  // 0178023P
-        //pet->ScheduleLoadSeries("aabad2e7-80702b5d-e599d26c-4f13398e-38d58a9e");
-        //pet->ScheduleLoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // IBA 1
-        //pet->ScheduleLoadInstance("337876a1-a68a9718-f15abccd-38faafa1-b99b496a"); // IBA 2
-        //pet->ScheduleLoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb");  // IBA 3
-        //pet->ScheduleLoadInstance("269f26f4-0c83eeeb-2e67abbd-5467a40f-f1bec90c");  // 0522c0001 TCIA
-        pet->ScheduleLoadInstance("f080888c-0ab7528a-f7d9c28c-84980eb1-ff3b0ae6");  // Captain 1
-        //pet->ScheduleLoadInstance("4f78055b-6499a2c5-1e089290-394acc05-3ec781c1");  // Captain 2
-
-        std::unique_ptr<StructureSetLoader> rtStruct(new StructureSetLoader(context_->GetWebService()));
-        //rtStruct->ScheduleLoadInstance("c2ebc17b-6b3548db-5e5da170-b8ecab71-ea03add3");  // 0178023P
-        //rtStruct->ScheduleLoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // IBA
-        //rtStruct->ScheduleLoadInstance("17cd032b-ad92a438-ca05f06a-f9e96668-7e3e9e20");  // 0522c0001 TCIA
-        rtStruct->ScheduleLoadInstance("96c889ab-29fe5c54-dda6e66c-3949e4da-58f90d75");  // Captain
-        
-        widget->AddLayer(new VolumeImageMPRSlicer(*ct));
-        widget->AddLayer(new VolumeImageMPRSlicer(*pet));
-        widget->AddLayer(new DicomStructureSetSlicer(*rtStruct));
-        
-        context_->AddInteractor(new Interactor(*pet, *widget, projection, 1));
-        //context_->AddInteractor(new VolumeImageInteractor(*ct, *widget, projection));
-
-        context_->AddSlicedVolume(ct.release());
-        context_->AddSlicedVolume(pet.release());
-        context_->AddVolumeLoader(rtStruct.release());
-
-        {
-          RenderStyle s;
-          //s.drawGrid_ = true;
-          s.alpha_ = 1;
-          s.windowing_ = ImageWindowing_Bone;
-          widget->SetLayerStyle(0, s);
-        }
-
-        {
-          RenderStyle s;
-          //s.drawGrid_ = true;
-          s.SetColor(255, 0, 0);  // Draw missing PET layer in red
-          s.alpha_ = 0.5;
-          s.applyLut_ = true;
-          s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET;
-          s.interpolation_ = ImageInterpolation_Bilinear;
-          s.windowing_ = ImageWindowing_Custom;
-          s.customWindowCenter_ = 0;
-          s.customWindowWidth_ = 128;
-          widget->SetLayerStyle(1, s);
-        }
-#endif
-
-
-        statusBar.SetMessage("Use the keys \"b\", \"l\" and \"d\" to change Hounsfield windowing");
-        statusBar.SetMessage("Use the keys \"t\" to track the (X,Y,Z) mouse coordinates");
-        statusBar.SetMessage("Use the keys \"m\" to measure distances");
-        statusBar.SetMessage("Use the keys \"c\" to draw circles");
-
-        widget->SetTransmitMouseOver(true);
-        context_->SetCentralWidget(widget.release());
-      }
-    };
-  }
-}
--- a/Applications/Samples/Deprecated/StoneSampleCommands.yml	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-#
-#        1         2         3         4         5         6         7         8
-# 345678901234567890123456789012345678901234567890123456789012345678901234567890
-#
-rootName: StoneSampleCommands
-
-# +---------------------------------+
-# | Messages from TypeScript to C++ |
-# +---------------------------------+
-
-enum Tool:
-  - LineMeasure
-  - CircleMeasure
-  - Crop
-  - Windowing
-  - Zoom
-  - Pan
-  - Move
-  - Rotate
-  - Resize
-  - Mask
-
-struct SelectTool:
-  __handler: cpp
-  tool: Tool
-
-enum ActionType:
-  - UndoCrop
-  - Rotate
-  - Invert
-
-struct Action:
-  __handler: cpp
-  type: ActionType
-
--- a/Applications/Samples/Deprecated/StoneSampleCommands_generate.py	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-import sys
-import os
-
-# add the generation script location to the search paths
-sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'Resources', 'CodeGeneration'))
-
-# import the code generation tooling script
-import stonegentool
-
-schemaFile = os.path.join(os.path.dirname(__file__), 'StoneSampleCommands.yml')
-outDir = os.path.dirname(__file__)
-
-# ignition!
-stonegentool.Process(schemaFile, outDir)
-
-
--- a/Applications/Samples/Deprecated/StoneSampleCommands_generated.hpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,703 +0,0 @@
-/*
-         1         2         3         4         5         6         7
-12345678901234567890123456789012345678901234567890123456789012345678901234567890
-
-Generated on 2019-03-18 12:07:42.696093 by stonegentool
-
-*/
-#pragma once
-
-#include <exception>
-#include <iostream>
-#include <string>
-#include <sstream>
-#include <assert.h>
-#include <memory>
-#include <json/json.h>
-
-//#define STONEGEN_NO_CPP11 1
-
-#ifdef STONEGEN_NO_CPP11
-#define StoneSmartPtr std::unique_ptr
-#else 
-#define StoneSmartPtr std::unique_ptr
-#endif 
-
-namespace StoneSampleCommands
-{
-  /** Throws in case of problem */
-  inline void _StoneDeserializeValue(int32_t& destValue, const Json::Value& jsonValue)
-  {
-    destValue = jsonValue.asInt();
-  }
-
-  inline Json::Value _StoneSerializeValue(int32_t value)
-  {
-    Json::Value result(value);
-    return result;
-  }
-
-  inline void _StoneDeserializeValue(Json::Value& destValue, const Json::Value& jsonValue)
-  {
-    destValue = jsonValue;
-  }
-
-  inline Json::Value _StoneSerializeValue(Json::Value value)
-  {
-    return value;
-  }
-
-  /** Throws in case of problem */
-  inline void _StoneDeserializeValue(double& destValue, const Json::Value& jsonValue)
-  {
-    destValue = jsonValue.asDouble();
-  }
-
-  inline Json::Value _StoneSerializeValue(double value)
-  {
-    Json::Value result(value);
-    return result;
-  }
-
-  /** Throws in case of problem */
-  inline void _StoneDeserializeValue(bool& destValue, const Json::Value& jsonValue)
-  {
-    destValue = jsonValue.asBool();
-  }
-
-  inline Json::Value _StoneSerializeValue(bool value)
-  {
-    Json::Value result(value);
-    return result;
-  }
-
-  /** Throws in case of problem */
-  inline void _StoneDeserializeValue(
-       std::string& destValue
-     , const Json::Value& jsonValue)
-  {
-    destValue = jsonValue.asString();
-  }
-
-  inline Json::Value _StoneSerializeValue(const std::string& value)
-  {
-    // the following is better than 
-    Json::Value result(value.data(),value.data()+value.size());
-    return result;
-  }
-
-  inline std::string MakeIndent(size_t indent)
-  {
-    char* txt = reinterpret_cast<char*>(malloc(indent+1)); // NO EXCEPTION BELOW!!!!!!!!!!!!
-    for(size_t i = 0; i < indent; ++i)
-      txt[i] = ' ';
-    txt[indent] = 0;
-    std::string retVal(txt);
-    free(txt); // NO EXCEPTION ABOVE !!!!!!!!!!
-    return retVal;
-  }
-
-  // generic dumper
-  template<typename T>
-  std::ostream& StoneDumpValue(std::ostream& out, const T& value, size_t indent)
-  {
-    out << MakeIndent(indent) << value;
-    return out;
-  }
-
-  // string dumper
-  inline std::ostream& StoneDumpValue(std::ostream& out, const std::string& value, size_t indent)
-  {
-    out << MakeIndent(indent) << "\"" << value  << "\"";
-    return out;
-  }
-
-  /** Throws in case of problem */
-  template<typename T>
-  void _StoneDeserializeValue(
-    std::map<std::string, T>& destValue, const Json::Value& jsonValue)
-  {
-    destValue.clear();
-    for (
-      Json::Value::const_iterator itr = jsonValue.begin();
-      itr != jsonValue.end();
-      itr++)
-    {
-      std::string key;
-      _StoneDeserializeValue(key, itr.key());
-
-      T innerDestValue;
-      _StoneDeserializeValue(innerDestValue, *itr);
-
-      destValue[key] = innerDestValue;
-    }
-  }
-
-  template<typename T>
-  Json::Value _StoneSerializeValue(const std::map<std::string,T>& value)
-  {
-    Json::Value result(Json::objectValue);
-
-    for (typename std::map<std::string, T>::const_iterator it = value.cbegin();
-      it != value.cend(); ++it)
-    {
-      // it->first it->second
-      result[it->first] = _StoneSerializeValue(it->second);
-    }
-    return result;
-  }
-
-  template<typename T>
-  std::ostream& StoneDumpValue(std::ostream& out, const std::map<std::string,T>& value, size_t indent)
-  {
-    out << MakeIndent(indent) << "{\n";
-    for (typename std::map<std::string, T>::const_iterator it = value.cbegin();
-      it != value.cend(); ++it)
-    {
-      out << MakeIndent(indent+2) << "\"" << it->first << "\" : ";
-      StoneDumpValue(out, it->second, indent+2);
-    }
-    out << MakeIndent(indent) << "}\n";
-    return out;
-  }
-
-  /** Throws in case of problem */
-  template<typename T>
-  void _StoneDeserializeValue(
-    std::vector<T>& destValue, const Json::Value& jsonValue)
-  {
-    destValue.clear();
-    destValue.reserve(jsonValue.size());
-    for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++)
-    {
-      T innerDestValue;
-      _StoneDeserializeValue(innerDestValue, jsonValue[i]);
-      destValue.push_back(innerDestValue);
-    }
-  }
-
-  template<typename T>
-  Json::Value _StoneSerializeValue(const std::vector<T>& value)
-  {
-    Json::Value result(Json::arrayValue);
-    for (size_t i = 0; i < value.size(); ++i)
-    {
-      result.append(_StoneSerializeValue(value[i]));
-    }
-    return result;
-  }
-
-  template<typename T>
-  std::ostream& StoneDumpValue(std::ostream& out, const std::vector<T>& value, size_t indent)
-  {
-    out << MakeIndent(indent) << "[\n";
-    for (size_t i = 0; i < value.size(); ++i)
-    {
-      StoneDumpValue(out, value[i], indent+2);
-    }
-    out << MakeIndent(indent) << "]\n";
-    return out;
-  }
-
-  inline void StoneCheckSerializedValueTypeGeneric(const Json::Value& value)
-  {
-    if ((!value.isMember("type")) || (!value["type"].isString()))
-    {
-      std::stringstream ss;
-      ss << "Cannot deserialize value ('type' key invalid)";
-      throw std::runtime_error(ss.str());
-    }
-  }
-
-  inline void StoneCheckSerializedValueType(
-    const Json::Value& value, std::string typeStr)
-  {
-    StoneCheckSerializedValueTypeGeneric(value);
-
-    std::string actTypeStr = value["type"].asString();
-    if (actTypeStr != typeStr)
-    {
-      std::stringstream ss;
-      ss << "Cannot deserialize type" << actTypeStr
-        << "into " << typeStr;
-      throw std::runtime_error(ss.str());
-    }
-  }
-
-  // end of generic methods
-
-// end of generic methods
-
-  enum Tool {
-    Tool_LineMeasure,
-    Tool_CircleMeasure,
-    Tool_Crop,
-    Tool_Windowing,
-    Tool_Zoom,
-    Tool_Pan,
-    Tool_Move,
-    Tool_Rotate,
-    Tool_Resize,
-    Tool_Mask,
-  };
-
-  inline std::string ToString(const Tool& value)
-  {
-    if( value == Tool_LineMeasure)
-    {
-      return std::string("LineMeasure");
-    }
-    if( value == Tool_CircleMeasure)
-    {
-      return std::string("CircleMeasure");
-    }
-    if( value == Tool_Crop)
-    {
-      return std::string("Crop");
-    }
-    if( value == Tool_Windowing)
-    {
-      return std::string("Windowing");
-    }
-    if( value == Tool_Zoom)
-    {
-      return std::string("Zoom");
-    }
-    if( value == Tool_Pan)
-    {
-      return std::string("Pan");
-    }
-    if( value == Tool_Move)
-    {
-      return std::string("Move");
-    }
-    if( value == Tool_Rotate)
-    {
-      return std::string("Rotate");
-    }
-    if( value == Tool_Resize)
-    {
-      return std::string("Resize");
-    }
-    if( value == Tool_Mask)
-    {
-      return std::string("Mask");
-    }
-    std::stringstream ss;
-    ss << "Value \"" << value << "\" cannot be converted to Tool. Possible values are: "
-        << " LineMeasure = " << static_cast<int64_t>(Tool_LineMeasure)  << ", " 
-        << " CircleMeasure = " << static_cast<int64_t>(Tool_CircleMeasure)  << ", " 
-        << " Crop = " << static_cast<int64_t>(Tool_Crop)  << ", " 
-        << " Windowing = " << static_cast<int64_t>(Tool_Windowing)  << ", " 
-        << " Zoom = " << static_cast<int64_t>(Tool_Zoom)  << ", " 
-        << " Pan = " << static_cast<int64_t>(Tool_Pan)  << ", " 
-        << " Move = " << static_cast<int64_t>(Tool_Move)  << ", " 
-        << " Rotate = " << static_cast<int64_t>(Tool_Rotate)  << ", " 
-        << " Resize = " << static_cast<int64_t>(Tool_Resize)  << ", " 
-        << " Mask = " << static_cast<int64_t>(Tool_Mask)  << ", " 
-        << std::endl;
-    std::string msg = ss.str();
-    throw std::runtime_error(msg);
-  }
-
-  inline void FromString(Tool& value, std::string strValue)
-  {
-    if( strValue == std::string("LineMeasure") )
-    {
-      value = Tool_LineMeasure;
-      return;
-    }
-    if( strValue == std::string("CircleMeasure") )
-    {
-      value = Tool_CircleMeasure;
-      return;
-    }
-    if( strValue == std::string("Crop") )
-    {
-      value = Tool_Crop;
-      return;
-    }
-    if( strValue == std::string("Windowing") )
-    {
-      value = Tool_Windowing;
-      return;
-    }
-    if( strValue == std::string("Zoom") )
-    {
-      value = Tool_Zoom;
-      return;
-    }
-    if( strValue == std::string("Pan") )
-    {
-      value = Tool_Pan;
-      return;
-    }
-    if( strValue == std::string("Move") )
-    {
-      value = Tool_Move;
-      return;
-    }
-    if( strValue == std::string("Rotate") )
-    {
-      value = Tool_Rotate;
-      return;
-    }
-    if( strValue == std::string("Resize") )
-    {
-      value = Tool_Resize;
-      return;
-    }
-    if( strValue == std::string("Mask") )
-    {
-      value = Tool_Mask;
-      return;
-    }
-
-    std::stringstream ss;
-    ss << "String \"" << strValue << "\" cannot be converted to Tool. Possible values are: LineMeasure CircleMeasure Crop Windowing Zoom Pan Move Rotate Resize Mask ";
-    std::string msg = ss.str();
-    throw std::runtime_error(msg);
-  }
-
-
-  inline void _StoneDeserializeValue(
-    Tool& destValue, const Json::Value& jsonValue)
-  {
-    FromString(destValue, jsonValue.asString());
-  }
-
-  inline Json::Value _StoneSerializeValue(const Tool& value)
-  {
-    std::string strValue = ToString(value);
-    return Json::Value(strValue);
-  }
-
-  inline std::ostream& StoneDumpValue(std::ostream& out, const Tool& value, size_t indent = 0)
-  {
-    if( value == Tool_LineMeasure)
-    {
-      out << MakeIndent(indent) << "LineMeasure" << std::endl;
-    }
-    if( value == Tool_CircleMeasure)
-    {
-      out << MakeIndent(indent) << "CircleMeasure" << std::endl;
-    }
-    if( value == Tool_Crop)
-    {
-      out << MakeIndent(indent) << "Crop" << std::endl;
-    }
-    if( value == Tool_Windowing)
-    {
-      out << MakeIndent(indent) << "Windowing" << std::endl;
-    }
-    if( value == Tool_Zoom)
-    {
-      out << MakeIndent(indent) << "Zoom" << std::endl;
-    }
-    if( value == Tool_Pan)
-    {
-      out << MakeIndent(indent) << "Pan" << std::endl;
-    }
-    if( value == Tool_Move)
-    {
-      out << MakeIndent(indent) << "Move" << std::endl;
-    }
-    if( value == Tool_Rotate)
-    {
-      out << MakeIndent(indent) << "Rotate" << std::endl;
-    }
-    if( value == Tool_Resize)
-    {
-      out << MakeIndent(indent) << "Resize" << std::endl;
-    }
-    if( value == Tool_Mask)
-    {
-      out << MakeIndent(indent) << "Mask" << std::endl;
-    }
-    return out;
-  }
-
-
-  enum ActionType {
-    ActionType_UndoCrop,
-    ActionType_Rotate,
-    ActionType_Invert,
-  };
-
-  inline std::string ToString(const ActionType& value)
-  {
-    if( value == ActionType_UndoCrop)
-    {
-      return std::string("UndoCrop");
-    }
-    if( value == ActionType_Rotate)
-    {
-      return std::string("Rotate");
-    }
-    if( value == ActionType_Invert)
-    {
-      return std::string("Invert");
-    }
-    std::stringstream ss;
-    ss << "Value \"" << value << "\" cannot be converted to ActionType. Possible values are: "
-        << " UndoCrop = " << static_cast<int64_t>(ActionType_UndoCrop)  << ", " 
-        << " Rotate = " << static_cast<int64_t>(ActionType_Rotate)  << ", " 
-        << " Invert = " << static_cast<int64_t>(ActionType_Invert)  << ", " 
-        << std::endl;
-    std::string msg = ss.str();
-    throw std::runtime_error(msg);
-  }
-
-  inline void FromString(ActionType& value, std::string strValue)
-  {
-    if( strValue == std::string("UndoCrop") )
-    {
-      value = ActionType_UndoCrop;
-      return;
-    }
-    if( strValue == std::string("Rotate") )
-    {
-      value = ActionType_Rotate;
-      return;
-    }
-    if( strValue == std::string("Invert") )
-    {
-      value = ActionType_Invert;
-      return;
-    }
-
-    std::stringstream ss;
-    ss << "String \"" << strValue << "\" cannot be converted to ActionType. Possible values are: UndoCrop Rotate Invert ";
-    std::string msg = ss.str();
-    throw std::runtime_error(msg);
-  }
-
-
-  inline void _StoneDeserializeValue(
-    ActionType& destValue, const Json::Value& jsonValue)
-  {
-    FromString(destValue, jsonValue.asString());
-  }
-
-  inline Json::Value _StoneSerializeValue(const ActionType& value)
-  {
-    std::string strValue = ToString(value);
-    return Json::Value(strValue);
-  }
-
-  inline std::ostream& StoneDumpValue(std::ostream& out, const ActionType& value, size_t indent = 0)
-  {
-    if( value == ActionType_UndoCrop)
-    {
-      out << MakeIndent(indent) << "UndoCrop" << std::endl;
-    }
-    if( value == ActionType_Rotate)
-    {
-      out << MakeIndent(indent) << "Rotate" << std::endl;
-    }
-    if( value == ActionType_Invert)
-    {
-      out << MakeIndent(indent) << "Invert" << std::endl;
-    }
-    return out;
-  }
-
-
-
-#ifdef _MSC_VER
-#pragma region SelectTool
-#endif //_MSC_VER
-
-  struct SelectTool
-  {
-    Tool tool;
-
-    SelectTool(Tool tool = Tool())
-    {
-      this->tool = tool;
-    }
-  };
-
-  inline void _StoneDeserializeValue(SelectTool& destValue, const Json::Value& value)
-  {
-    _StoneDeserializeValue(destValue.tool, value["tool"]);
-    }
-
-  inline Json::Value _StoneSerializeValue(const SelectTool& value)
-  {
-    Json::Value result(Json::objectValue);
-    result["tool"] = _StoneSerializeValue(value.tool);
-
-    return result;
-  }
-
-  inline std::ostream& StoneDumpValue(std::ostream& out, const SelectTool& value, size_t indent = 0)
-  {
-    out << MakeIndent(indent) << "{\n";
-    out << MakeIndent(indent) << "tool:\n";
-    StoneDumpValue(out, value.tool,indent+2);
-    out << "\n";
-
-    out << MakeIndent(indent) << "}\n";
-    return out;
-  }
-
-  inline void StoneDeserialize(SelectTool& destValue, const Json::Value& value)
-  {
-    StoneCheckSerializedValueType(value, "StoneSampleCommands.SelectTool");
-    _StoneDeserializeValue(destValue, value["value"]);
-  }
-
-  inline Json::Value StoneSerializeToJson(const SelectTool& value)
-  {
-    Json::Value result(Json::objectValue);
-    result["type"] = "StoneSampleCommands.SelectTool";
-    result["value"] = _StoneSerializeValue(value);
-    return result;
-  }
-
-  inline std::string StoneSerialize(const SelectTool& value)
-  {
-    Json::Value resultJson = StoneSerializeToJson(value);
-    std::string resultStr = resultJson.toStyledString();
-    return resultStr;
-  }
-
-#ifdef _MSC_VER
-#pragma endregion SelectTool
-#endif //_MSC_VER
-
-#ifdef _MSC_VER
-#pragma region Action
-#endif //_MSC_VER
-
-  struct Action
-  {
-    ActionType type;
-
-    Action(ActionType type = ActionType())
-    {
-      this->type = type;
-    }
-  };
-
-  inline void _StoneDeserializeValue(Action& destValue, const Json::Value& value)
-  {
-    _StoneDeserializeValue(destValue.type, value["type"]);
-    }
-
-  inline Json::Value _StoneSerializeValue(const Action& value)
-  {
-    Json::Value result(Json::objectValue);
-    result["type"] = _StoneSerializeValue(value.type);
-
-    return result;
-  }
-
-  inline std::ostream& StoneDumpValue(std::ostream& out, const Action& value, size_t indent = 0)
-  {
-    out << MakeIndent(indent) << "{\n";
-    out << MakeIndent(indent) << "type:\n";
-    StoneDumpValue(out, value.type,indent+2);
-    out << "\n";
-
-    out << MakeIndent(indent) << "}\n";
-    return out;
-  }
-
-  inline void StoneDeserialize(Action& destValue, const Json::Value& value)
-  {
-    StoneCheckSerializedValueType(value, "StoneSampleCommands.Action");
-    _StoneDeserializeValue(destValue, value["value"]);
-  }
-
-  inline Json::Value StoneSerializeToJson(const Action& value)
-  {
-    Json::Value result(Json::objectValue);
-    result["type"] = "StoneSampleCommands.Action";
-    result["value"] = _StoneSerializeValue(value);
-    return result;
-  }
-
-  inline std::string StoneSerialize(const Action& value)
-  {
-    Json::Value resultJson = StoneSerializeToJson(value);
-    std::string resultStr = resultJson.toStyledString();
-    return resultStr;
-  }
-
-#ifdef _MSC_VER
-#pragma endregion Action
-#endif //_MSC_VER
-
-#ifdef _MSC_VER
-#pragma region Dispatching code
-#endif //_MSC_VER
-
-  class IHandler
-  {
-  public:
-    virtual bool Handle(const SelectTool& value) = 0;
-    virtual bool Handle(const Action& value) = 0;
-  };
-
-  /** Service function for StoneDispatchToHandler */
-  inline bool StoneDispatchJsonToHandler(
-    const Json::Value& jsonValue, IHandler* handler)
-  {
-    StoneCheckSerializedValueTypeGeneric(jsonValue);
-    std::string type = jsonValue["type"].asString();
-    if (type == "")
-    {
-      // this should never ever happen
-      throw std::runtime_error("Caught empty type while dispatching");
-    }
-    else if (type == "StoneSampleCommands.SelectTool")
-    {
-      SelectTool value;
-      _StoneDeserializeValue(value, jsonValue["value"]);
-      return handler->Handle(value);
-    }
-    else if (type == "StoneSampleCommands.Action")
-    {
-      Action value;
-      _StoneDeserializeValue(value, jsonValue["value"]);
-      return handler->Handle(value);
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-  /** Takes a serialized type and passes this to the handler */
-  inline bool StoneDispatchToHandler(std::string strValue, IHandler* handler)
-  {
-    Json::Value readValue;
-
-    Json::CharReaderBuilder builder;
-    Json::CharReader* reader = builder.newCharReader();
-
-    StoneSmartPtr<Json::CharReader> ptr(reader);
-
-    std::string errors;
-
-    bool ok = reader->parse(
-      strValue.c_str(),
-      strValue.c_str() + strValue.size(),
-      &readValue,
-      &errors
-    );
-    if (!ok)
-    {
-      std::stringstream ss;
-      ss << "Jsoncpp parsing error: " << errors;
-      throw std::runtime_error(ss.str());
-    }
-    return StoneDispatchJsonToHandler(readValue, handler);
-  }
-
-#ifdef _MSC_VER
-#pragma endregion Dispatching code
-#endif //_MSC_VER
-}
\ No newline at end of file
--- a/Applications/Samples/Deprecated/StoneSampleCommands_generated.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,333 +0,0 @@
-/*
-         1         2         3         4         5         6         7
-12345678901234567890123456789012345678901234567890123456789012345678901234567890
-
-Generated on 2019-03-18 12:07:42.696093 by stonegentool
-
-*/
-
-function StoneCheckSerializedValueType(value: any, typeStr: string)
-{
-  StoneCheckSerializedValueTypeGeneric(value);
-
-  if (value['type'] != typeStr)
-  {
-    throw new Error(
-      `Cannot deserialize type ${value['type']} into ${typeStr}`);
-  }
-}
-
-function isString(val: any) :boolean
-{
-  return ((typeof val === 'string') || (val instanceof String));
-}
-
-function StoneCheckSerializedValueTypeGeneric(value: any)
-{
-  // console.//log("+-------------------------------------------------+");
-  // console.//log("|            StoneCheckSerializedValueTypeGeneric |");
-  // console.//log("+-------------------------------------------------+");
-  // console.//log("value = ");
-  // console.//log(value);
-  if ( (!('type' in value)) || (!isString(value.type)) )
-  {
-    throw new Error(
-      "Cannot deserialize value ('type' key invalid)");
-  }
-}
-
-// end of generic methods
-
-export enum Tool {
-  LineMeasure = "LineMeasure",
-  CircleMeasure = "CircleMeasure",
-  Crop = "Crop",
-  Windowing = "Windowing",
-  Zoom = "Zoom",
-  Pan = "Pan",
-  Move = "Move",
-  Rotate = "Rotate",
-  Resize = "Resize",
-  Mask = "Mask"
-};
-
-export function Tool_FromString(strValue:string) : Tool
-{
-  if( strValue == "LineMeasure" )
-  {
-    return Tool.LineMeasure;
-  }
-  if( strValue == "CircleMeasure" )
-  {
-    return Tool.CircleMeasure;
-  }
-  if( strValue == "Crop" )
-  {
-    return Tool.Crop;
-  }
-  if( strValue == "Windowing" )
-  {
-    return Tool.Windowing;
-  }
-  if( strValue == "Zoom" )
-  {
-    return Tool.Zoom;
-  }
-  if( strValue == "Pan" )
-  {
-    return Tool.Pan;
-  }
-  if( strValue == "Move" )
-  {
-    return Tool.Move;
-  }
-  if( strValue == "Rotate" )
-  {
-    return Tool.Rotate;
-  }
-  if( strValue == "Resize" )
-  {
-    return Tool.Resize;
-  }
-  if( strValue == "Mask" )
-  {
-    return Tool.Mask;
-  }
-
-  let msg : string =  `String ${strValue} cannot be converted to Tool. Possible values are: LineMeasure, CircleMeasure, Crop, Windowing, Zoom, Pan, Move, Rotate, Resize, Mask`;
-  throw new Error(msg);
-}
-
-export function Tool_ToString(value:Tool) : string
-{
-  if( value == Tool.LineMeasure )
-  {
-    return "LineMeasure";
-  }
-  if( value == Tool.CircleMeasure )
-  {
-    return "CircleMeasure";
-  }
-  if( value == Tool.Crop )
-  {
-    return "Crop";
-  }
-  if( value == Tool.Windowing )
-  {
-    return "Windowing";
-  }
-  if( value == Tool.Zoom )
-  {
-    return "Zoom";
-  }
-  if( value == Tool.Pan )
-  {
-    return "Pan";
-  }
-  if( value == Tool.Move )
-  {
-    return "Move";
-  }
-  if( value == Tool.Rotate )
-  {
-    return "Rotate";
-  }
-  if( value == Tool.Resize )
-  {
-    return "Resize";
-  }
-  if( value == Tool.Mask )
-  {
-    return "Mask";
-  }
-
-  let msg : string = `Value ${value} cannot be converted to Tool. Possible values are: `;
-  {
-    let _LineMeasure_enumValue : string = Tool.LineMeasure; // enums are strings in stonecodegen, so this will work.
-    let msg_LineMeasure : string = `LineMeasure (${_LineMeasure_enumValue}), `;
-    msg = msg + msg_LineMeasure;
-  }
-  {
-    let _CircleMeasure_enumValue : string = Tool.CircleMeasure; // enums are strings in stonecodegen, so this will work.
-    let msg_CircleMeasure : string = `CircleMeasure (${_CircleMeasure_enumValue}), `;
-    msg = msg + msg_CircleMeasure;
-  }
-  {
-    let _Crop_enumValue : string = Tool.Crop; // enums are strings in stonecodegen, so this will work.
-    let msg_Crop : string = `Crop (${_Crop_enumValue}), `;
-    msg = msg + msg_Crop;
-  }
-  {
-    let _Windowing_enumValue : string = Tool.Windowing; // enums are strings in stonecodegen, so this will work.
-    let msg_Windowing : string = `Windowing (${_Windowing_enumValue}), `;
-    msg = msg + msg_Windowing;
-  }
-  {
-    let _Zoom_enumValue : string = Tool.Zoom; // enums are strings in stonecodegen, so this will work.
-    let msg_Zoom : string = `Zoom (${_Zoom_enumValue}), `;
-    msg = msg + msg_Zoom;
-  }
-  {
-    let _Pan_enumValue : string = Tool.Pan; // enums are strings in stonecodegen, so this will work.
-    let msg_Pan : string = `Pan (${_Pan_enumValue}), `;
-    msg = msg + msg_Pan;
-  }
-  {
-    let _Move_enumValue : string = Tool.Move; // enums are strings in stonecodegen, so this will work.
-    let msg_Move : string = `Move (${_Move_enumValue}), `;
-    msg = msg + msg_Move;
-  }
-  {
-    let _Rotate_enumValue : string = Tool.Rotate; // enums are strings in stonecodegen, so this will work.
-    let msg_Rotate : string = `Rotate (${_Rotate_enumValue}), `;
-    msg = msg + msg_Rotate;
-  }
-  {
-    let _Resize_enumValue : string = Tool.Resize; // enums are strings in stonecodegen, so this will work.
-    let msg_Resize : string = `Resize (${_Resize_enumValue}), `;
-    msg = msg + msg_Resize;
-  }
-  {
-    let _Mask_enumValue : string = Tool.Mask; // enums are strings in stonecodegen, so this will work.
-    let msg_Mask : string = `Mask (${_Mask_enumValue})`;
-    msg = msg + msg_Mask;
-  }
-  throw new Error(msg);
-}
-
-export enum ActionType {
-  UndoCrop = "UndoCrop",
-  Rotate = "Rotate",
-  Invert = "Invert"
-};
-
-export function ActionType_FromString(strValue:string) : ActionType
-{
-  if( strValue == "UndoCrop" )
-  {
-    return ActionType.UndoCrop;
-  }
-  if( strValue == "Rotate" )
-  {
-    return ActionType.Rotate;
-  }
-  if( strValue == "Invert" )
-  {
-    return ActionType.Invert;
-  }
-
-  let msg : string =  `String ${strValue} cannot be converted to ActionType. Possible values are: UndoCrop, Rotate, Invert`;
-  throw new Error(msg);
-}
-
-export function ActionType_ToString(value:ActionType) : string
-{
-  if( value == ActionType.UndoCrop )
-  {
-    return "UndoCrop";
-  }
-  if( value == ActionType.Rotate )
-  {
-    return "Rotate";
-  }
-  if( value == ActionType.Invert )
-  {
-    return "Invert";
-  }
-
-  let msg : string = `Value ${value} cannot be converted to ActionType. Possible values are: `;
-  {
-    let _UndoCrop_enumValue : string = ActionType.UndoCrop; // enums are strings in stonecodegen, so this will work.
-    let msg_UndoCrop : string = `UndoCrop (${_UndoCrop_enumValue}), `;
-    msg = msg + msg_UndoCrop;
-  }
-  {
-    let _Rotate_enumValue : string = ActionType.Rotate; // enums are strings in stonecodegen, so this will work.
-    let msg_Rotate : string = `Rotate (${_Rotate_enumValue}), `;
-    msg = msg + msg_Rotate;
-  }
-  {
-    let _Invert_enumValue : string = ActionType.Invert; // enums are strings in stonecodegen, so this will work.
-    let msg_Invert : string = `Invert (${_Invert_enumValue})`;
-    msg = msg + msg_Invert;
-  }
-  throw new Error(msg);
-}
-
-
-
-export class SelectTool {
-  tool:Tool;
-
-  constructor() {
-  }
-
-  public StoneSerialize(): string {
-    let container: object = {};
-    container['type'] = 'StoneSampleCommands.SelectTool';
-    container['value'] = this;
-    return JSON.stringify(container);
-  }
-
-  public static StoneDeserialize(valueStr: string) : SelectTool
-  {
-    let value: any = JSON.parse(valueStr);
-    StoneCheckSerializedValueType(value, 'StoneSampleCommands.SelectTool');
-    let result: SelectTool = value['value'] as SelectTool;
-    return result;
-  }
-}
-export class Action {
-  type:ActionType;
-
-  constructor() {
-  }
-
-  public StoneSerialize(): string {
-    let container: object = {};
-    container['type'] = 'StoneSampleCommands.Action';
-    container['value'] = this;
-    return JSON.stringify(container);
-  }
-
-  public static StoneDeserialize(valueStr: string) : Action
-  {
-    let value: any = JSON.parse(valueStr);
-    StoneCheckSerializedValueType(value, 'StoneSampleCommands.Action');
-    let result: Action = value['value'] as Action;
-    return result;
-  }
-}
-
-export interface IHandler {
-};
-
-/** Service function for StoneDispatchToHandler */
-export function StoneDispatchJsonToHandler(
-  jsonValue: any, handler: IHandler): boolean
-{
-  StoneCheckSerializedValueTypeGeneric(jsonValue);
-  let type: string = jsonValue["type"];
-  if (type == "")
-  {
-    // this should never ever happen
-    throw new Error("Caught empty type while dispatching");
-  }
-  else
-  {
-    return false;
-  }
-}
-
-/** Takes a serialized type and passes this to the handler */
-export function StoneDispatchToHandler(
-  strValue: string, handler: IHandler): boolean
-{
-  // console.//log("+------------------------------------------------+");
-  // console.//log("|            StoneDispatchToHandler              |");
-  // console.//log("+------------------------------------------------+");
-  // console.//log("strValue = ");
-  // console.//log(strValue);
-  let jsonValue: any = JSON.parse(strValue)
-  return StoneDispatchJsonToHandler(jsonValue, handler);
-}
\ No newline at end of file
--- a/Applications/Samples/Deprecated/SynchronizedSeriesApplication.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-/**
- * 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 "SampleInteractor.h"
-
-#include "../../../Framework/Toolbox/OrthancSeriesLoader.h"
-#include "../../../Framework/Layers/SeriesFrameRendererFactory.h"
-#include "../../../Framework/Layers/ReferenceLineFactory.h"
-#include "../../../Framework/Widgets/LayoutWidget.h"
-
-#include <Core/Logging.h>
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class SynchronizedSeriesApplication : public SampleApplicationBase
-    {
-    private:   
-      LayeredSceneWidget* CreateSeriesWidget(BasicApplicationContext& context,
-                                             const std::string& series)
-      {
-        std::unique_ptr<ISeriesLoader> loader
-          (new OrthancSeriesLoader(context.GetWebService().GetConnection(), series));
-
-        std::unique_ptr<SampleInteractor> interactor(new SampleInteractor(*loader, false));
-
-        std::unique_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget);
-        widget->AddLayer(new SeriesFrameRendererFactory(loader.release(), false));
-        widget->SetSlice(interactor->GetCursor().GetCurrentSlice());
-        widget->SetInteractor(*interactor);
-
-        context.AddInteractor(interactor.release());
-
-        return widget.release();
-      }
-
-    public:
-      virtual void DeclareCommandLineOptions(boost::program_options::options_description& options)
-      {
-        boost::program_options::options_description generic("Sample options");
-        generic.add_options()
-          ("a", boost::program_options::value<std::string>(), 
-           "Orthanc ID of the 1st series")
-          ("b", boost::program_options::value<std::string>(), 
-           "Orthanc ID of the 2nd series")
-          ("c", boost::program_options::value<std::string>(), 
-           "Orthanc ID of the 3rd series")
-          ;
-
-        options.add(generic);    
-      }
-
-      virtual void Initialize(BasicApplicationContext& context,
-                              IStatusBar& statusBar,
-                              const boost::program_options::variables_map& parameters)
-      {
-        if (parameters.count("a") != 1 ||
-            parameters.count("b") != 1 ||
-            parameters.count("c") != 1)
-        {
-          LOG(ERROR) << "At least one of the three series IDs is missing";
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-        }
-
-        std::unique_ptr<LayeredSceneWidget> a(CreateSeriesWidget(context, parameters["a"].as<std::string>()));
-        std::unique_ptr<LayeredSceneWidget> b(CreateSeriesWidget(context, parameters["b"].as<std::string>()));
-        std::unique_ptr<LayeredSceneWidget> c(CreateSeriesWidget(context, parameters["c"].as<std::string>()));
-
-        ReferenceLineFactory::Configure(*a, *b);
-        ReferenceLineFactory::Configure(*a, *c);
-        ReferenceLineFactory::Configure(*b, *c);
-
-        std::unique_ptr<LayoutWidget> layout(new LayoutWidget);
-        layout->SetPadding(5);
-        layout->AddWidget(a.release());
-
-        std::unique_ptr<LayoutWidget> layoutB(new LayoutWidget);
-        layoutB->SetVertical();
-        layoutB->SetPadding(5);
-        layoutB->AddWidget(b.release());
-        layoutB->AddWidget(c.release());
-        layout->AddWidget(layoutB.release());
-
-        context.SetCentralWidget(layout.release());        
-      }
-    };
-  }
-}
--- a/Applications/Samples/Deprecated/TestPatternApplication.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/**
- * 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 "SampleApplicationBase.h"
-
-#include "../../../Framework/Widgets/TestCairoWidget.h"
-#include "../../../Framework/Widgets/TestWorldSceneWidget.h"
-#include "../../../Framework/Widgets/LayoutWidget.h"
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class TestPatternApplication : public SampleApplicationBase
-    {
-    public:
-      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
-      {
-        boost::program_options::options_description generic("Sample options");
-        generic.add_options()
-          ("animate", boost::program_options::value<bool>()->default_value(true), "Animate the test pattern")
-          ;
-
-        options.add(generic);    
-      }
-
-      virtual void Initialize(IStatusBar& statusBar,
-                              const boost::program_options::variables_map& parameters)
-      {
-        using namespace OrthancStone;
-
-        std::unique_ptr<LayoutWidget> layout(new LayoutWidget);
-        layout->SetPadding(10);
-        layout->SetBackgroundCleared(true);
-        layout->AddWidget(new TestCairoWidget(parameters["animate"].as<bool>()));
-        layout->AddWidget(new TestWorldSceneWidget(parameters["animate"].as<bool>()));
-
-        context_->SetCentralWidget(layout.release());
-        context_->SetUpdateDelay(25);  // If animation, update the content each 25ms
-      }
-    };
-  }
-}
--- a/Applications/Samples/Deprecated/Web/index.html	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-<!doctype html>
-
-<html lang="us">
-  <head>
-    <meta charset="utf-8" />
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
-    <!-- Disable pinch zoom on mobile devices -->
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
-    <meta name="HandheldFriendly" content="true" />
-
-    <title>Wasm Samples</title>
-
-<body>
-    <ul>
-      <li><a href="simple-viewer/simple-viewer.html">Simple Viewer Project (you may add ?studyId=XXX in the url)</a></li>
-      <li><a href="single-frame.html?instance=XXX">Single frame application (you must replace XXX by a valid instance id in the url)</a></li>
-      <li><a href="single-frame-editor.html?instance=XXX">Single frame editor application (you must replace XXX by a valid instance id in the url)</a></li>
-      <li><a href="simple-viewer-single-file.html">Simple Viewer Single file (to be replaced by other samples)</a></li>
-    </ul>
-</body>
-
-</html>
\ No newline at end of file
--- a/Applications/Samples/Deprecated/Web/samples-styles.css	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-html, body {
-  width: 100%;
-  height: 100%;
-  margin: 0px;
-  border: 0;
-  overflow: hidden; /*  Disable scrollbars */
-  display: block;  /* No floating content on sides */
-  background-color: black;
-  color: white;
-  font-family: Arial, Helvetica, sans-serif;
-}
-
-canvas {
-  left:0px;
-  top:0px;
-}
\ No newline at end of file
--- a/Applications/Samples/Deprecated/Web/simple-viewer-single-file.html	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-<!doctype html>
-
-<html lang="us">
-
-<head>
-  <meta charset="utf-8" />
-  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
-  <!-- Disable pinch zoom on mobile devices -->
-  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
-  <meta name="HandheldFriendly" content="true" />
-
-  <title>Simple Viewer</title>
-  <link href="samples-styles.css" rel="stylesheet" />
-
-<body>
-  <div id="breadcrumb">
-    <span id="patient-id"></span>
-    <span id="study-description"></span>
-    <span id="series-description"></span>
-  </div>
-  <div style="height: calc(100% - 50px)">
-    <div style="width: 20%; height: 100%; display: inline-block">
-      <canvas id="canvas"></canvas>
-    </div>
-    <div style="width: 70%; height: 100%; display: inline-block">
-      <canvas id="canvas2"></canvas>
-    </div>
-  </div>
-  <div id="toolbox" style="height: 50px">
-    <input tool-selector="line-measure" type="radio" name="radio-tool-selector" class="tool-selector">line
-    <input tool-selector="circle-measure" type="radio" name="radio-tool-selector" class="tool-selector">circle
-    <button action-trigger="action1" class="action-trigger">action1</button>
-    <button action-trigger="action2" class="action-trigger">action2</button>
-  </div>
-  <script type="text/javascript" src="app-simple-viewer-single-file.js"></script>
-</body>
-
-</html>
\ No newline at end of file
--- a/Applications/Samples/Deprecated/Web/simple-viewer-single-file.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-import wasmApplicationRunner = require('../../../Platforms/Wasm/wasm-application-runner');
-
-wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSimpleViewerSingleFile", "/orthanc");
-
-function SelectTool(toolName: string) {
-    var command = {
-        command: "selectTool",
-        args: {
-            toolName: toolName
-        }
-    };
-    wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command));
-
-}
-
-function PerformAction(commandName: string) {
-    var command = {
-        command: commandName,
-        commandType: "simple",
-        args: {}
-    };
-    wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command));
-}
-
-//initializes the buttons
-//-----------------------
-// install "SelectTool" handlers
-document.querySelectorAll("[tool-selector]").forEach((e) => {
-    console.log(e);
-    (e as HTMLInputElement).addEventListener("click", () => {
-        console.log(e);
-        SelectTool(e.attributes["tool-selector"].value);
-    });
-});
-
-// install "PerformAction" handlers
-document.querySelectorAll("[action-trigger]").forEach((e) => {
-    (e as HTMLInputElement).addEventListener("click", () => {
-        PerformAction(e.attributes["action-trigger"].value);
-    });
-});
-
-// this method is called "from the C++ code" when the StoneApplication is updated.
-// it can be used to update the UI of the application
-function UpdateWebApplicationWithString(statusUpdateMessage: string) {
-  console.log(statusUpdateMessage);
-  
-  if (statusUpdateMessage.startsWith("series-description=")) {
-      document.getElementById("series-description").innerText = statusUpdateMessage.split("=")[1];
-  }
-}
-
-function UpdateWebApplicationWithSerializedMessage(statusUpdateMessageString: string) {
-  console.log("updating web application with serialized message: ", statusUpdateMessageString);
-  console.log("<not supported in the simple viewer (single file)!>");
-}
-
-// make it available to other js scripts in the application
-(<any> window).UpdateWebApplicationWithString = UpdateWebApplicationWithString;
-
-(<any> window).UpdateWebApplicationWithSerializedMessage = UpdateWebApplicationWithSerializedMessage;
--- a/Applications/Samples/Deprecated/Web/simple-viewer-single-file.tsconfig.json	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-{
-    "extends" : "./tsconfig-samples",
-    "compilerOptions": {
-        // "outFile": "../build-web/app-simple-viewer-single-file.js"
-    },
-    "include" : [
-        "simple-viewer-single-file.ts"
-    ]
-}
\ No newline at end of file
--- a/Applications/Samples/Deprecated/Web/single-frame-editor.html	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-<!doctype html>
-
-<html lang="us">
-  <head>
-    <meta charset="utf-8" />
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
-    <!-- Disable pinch zoom on mobile devices -->
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
-    <meta name="HandheldFriendly" content="true" />
-
-    <title>Simple Viewer</title>
-    <link href="samples-styles.css" rel="stylesheet" />
-
-<body>
-  <div style="width: 100%; height: 100%">
-    <canvas id="canvas"></canvas>
-  </div>
-  <script type="text/javascript" src="app-single-frame-editor.js"></script>
-</body>
-
-</html>
\ No newline at end of file
--- a/Applications/Samples/Deprecated/Web/single-frame-editor.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-import wasmApplicationRunner = require('../../../Platforms/Wasm/wasm-application-runner');
-
-wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSingleFrameEditor", "/orthanc");
--- a/Applications/Samples/Deprecated/Web/single-frame-editor.tsconfig.json	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-{
-    "extends" : "./tsconfig-samples",
-    "compilerOptions": {
-    },
-    "include" : [
-        "single-frame-editor.ts"
-    ]
-}
--- a/Applications/Samples/Deprecated/Web/single-frame.html	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-<!doctype html>
-
-<html lang="us">
-  <head>
-    <meta charset="utf-8" />
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
-    <!-- Disable pinch zoom on mobile devices -->
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
-    <meta name="HandheldFriendly" content="true" />
-
-    <title>Simple Viewer</title>
-    <link href="samples-styles.css" rel="stylesheet" />
-
-<body>
-  <div style="width: 100%; height: 100%">
-    <canvas id="canvas"></canvas>
-  </div>
-  <script type="text/javascript" src="app-single-frame.js"></script>
-</body>
-
-</html>
\ No newline at end of file
--- a/Applications/Samples/Deprecated/Web/single-frame.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-import wasmApplicationRunner = require('../../../Platforms/Wasm/wasm-application-runner');
-
-wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSingleFrame", "/orthanc");
-
--- a/Applications/Samples/Deprecated/Web/single-frame.tsconfig.json	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-{
-    "extends" : "./tsconfig-samples",
-    "compilerOptions": {
-    },
-    "include" : [
-        "single-frame.ts"
-    ]
-}
--- a/Applications/Samples/Deprecated/Web/tsconfig-samples.json	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-{
-    "extends" : "../../../Platforms/Wasm/tsconfig-stone",
-    "compilerOptions": {
-        "sourceMap": false,
-        "lib" : [
-            "es2017",
-            "dom",
-            "dom.iterable"
-        ]
-    }
-}
--- a/Applications/Samples/Deprecated/build-wasm.sh	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# usage:
-# to build all targets in Debug:
-# ./build-wasm.sh
-#
-# to build a single target in release:
-# ./build-wasm.sh OrthancStoneSingleFrameEditor Release
-
-set -e
-
-target=${1:-all}
-buildType=${2:-Debug}
-
-currentDir=$(pwd)
-samplesRootDir=$(pwd)
-
-mkdir -p $samplesRootDir/build-wasm
-cd $samplesRootDir/build-wasm
-
-source ~/apps/emsdk/emsdk_env.sh
-cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=~/apps/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=$buildType -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc -DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON
-ninja $target
-
-echo "-- building the web application -- "
-cd $currentDir
-./build-web.sh
\ No newline at end of file
--- a/Applications/Samples/Deprecated/build-wasm.sh.old	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-#!/bin/bash
-#
-# usage:
-# to build all targets:
-# ./build-wasm.sh
-#
-# to build a single target:
-# ./build-wasm.sh OrthancStoneSingleFrameEditor
-
-set -e
-
-target=${1:-all}
-
-currentDir=$(pwd)
-samplesRootDir=$(pwd)
-
-mkdir -p $samplesRootDir/build-wasm
-cd $samplesRootDir/build-wasm
-
-source ~/apps/emsdk/emsdk_env.sh
-cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake \
-  -DCMAKE_BUILD_TYPE=Release \
-  -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone \
-  -DORTHANC_FRAMEWORK_SOURCE=path \
-  -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc \
-  -DALLOW_DOWNLOADS=ON .. \
-  -DENABLE_WASM=ON
-
-ninja $target
-
-echo "-- building the web application -- "
-cd $currentDir
-./build-web.sh
--- a/Applications/Samples/Deprecated/build-web-ext.sh	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-#!/bin/bash
-
-set -e
-
-target=${1:-all}
-# this script currently assumes that the wasm code has been built on its side and is availabie in build-wasm/
-
-currentDir=$(pwd)
-
-scriptDirRel=$(dirname $0)
-#echo $scriptDirRel
-scriptDirAbs=$(realpath $scriptDirRel)
-echo $scriptDirAbs
-
-samplesRootDir=scriptDirAbs
-
-outputDir=$samplesRootDir/build-web/
-mkdir -p $outputDir
-
-# files used by all single files samples
-cp $samplesRootDir/Web/index.html $outputDir
-cp $samplesRootDir/Web/samples-styles.css $outputDir
-
-# build simple-viewer-single-file (obsolete project)
-if [[ $target == "all" || $target == "OrthancStoneSimpleViewerSingleFile" ]]; then
-  cp $samplesRootDir/Web/simple-viewer-single-file.html $outputDir
-  tsc --allowJs --project $samplesRootDir/Web/simple-viewer-single-file.tsconfig.json
-  cp $currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.js  $outputDir
-  cp $currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.wasm  $outputDir
-fi
-
-# build single-frame
-if [[ $target == "all" || $target == "OrthancStoneSingleFrame" ]]; then
-  cp $samplesRootDir/Web/single-frame.html $outputDir
-  tsc --allowJs --project $samplesRootDir/Web/single-frame.tsconfig.json
-  cp $currentDir/build-wasm/OrthancStoneSingleFrame.js  $outputDir
-  cp $currentDir/build-wasm/OrthancStoneSingleFrame.wasm  $outputDir
-fi
-
-# build single-frame-editor
-if [[ $target == "all" || $target == "OrthancStoneSingleFrameEditor" ]]; then
-  cp $samplesRootDir/Web/single-frame-editor.html $outputDir
-  tsc --allowJs --project $samplesRootDir/Web/single-frame-editor.tsconfig.json
-  cp $currentDir/build-wasm/OrthancStoneSingleFrameEditor.js  $outputDir
-  cp $currentDir/build-wasm/OrthancStoneSingleFrameEditor.wasm  $outputDir
-fi
-
-# build simple-viewer project
-if [[ $target == "all" || $target == "OrthancStoneSimpleViewer" ]]; then
-  mkdir -p $outputDir/simple-viewer/
-  cp $samplesRootDir/SimpleViewer/Wasm/simple-viewer.html $outputDir/simple-viewer/
-  cp $samplesRootDir/SimpleViewer/Wasm/styles.css $outputDir/simple-viewer/
-  tsc --allowJs --project $samplesRootDir/SimpleViewer/Wasm/tsconfig-simple-viewer.json
-  cp $currentDir/build-wasm/OrthancStoneSimpleViewer.js  $outputDir/simple-viewer/
-  cp $currentDir/build-wasm/OrthancStoneSimpleViewer.wasm  $outputDir/simple-viewer/
-fi
-
-cd $currentDir
--- a/Applications/Samples/Deprecated/build-web.sh	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-#!/bin/bash
-
-set -e
-
-target=${1:-all}
-# this script currently assumes that the wasm code has been built on its side and is availabie in build-wasm/
-
-currentDir=$(pwd)
-samplesRootDir=$(pwd)
-
-echo "*************************************************************************"
-echo "samplesRootDir = $samplesRootDir"
-echo "*************************************************************************"
-
-outputDir=$samplesRootDir/build-web/
-mkdir -p "$outputDir"
-
-# files used by all single files samples
-cp "$samplesRootDir/Web/index.html" "$outputDir"
-cp "$samplesRootDir/Web/samples-styles.css" "$outputDir"
-
-# # build simple-viewer-single-file (obsolete project)
-# if [[ $target == "all" || $target == "OrthancStoneSimpleViewerSingleFile" ]]; then
-#   cp $samplesRootDir/Web/simple-viewer-single-file.html $outputDir
-#   tsc --project $samplesRootDir/Web/simple-viewer-single-file.tsconfig.json --outDir "$outputDir"
-#   browserify \
-#       "$outputDir/Platforms/Wasm/wasm-application-runner.js" \
-#       "$outputDir/Applications/Samples/Web/simple-viewer-single-file.js" \
-#       -o "$outputDir/app-simple-viewer-single-file.js"
-#   cp "$currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.js"  $outputDir
-#   cp "$currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.wasm"  $outputDir
-# fi
-
-# # build single-frame
-# if [[ $target == "all" || $target == "OrthancStoneSingleFrame" ]]; then
-#   cp $samplesRootDir/Web/single-frame.html $outputDir
-#   tsc --project $samplesRootDir/Web/single-frame.tsconfig.json --outDir "$outputDir"
-#   browserify \
-#       "$outputDir/Platforms/Wasm/wasm-application-runner.js" \
-#       "$outputDir/Applications/Samples/Web/single-frame.js" \
-#       -o "$outputDir/app-single-frame.js"
-#   cp "$currentDir/build-wasm/OrthancStoneSingleFrame.js"  $outputDir
-#   cp "$currentDir/build-wasm/OrthancStoneSingleFrame.wasm"  $outputDir
-# fi
-
-# build single-frame-editor
-if [[ $target == "all" || $target == "OrthancStoneSingleFrameEditor" ]]; then
-  cp $samplesRootDir/Web/single-frame-editor.html $outputDir
-  tsc --project $samplesRootDir/Web/single-frame-editor.tsconfig.json --outDir "$outputDir"
-  browserify \
-      "$outputDir/Platforms/Wasm/wasm-application-runner.js" \
-      "$outputDir/Applications/Samples/Web/single-frame-editor.js" \
-      -o "$outputDir/app-single-frame-editor.js"
-  cp "$currentDir/build-wasm/OrthancStoneSingleFrameEditor.js"  $outputDir
-  cp "$currentDir/build-wasm/OrthancStoneSingleFrameEditor.wasm"  $outputDir
-fi
-
-# build simple-viewer project
-if [[ $target == "all" || $target == "OrthancStoneSimpleViewer" ]]; then
-  mkdir -p $outputDir/simple-viewer/
-  cp $samplesRootDir/SimpleViewer/Wasm/simple-viewer.html $outputDir/simple-viewer/
-  cp $samplesRootDir/SimpleViewer/Wasm/styles.css $outputDir/simple-viewer/
-  
-  # the root dir must contain all the source files for the whole project
-  tsc --module commonjs --allowJs --project "$samplesRootDir/SimpleViewer/Wasm/tsconfig-simple-viewer.json" --rootDir "$samplesRootDir/../.." --outDir "$outputDir/simple-viewer/"
-  browserify \
-    "$outputDir/simple-viewer/Platforms/Wasm/wasm-application-runner.js" \
-    "$outputDir/simple-viewer/Applications/Samples/SimpleViewer/Wasm/simple-viewer.js" \
-    -o "$outputDir/simple-viewer/app-simple-viewer.js"
-  cp "$currentDir/build-wasm/OrthancStoneSimpleViewer.js"  "$outputDir/simple-viewer/"
-  cp "$currentDir/build-wasm/OrthancStoneSimpleViewer.wasm"  "$outputDir/simple-viewer/"
-fi
-
-cd $currentDir
--- a/Applications/Samples/Deprecated/get-requirements-windows.ps1	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-
-if ($true) {
-
-    Write-Error "This script is obsolete. Please work under WSL and run build-wasm.sh"
-
-} else {
-
-    param(
-        [IO.DirectoryInfo] $EmsdkRootDir = "C:\Emscripten",
-        [bool] $Overwrite = $false
-      )
-    
-    if (Test-Path -Path $EmsdkRootDir) {
-        if( $Override) {
-            Remove-Item -Path $EmsdkRootDir -Force -Recurse
-        } else {
-            throw "The `"$EmsdkRootDir`" folder may not exist! Use the Overwrite flag to bypass this check."
-        }
-    }
-    
-    # TODO: detect whether git is installed
-    # choco install -y git
-    
-    Write-Host "Will retrieve the Emscripten SDK to the `"$EmsdkRootDir`" folder"
-    
-    $EmsdkParentDir = split-path -Parent $EmsdkRootDir
-    $EmsdkRootName = split-path -Leaf $EmsdkRootDir
-    
-    Push-Location $EmsdkParentDir
-    
-    git clone https://github.com/juj/emsdk.git $EmsdkRootName
-    cd $EmsdkRootName
-    
-    git pull
-    
-    ./emsdk install latest
-    
-    ./emsdk activate latest
-    
-    echo "INFO: the ~/.emscripten file has been configured for this installation of Emscripten."
-    
-    Write-Host "emsdk is now installed in $EmsdkRootDir"
-    
-    Pop-Location
-
-}
-
-
-
-
--- a/Applications/Samples/Deprecated/nginx.local.conf	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-# Local config to serve the WASM samples static files and reverse proxy Orthanc.
-# Uses port 9977 instead of 80.
-
-# `events` section is mandatory
-events {
-  worker_connections 1024; # Default: 1024
-}
-
-http {
-
-  # prevent nginx sync issues on OSX
-  proxy_buffering off;
-
-  server {
-    listen 9977 default_server;
-    client_max_body_size 4G;
-
-    # location may have to be adjusted depending on your OS and nginx install
-    include /etc/nginx/mime.types;
-    # if not in your system mime.types, add this line to support WASM:
-    # types {
-    #    application/wasm                      wasm; 
-    # }
-
-    # serve WASM static files
-    root build-web/;
-    location / {
-	}
-
-    # reverse proxy orthanc
-	location /orthanc/ {
-		rewrite /orthanc(.*) $1 break;
-		proxy_pass http://127.0.0.1:8042;
-		proxy_set_header Host $http_host;
-		proxy_set_header my-auth-header good-token;
-		proxy_request_buffering off;
-		proxy_max_temp_file_size 0;
-		client_max_body_size 0;
-	}
-
-
-  }
-  
-}
--- a/Applications/Samples/Deprecated/package-lock.json	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-{
-  "requires": true,
-  "lockfileVersion": 1,
-  "dependencies": {
-    "typescript": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz",
-      "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg=="
-    }
-  }
-}
--- a/Applications/Samples/Deprecated/rt-viewer-demo/CMakeLists.txt	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,142 +0,0 @@
-cmake_minimum_required(VERSION 2.8.3)
-project(RtViewerDemo)
-
-if(MSVC)
-  add_definitions(/MP)
-  if (CMAKE_BUILD_TYPE MATCHES DEBUG)
-    add_definitions(/JMC)
-  endif()
-endif()
-
-message("-------------------------------------------------------------------------------------------------------------------")
-message("ORTHANC_FRAMEWORK_ROOT is set to ${ORTHANC_FRAMEWORK_ROOT}")
-message("-------------------------------------------------------------------------------------------------------------------")
-
-if(NOT DEFINED ORTHANC_FRAMEWORK_ROOT)
-  message(FATAL_ERROR "The location of the Orthanc source repository must be set in the ORTHANC_FRAMEWORK_ROOT CMake variable")
-endif()
-
-message("-------------------------------------------------------------------------------------------------------------------")
-message("STONE_SOURCES_DIR is set to ${STONE_SOURCES_DIR}")
-message("-------------------------------------------------------------------------------------------------------------------")
-
-if(NOT DEFINED STONE_SOURCES_DIR)
-  message(FATAL_ERROR "The location of the Stone of Orthanc source repository must be set in the STONE_SOURCES_DIR CMake variable")
-endif()
-
-include(${STONE_SOURCES_DIR}/Resources/CMake/OrthancStoneParameters.cmake)
-
-if (OPENSSL_NO_CAPIENG)
-add_definitions(-DOPENSSL_NO_CAPIENG=1)
-endif()
-
-set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application")
-set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application")
-set(ENABLE_WASM OFF CACHE BOOL "Target WASM application")
-
-if (ENABLE_WASM)
-  #####################################################################
-  ## Configuration of the Emscripten compiler for WebAssembly target
-  #####################################################################
-
-  set(WASM_FLAGS "-s WASM=1")
-  set(WASM_FLAGS "${WASM_FLAGS} -s STRICT=1") # drops support for all deprecated build options
-  set(WASM_FLAGS "${WASM_FLAGS} -s FILESYSTEM=1") # if we don't include it, gen_uuid.c fails to build because srand, getpid(), ... are not defined
-  set(WASM_FLAGS "${WASM_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") # actually enable exception catching 
-  set(WASM_FLAGS "${WASM_FLAGS} -s ERROR_ON_MISSING_LIBRARIES=1")
-
-  if (CMAKE_BUILD_TYPE MATCHES DEBUG)
-    set(WASM_FLAGS "${WASM_FLAGS} -g4") # generate debug information
-    set(WASM_FLAGS "${WASM_FLAGS} -s ASSERTIONS=2") # more runtime checks
-  else()
-    set(WASM_FLAGS "${WASM_FLAGS} -Os") # optimize for web (speed and size)
-  endif()
-
-  set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module")
-
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}")
-
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${WASM_FLAGS}")  # not always clear which flags are for the compiler and which one are for the linker -> pass them all to the linker too
-  # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Applications/Samples/samples-library.js")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"'")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_STACK=128000000")
-
-  add_definitions(-DORTHANC_ENABLE_WASM=1)
-  set(ORTHANC_SANDBOXED ON)
-
-elseif (ENABLE_QT OR ENABLE_SDL)
-
-  set(ENABLE_NATIVE ON)
-  set(ORTHANC_SANDBOXED OFF)
-  set(ENABLE_CRYPTO_OPTIONS ON)
-  set(ENABLE_GOOGLE_TEST ON)
-  set(ENABLE_WEB_CLIENT ON)
-
-endif()
-
-
-#####################################################################
-## Configuration for Orthanc
-#####################################################################
-
-if (ORTHANC_STONE_VERSION STREQUAL "mainline")
-  set(ORTHANC_FRAMEWORK_VERSION "mainline")
-  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
-else()
-  set(ORTHANC_FRAMEWORK_VERSION "1.4.1")
-  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
-endif()
-
-set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")")
-set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"")
-set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
-
-add_definitions(
-  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
-  )
-
-
-#####################################################################
-## Build a static library containing the Orthanc Stone framework
-#####################################################################
-
-LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
-
-include(${STONE_SOURCES_DIR}/Resources/CMake/OrthancStoneConfiguration.cmake)
-
-add_library(OrthancStone STATIC
-  ${ORTHANC_STONE_SOURCES}
-  )
-
-#####################################################################
-## Build all the sample applications
-#####################################################################
-
-include_directories(${ORTHANC_STONE_ROOT})
-
-list(APPEND RTVIEWERDEMO_APPLICATION_SOURCES
-  ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleInteractor.h
-  ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleApplicationBase.h
-  )
-
-if (ENABLE_WASM)
-  list(APPEND RTVIEWERDEMO_APPLICATION_SOURCES
-    ${STONE_WASM_SOURCES}
-    )
-endif()
-
-add_executable(RtViewerDemo
-  main.cpp
-  ${RTVIEWERDEMO_APPLICATION_SOURCES}
-)
-set_target_properties(RtViewerDemo PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=3)
-target_include_directories(RtViewerDemo PRIVATE ${ORTHANC_STONE_ROOT})
-target_link_libraries(RtViewerDemo OrthancStone)
-
--- a/Applications/Samples/Deprecated/rt-viewer-demo/build-sdl-msvc15.ps1	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-if (-not (Test-Path "build-sdl-msvc15")) {
-  mkdir -p "build-sdl-msvc15"
-}
-
-cd build-sdl-msvc15
-
-cmake -G "Visual Studio 15 2017 Win64" -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DSTONE_SOURCES_DIR="$($pwd)\..\..\..\.." -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\..\..\..\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON .. 
-
-if (!$?) {
-	Write-Error 'cmake configuration failed' -ErrorAction Stop
-}
-
-cmake --build . --target RtViewerDemo --config Debug
-
-if (!$?) {
-	Write-Error 'cmake build failed' -ErrorAction Stop
-}
-
-cd Debug
-
-.\RtViewerDemo.exe --ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa --dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb --struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9
-
-
-
--- a/Applications/Samples/Deprecated/rt-viewer-demo/build-wasm.sh	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-#!/bin/bash
-#
-# usage:
-# build-wasm BUILD_TYPE
-# where BUILD_TYPE is Debug, RelWithDebInfo or Release
-
-set -e
-
-buildType=${1:-Debug}
-
-currentDir=$(pwd)
-currentDirAbs=$(realpath $currentDir)
-
-mkdir -p build-wasm
-cd build-wasm
-
-source ~/apps/emsdk/emsdk_env.sh
-cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake \
--DCMAKE_BUILD_TYPE=$buildType -DSTONE_SOURCES_DIR=$currentDirAbs/../../../../orthanc-stone \
--DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDirAbs/../../../../orthanc \
--DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON
-
-ninja $target
-
-echo "-- building the web application -- "
-cd $currentDir
-./build-web.sh
-
-echo "Launch start-serving-files.sh to access the web sample application locally"
--- a/Applications/Samples/Deprecated/rt-viewer-demo/build-web.sh	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-#!/bin/bash
-
-set -e
-
-target=${1:-all}
-# this script currently assumes that the wasm code has been built on its side and is availabie in build-wasm/
-
-currentDir=$(pwd)
-samplesRootDir=$(pwd)
-
-tscOutput=$samplesRootDir/build-tsc-output/
-outputDir=$samplesRootDir/build-web/
-mkdir -p "$outputDir"
-
-# files used by all single files samples
-cp "$samplesRootDir/index.html" "$outputDir"
-cp "$samplesRootDir/samples-styles.css" "$outputDir"
-
-# build rt-viewer-demo
-cp $samplesRootDir/rt-viewer-demo.html $outputDir
-tsc --project $samplesRootDir/rt-viewer-demo.tsconfig.json --outDir "$tscOutput"
-browserify \
-    "$tscOutput/orthanc-stone/Platforms/Wasm/logger.js" \
-    "$tscOutput/orthanc-stone/Platforms/Wasm/stone-framework-loader.js" \
-    "$tscOutput/orthanc-stone/Platforms/Wasm/wasm-application-runner.js" \
-    "$tscOutput/orthanc-stone/Platforms/Wasm/wasm-viewport.js" \
-    "$tscOutput/rt-viewer-sample/rt-viewer-demo.js" \
-    -o "$outputDir/app-rt-viewer-demo.js"
-cp "$currentDir/build-wasm/RtViewerDemo.js"  $outputDir
-cp "$currentDir/build-wasm/RtViewerDemo.wasm"  $outputDir
-
-cd $currentDir
--- a/Applications/Samples/Deprecated/rt-viewer-demo/index.html	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-<!doctype html>
-
-<html lang="us">
-  <head>
-    <meta charset="utf-8" />
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
-    <!-- Disable pinch zoom on mobile devices -->
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
-    <meta name="HandheldFriendly" content="true" />
-
-    <title>Wasm Samples</title>
-
-<body>
-    <ul>
-      <li><a href="rt-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa&dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb&struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9">RTSTRUCT + CT + RTDOSE viewer demo. Pplease replace the url arguments with suitable IDs (you can find those in the Orthanc Explorer, for instance)</a></li>
-    </ul>
-</body>
-
-</html>
--- a/Applications/Samples/Deprecated/rt-viewer-demo/main.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,893 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "Applications/IStoneApplication.h"
-#include "Framework/Widgets/WorldSceneWidget.h"
-#include "Framework/Widgets/LayoutWidget.h"
-
-#if ORTHANC_ENABLE_WASM==1
-  #include "Platforms/Wasm/WasmPlatformApplicationAdapter.h"
-  #include "Platforms/Wasm/Defaults.h"
-  #include "Platforms/Wasm/WasmViewport.h"
-#endif
-
-#if ORTHANC_ENABLE_QT==1
-  #include "Qt/SampleMainWindow.h"
-  #include "Qt/SampleMainWindowWithButtons.h"
-#endif
-
-#include "Framework/Layers/DicomSeriesVolumeSlicer.h"
-#include "Framework/Widgets/SliceViewerWidget.h"
-#include "Framework/Volumes/StructureSetLoader.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-#include <Core/Images/ImageTraits.h>
-
-#include <boost/math/constants/constants.hpp>
-#include "Framework/dev.h"
-#include "Framework/Widgets/LayoutWidget.h"
-#include "Framework/Layers/DicomStructureSetSlicer.h"
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class RtViewerDemoBaseApplication : public IStoneApplication
-    {
-    protected:
-      // ownership is transferred to the application context
-#ifndef RESTORE_NON_RTVIEWERDEMO_BEHAVIOR
-      LayoutWidget*          mainWidget_;
-#else
-      WorldSceneWidget*  mainWidget_;
-#endif
-
-    public:
-      virtual void Initialize(StoneApplicationContext* context,
-                              IStatusBar& statusBar,
-                              const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE
-      {
-      }
-
-      virtual std::string GetTitle() const ORTHANC_OVERRIDE
-      {
-        return "Stone of Orthanc - Sample";
-      }
-
-      /**
-       * In the basic samples, the commands are handled by the platform adapter and NOT
-       * by the application handler
-      */
-      virtual void HandleSerializedMessage(const char* data) ORTHANC_OVERRIDE {};
-
-
-      virtual void Finalize() ORTHANC_OVERRIDE {}
-      virtual IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainWidget_;}
-
-#if ORTHANC_ENABLE_WASM==1
-      // default implementations for a single canvas named "canvas" in the HTML and an empty WasmApplicationAdapter
-
-      virtual void InitializeWasm() ORTHANC_OVERRIDE
-      {
-        AttachWidgetToWasmViewport("canvas", mainWidget_);
-      }
-
-      virtual WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(MessageBroker& broker)
-      {
-        return new WasmPlatformApplicationAdapter(broker, *this);
-      }
-#endif
-
-    };
-
-    // this application actually works in Qt and WASM
-    class RtViewerDemoBaseSingleCanvasWithButtonsApplication : public RtViewerDemoBaseApplication
-    {
-public:
-      virtual void OnPushButton1Clicked() {}
-      virtual void OnPushButton2Clicked() {}
-      virtual void OnTool1Clicked() {}
-      virtual void OnTool2Clicked() {}
-
-      virtual void GetButtonNames(std::string& pushButton1,
-                                  std::string& pushButton2,
-                                  std::string& tool1,
-                                  std::string& tool2
-                                  ) {
-        pushButton1 = "action1";
-        pushButton2 = "action2";
-        tool1 = "tool1";
-        tool2 = "tool2";
-      }
-
-#if ORTHANC_ENABLE_QT==1
-      virtual QStoneMainWindow* CreateQtMainWindow() {
-        return new SampleMainWindowWithButtons(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this);
-      }
-#endif
-
-    };
-
-    // this application actually works in SDL and WASM
-    class RtViewerDemoBaseApplicationSingleCanvas : public RtViewerDemoBaseApplication
-    {
-public:
-
-#if ORTHANC_ENABLE_QT==1
-      virtual QStoneMainWindow* CreateQtMainWindow() {
-        return new SampleMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this);
-      }
-#endif
-    };
-  }
-}
-
-
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    template <Orthanc::PixelFormat T>
-    void ReadDistributionInternal(std::vector<float>& distribution,
-                                  const Orthanc::ImageAccessor& image)
-    {
-      const unsigned int width = image.GetWidth();
-      const unsigned int height = image.GetHeight();
-      
-      distribution.resize(width * height);
-      size_t pos = 0;
-
-      for (unsigned int y = 0; y < height; y++)
-      {
-        for (unsigned int x = 0; x < width; x++, pos++)
-        {
-          distribution[pos] = Orthanc::ImageTraits<T>::GetFloatPixel(image, x, y);
-        }
-      }
-    }
-
-    void ReadDistribution(std::vector<float>& distribution,
-                          const Orthanc::ImageAccessor& image)
-    {
-      switch (image.GetFormat())
-      {
-        case Orthanc::PixelFormat_Grayscale8:
-          ReadDistributionInternal<Orthanc::PixelFormat_Grayscale8>(distribution, image);
-          break;
-
-        case Orthanc::PixelFormat_Grayscale16:
-          ReadDistributionInternal<Orthanc::PixelFormat_Grayscale16>(distribution, image);
-          break;
-
-        case Orthanc::PixelFormat_SignedGrayscale16:
-          ReadDistributionInternal<Orthanc::PixelFormat_SignedGrayscale16>(distribution, image);
-          break;
-
-        case Orthanc::PixelFormat_Grayscale32:
-          ReadDistributionInternal<Orthanc::PixelFormat_Grayscale32>(distribution, image);
-          break;
-
-        case Orthanc::PixelFormat_Grayscale64:
-          ReadDistributionInternal<Orthanc::PixelFormat_Grayscale64>(distribution, image);
-          break;
-
-        default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-      }
-    }
-
-
-    class DoseInteractor : public VolumeImageInteractor
-    {
-    private:
-      SliceViewerWidget&         widget_;
-      size_t               layer_;
-      DicomFrameConverter  converter_;
-
-
-      
-    protected:
-      virtual void NotifySliceChange(const ISlicedVolume& slicedVolume,
-                                    const size_t& sliceIndex,
-                                    const Slice& slice)
-      {
-        converter_ = slice.GetConverter();
-        
-  #if 0
-        const OrthancVolumeImage& volume = dynamic_cast<const OrthancVolumeImage&>(slicedVolume);
-
-        RenderStyle s = widget_.GetLayerStyle(layer_);
-
-        if (volume.FitWindowingToRange(s, slice.GetConverter()))
-        {
-          printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_);
-          widget_.SetLayerStyle(layer_, s);
-        }
-  #endif
-      }
-
-      virtual void NotifyVolumeReady(const ISlicedVolume& slicedVolume)
-      {
-        const float percentile = 0.01f;
-        const OrthancVolumeImage& volume = dynamic_cast<const OrthancVolumeImage&>(slicedVolume);
-
-        std::vector<float> distribution;
-        ReadDistribution(distribution, volume.GetImage().GetInternalImage());
-        std::sort(distribution.begin(), distribution.end());
-
-        int start = static_cast<int>(std::ceil(distribution.size() * percentile));
-        int end = static_cast<int>(std::floor(distribution.size() * (1.0f - percentile)));
-
-        float a = 0;
-        float b = 0;
-        
-        if (start < end &&
-            start >= 0 &&
-            end < static_cast<int>(distribution.size()))
-        {
-          a = distribution[start];
-          b = distribution[end];
-        }
-        else if (!distribution.empty())
-        {
-          // Too small distribution: Use full range
-          a = distribution.front();
-          b = distribution.back();
-        }
-
-        //printf("%f %f\n", a, b);
-
-        RenderStyle s = widget_.GetLayerStyle(layer_);
-        s.windowing_ = ImageWindowing_Custom;
-        s.customWindowCenter_ = static_cast<float>(converter_.Apply((a + b) / 2.0f));
-        s.customWindowWidth_ = static_cast<float>(converter_.Apply(b - a));
-        
-        // 96.210556 => 192.421112
-        widget_.SetLayerStyle(layer_, s);
-        printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_);      
-      }
-
-    public:
-      DoseInteractor(MessageBroker& broker, OrthancVolumeImage& volume,
-                    SliceViewerWidget& widget,
-                    VolumeProjection projection,
-                    size_t layer) :
-        VolumeImageInteractor(broker, volume, widget, projection),
-        widget_(widget),
-        layer_(layer)
-      {
-      }
-    };
-
-    class RtViewerDemoApplication :
-      public RtViewerDemoBaseApplicationSingleCanvas,
-      public IObserver
-    {
-    public:
-      std::vector<std::pair<SliceViewerWidget*, size_t> > doseCtWidgetLayerPairs_;
-      std::list<OrthancStone::IWorldSceneInteractor*>    interactors_;
-
-      class Interactor : public IWorldSceneInteractor
-      {
-      private:
-        RtViewerDemoApplication&  application_;
-
-      public:
-        Interactor(RtViewerDemoApplication&  application) :
-          application_(application)
-        {
-        }
-
-        virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
-          const ViewportGeometry& view,
-          MouseButton button,
-          KeyboardModifiers modifiers,
-          int viewportX,
-          int viewportY,
-          double x,
-          double y,
-          IStatusBar* statusBar,
-          const std::vector<Touch>& displayTouches)
-        {
-          return NULL;
-        }
-
-        virtual void MouseOver(CairoContext& context,
-          WorldSceneWidget& widget,
-          const ViewportGeometry& view,
-          double x,
-          double y,
-          IStatusBar* statusBar)
-        {
-          if (statusBar != NULL)
-          {
-            Vector p = dynamic_cast<SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
-
-            char buf[64];
-            sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)",
-              p[0] / 10.0, p[1] / 10.0, p[2] / 10.0);
-            statusBar->SetMessage(buf);
-          }
-        }
-
-        virtual void MouseWheel(WorldSceneWidget& widget,
-          MouseWheelDirection direction,
-          KeyboardModifiers modifiers,
-          IStatusBar* statusBar)
-        {
-          int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1);
-
-          switch (direction)
-          {
-          case MouseWheelDirection_Up:
-            application_.OffsetSlice(-scale);
-            break;
-
-          case MouseWheelDirection_Down:
-            application_.OffsetSlice(scale);
-            break;
-
-          default:
-            break;
-          }
-        }
-
-        virtual void KeyPressed(WorldSceneWidget& widget,
-          KeyboardKeys key,
-          char keyChar,
-          KeyboardModifiers modifiers,
-          IStatusBar* statusBar)
-        {
-          switch (keyChar)
-          {
-          case 's':
-            // TODO: recursively traverse children
-            widget.FitContent();
-            break;
-
-          default:
-            break;
-          }
-        }
-      };
-
-      void OffsetSlice(int offset)
-      {
-        if (source_ != NULL)
-        {
-          int slice = static_cast<int>(slice_) + offset;
-
-          if (slice < 0)
-          {
-            slice = 0;
-          }
-
-          if (slice >= static_cast<int>(source_->GetSliceCount()))
-          {
-            slice = static_cast<int>(source_->GetSliceCount()) - 1;
-          }
-
-          if (slice != static_cast<int>(slice_))
-          {
-            SetSlice(slice);
-          }
-        }
-      }
-
-
-      SliceViewerWidget& GetMainWidget()
-      {
-        return *dynamic_cast<SliceViewerWidget*>(mainWidget_);
-      }
-
-
-      void SetSlice(size_t index)
-      {
-        if (source_ != NULL &&
-          index < source_->GetSliceCount())
-        {
-          slice_ = static_cast<unsigned int>(index);
-
-#if 1
-          GetMainWidget().SetSlice(source_->GetSlice(slice_).GetGeometry());
-#else
-          // TEST for scene extents - Rotate the axes
-          double a = 15.0 / 180.0 * boost::math::constants::pi<double>();
-
-#if 1
-          Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0);
-          Vector y; GeometryToolbox::AssignVector(y, -sin(a), cos(a), 0);
-#else
-          // Flip the normal
-          Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0);
-          Vector y; GeometryToolbox::AssignVector(y, sin(a), -cos(a), 0);
-#endif
-
-          SliceGeometry s(source_->GetSlice(slice_).GetGeometry().GetOrigin(), x, y);
-          widget_->SetSlice(s);
-#endif
-        }
-      }
-
-
-      void OnMainWidgetGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message)
-      {
-        // Once the geometry of the series is downloaded from Orthanc,
-        // display its middle slice, and adapt the viewport to fit this
-        // slice
-        if (source_ == &message.GetOrigin())
-        {
-          SetSlice(source_->GetSliceCount() / 2);
-        }
-
-        GetMainWidget().FitContent();
-      }
-
-    DicomFrameConverter                                converter_;
-
-    void OnSliceContentChangedMessage(const ISlicedVolume::SliceContentChangedMessage&   message)
-    {
-      converter_ = message.GetSlice().GetConverter();
-    }
-
-    void OnVolumeReadyMessage(const ISlicedVolume::VolumeReadyMessage& message)
-    {
-      const float percentile = 0.01f;
-
-      auto& slicedVolume = message.GetOrigin();
-      const OrthancVolumeImage& volume = dynamic_cast<const OrthancVolumeImage&>(slicedVolume);
-
-      std::vector<float> distribution;
-      ReadDistribution(distribution, volume.GetImage().GetInternalImage());
-      std::sort(distribution.begin(), distribution.end());
-
-      int start = static_cast<int>(std::ceil(distribution.size() * percentile));
-      int end = static_cast<int>(std::floor(distribution.size() * (1.0f - percentile)));
-
-      float a = 0;
-      float b = 0;
-
-      if (start < end &&
-        start >= 0 &&
-        end < static_cast<int>(distribution.size()))
-      {
-        a = distribution[start];
-        b = distribution[end];
-      }
-      else if (!distribution.empty())
-      {
-        // Too small distribution: Use full range
-        a = distribution.front();
-        b = distribution.back();
-      }
-
-      //printf("WINDOWING %f %f\n", a, b);
-
-      for (const auto& pair : doseCtWidgetLayerPairs_)
-      {
-        auto widget = pair.first;
-        auto layer = pair.second;
-        RenderStyle s = widget->GetLayerStyle(layer);
-        s.windowing_ = ImageWindowing_Custom;
-        s.customWindowCenter_ = static_cast<float>(converter_.Apply((a + b) / 2.0f));
-        s.customWindowWidth_ = static_cast<float>(converter_.Apply(b - a));
-
-        // 96.210556 => 192.421112
-        widget->SetLayerStyle(layer, s);
-        printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_);
-      }
-    }
-      
-
-
-      size_t AddDoseLayer(SliceViewerWidget& widget,
-        OrthancVolumeImage& volume, VolumeProjection projection);
-
-      void AddStructLayer(
-        SliceViewerWidget& widget, StructureSetLoader& loader);
-
-      SliceViewerWidget* CreateDoseCtWidget(
-        std::unique_ptr<OrthancVolumeImage>& ct,
-        std::unique_ptr<OrthancVolumeImage>& dose,
-        std::unique_ptr<StructureSetLoader>& structLoader,
-        VolumeProjection projection);
-
-      void AddCtLayer(SliceViewerWidget& widget, OrthancVolumeImage& volume);
-
-      std::unique_ptr<Interactor>         mainWidgetInteractor_;
-      const DicomSeriesVolumeSlicer*    source_;
-      unsigned int                      slice_;
-
-      std::string                                        ctSeries_;
-      std::string                                        doseInstance_;
-      std::string                                        doseSeries_;
-      std::string                                        structInstance_;
-      std::unique_ptr<OrthancStone::OrthancVolumeImage>    dose_;
-      std::unique_ptr<OrthancStone::OrthancVolumeImage>    ct_;
-      std::unique_ptr<OrthancStone::StructureSetLoader>    struct_;
-
-    public:
-      RtViewerDemoApplication(MessageBroker& broker) :
-        IObserver(broker),
-        source_(NULL),
-        slice_(0)
-      {
-      }
-
-      /*
-      dev options on bgo xps15
-
-      COMMAND LINE
-      --ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa --dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb --struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9
-
-      URL PARAMETERS
-      ?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa&dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb&struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9
-
-      */
-
-      void ParseParameters(const boost::program_options::variables_map&  parameters)
-      {
-        // Generic
-        {
-          if (parameters.count("verbose"))
-          {
-            Orthanc::Logging::EnableInfoLevel(true);
-            LOG(INFO) << "Verbose logs (info) are enabled";
-          }
-        }
-
-        {
-          if (parameters.count("trace"))
-          {
-            LOG(INFO) << "parameters.count(\"trace\") != 0";
-            Orthanc::Logging::EnableTraceLevel(true);
-            VLOG(1) << "Trace logs (debug) are enabled";
-          }
-        }
-
-        // CT series
-        {
-
-          if (parameters.count("ct-series") != 1)
-          {
-            LOG(ERROR) << "There must be exactly one CT series specified";
-            throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-          }
-          ctSeries_ = parameters["ct-series"].as<std::string>();
-        }
-
-        // RTDOSE 
-        {
-          if (parameters.count("dose-instance") == 1)
-          {
-            doseInstance_ = parameters["dose-instance"].as<std::string>();
-          }
-          else
-          {
-#ifdef BGO_NOT_IMPLEMENTED_YET
-            // Dose series
-            if (parameters.count("dose-series") != 1)
-            {
-              LOG(ERROR) << "the RTDOSE series is missing";
-              throw Orthanc::OrthancException(
-                Orthanc::ErrorCode_ParameterOutOfRange);
-            }
-            doseSeries_ = parameters["ct"].as<std::string>();
-#endif
-            LOG(ERROR) << "the RTSTRUCT instance is missing";
-            throw Orthanc::OrthancException(
-              Orthanc::ErrorCode_ParameterOutOfRange);
-          }
-        }
-        
-        // RTSTRUCT 
-        {
-          if (parameters.count("struct-instance") == 1)
-          {
-            structInstance_ = parameters["struct-instance"].as<std::string>();
-          }
-          else
-          {
-#ifdef BGO_NOT_IMPLEMENTED_YET
-            // Struct series
-            if (parameters.count("struct-series") != 1)
-            {
-              LOG(ERROR) << "the RTSTRUCT series is missing";
-              throw Orthanc::OrthancException(
-                Orthanc::ErrorCode_ParameterOutOfRange);
-            }
-            structSeries_ = parameters["struct-series"].as<std::string>();
-#endif
-            LOG(ERROR) << "the RTSTRUCT instance is missing";
-            throw Orthanc::OrthancException(
-              Orthanc::ErrorCode_ParameterOutOfRange);
-          }
-        }
-      }
-
-      virtual void DeclareStartupOptions(
-        boost::program_options::options_description& options)
-      {
-        boost::program_options::options_description generic(
-          "RtViewerDemo options. Please note that some of these options "
-          "are mutually exclusive");
-        generic.add_options()
-          ("ct-series", boost::program_options::value<std::string>(),
-            "Orthanc ID of the CT series")
-          ("dose-instance", boost::program_options::value<std::string>(), 
-            "Orthanc ID of the RTDOSE instance (incompatible with dose-series)")
-          ("dose-series", boost::program_options::value<std::string>(), 
-            "NOT IMPLEMENTED YET. Orthanc ID of the RTDOSE series (incompatible"
-            " with dose-instance)")
-          ("struct-instance", boost::program_options::value<std::string>(), 
-            "Orthanc ID of the RTSTRUCT instance (incompatible with struct-"
-            "series)")
-          ("struct-series", boost::program_options::value<std::string>(), 
-            "NOT IMPLEMENTED YET. Orthanc ID of the RTSTRUCT (incompatible with"
-            " struct-instance)")
-          ("smooth", boost::program_options::value<bool>()->default_value(true),
-            "Enable bilinear image smoothing")
-          ;
-
-        options.add(generic);
-      }
-
-      virtual void Initialize(
-        StoneApplicationContext*                      context,
-        IStatusBar&                                   statusBar,
-        const boost::program_options::variables_map&  parameters)
-      {
-        using namespace OrthancStone;
-
-        ParseParameters(parameters);
-
-        context_ = context;
-
-        statusBar.SetMessage("Use the key \"s\" to reinitialize the layout");
-
-        if (!ctSeries_.empty())
-        {
-          printf("CT = [%s]\n", ctSeries_.c_str());
-
-          ct_.reset(new OrthancStone::OrthancVolumeImage(
-            IObserver::GetBroker(), context->GetOrthancApiClient(), false));
-          ct_->ScheduleLoadSeries(ctSeries_);
-          //ct_->ScheduleLoadSeries(
-          //  "a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");
-          //ct_->ScheduleLoadSeries(
-          //  "03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0");
-        }
-
-        if (!doseSeries_.empty() ||
-          !doseInstance_.empty())
-        {
-          dose_.reset(new OrthancStone::OrthancVolumeImage(
-            IObserver::GetBroker(), context->GetOrthancApiClient(), true));
-
-
-          dose_->RegisterObserverCallback(
-            new Callable<RtViewerDemoApplication, ISlicedVolume::VolumeReadyMessage>
-            (*this, &RtViewerDemoApplication::OnVolumeReadyMessage));
-
-          dose_->RegisterObserverCallback(
-            new Callable<RtViewerDemoApplication, ISlicedVolume::SliceContentChangedMessage>
-            (*this, &RtViewerDemoApplication::OnSliceContentChangedMessage));
-
-          if (doseInstance_.empty())
-          {
-            dose_->ScheduleLoadSeries(doseSeries_);
-          }
-          else
-          {
-            dose_->ScheduleLoadInstance(doseInstance_);
-          }
-
-          //dose_->ScheduleLoadInstance(
-            //"830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb");  // 1
-          //dose_->ScheduleLoadInstance(
-            //"269f26f4-0c83eeeb-2e67abbd-5467a40f-f1bec90c"); //0522c0001 TCIA
-        }
-
-        if (!structInstance_.empty())
-        {
-          struct_.reset(new OrthancStone::StructureSetLoader(
-            IObserver::GetBroker(), context->GetOrthancApiClient()));
-
-          struct_->ScheduleLoadInstance(structInstance_);
-
-          //struct_->ScheduleLoadInstance(
-            //"54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");
-          //struct_->ScheduleLoadInstance(
-            //"17cd032b-ad92a438-ca05f06a-f9e96668-7e3e9e20"); // 0522c0001 TCIA
-        }
-
-        mainWidget_ = new LayoutWidget("main-layout");
-        mainWidget_->SetBackgroundColor(0, 0, 0);
-        mainWidget_->SetBackgroundCleared(true);
-        mainWidget_->SetPadding(0);
-
-        auto axialWidget = CreateDoseCtWidget
-        (ct_, dose_, struct_, OrthancStone::VolumeProjection_Axial);
-        mainWidget_->AddWidget(axialWidget);
-               
-        std::unique_ptr<OrthancStone::LayoutWidget> subLayout(
-          new OrthancStone::LayoutWidget("main-layout"));
-        subLayout->SetVertical();
-        subLayout->SetPadding(5);
-
-        auto coronalWidget = CreateDoseCtWidget
-        (ct_, dose_, struct_, OrthancStone::VolumeProjection_Coronal);
-        subLayout->AddWidget(coronalWidget);
-
-        auto sagittalWidget = CreateDoseCtWidget
-        (ct_, dose_, struct_, OrthancStone::VolumeProjection_Sagittal);
-        subLayout->AddWidget(sagittalWidget);
-        
-        mainWidget_->AddWidget(subLayout.release());
-      }
-    };
-
-
-    size_t RtViewerDemoApplication::AddDoseLayer(
-      SliceViewerWidget& widget,
-      OrthancVolumeImage& volume, VolumeProjection projection)
-    {
-      size_t layer = widget.AddLayer(
-        new VolumeImageMPRSlicer(IObserver::GetBroker(), volume));
-
-      RenderStyle s;
-      //s.drawGrid_ = true;
-      s.SetColor(255, 0, 0);  // Draw missing PET layer in red
-      s.alpha_ = 0.3f;
-      s.applyLut_ = true;
-      s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET;
-      s.interpolation_ = ImageInterpolation_Bilinear;
-      widget.SetLayerStyle(layer, s);
-
-      return layer;
-    }
-
-    void RtViewerDemoApplication::AddStructLayer(
-      SliceViewerWidget& widget, StructureSetLoader& loader)
-    {
-      widget.AddLayer(new DicomStructureSetSlicer(
-        IObserver::GetBroker(), loader));
-    }
-
-    SliceViewerWidget* RtViewerDemoApplication::CreateDoseCtWidget(
-      std::unique_ptr<OrthancVolumeImage>& ct,
-      std::unique_ptr<OrthancVolumeImage>& dose,
-      std::unique_ptr<StructureSetLoader>& structLoader,
-      VolumeProjection projection)
-    {
-      std::unique_ptr<OrthancStone::SliceViewerWidget> widget(
-        new OrthancStone::SliceViewerWidget(IObserver::GetBroker(),
-          "ct-dose-widget"));
-
-      if (ct.get() != NULL)
-      {
-        AddCtLayer(*widget, *ct);
-      }
-
-      if (dose.get() != NULL)
-      {
-        size_t layer = AddDoseLayer(*widget, *dose, projection);
-
-        // we need to store the dose rendering widget because we'll update them
-        // according to various asynchronous events
-        doseCtWidgetLayerPairs_.push_back(std::make_pair(widget.get(), layer));
-#if 0
-        interactors_.push_back(new VolumeImageInteractor(
-          IObserver::GetBroker(), *dose, *widget, projection));
-#else
-        interactors_.push_back(new DoseInteractor(
-          IObserver::GetBroker(), *dose, *widget, projection, layer));
-#endif
-      }
-      else if (ct.get() != NULL)
-      {
-        interactors_.push_back(
-          new VolumeImageInteractor(
-            IObserver::GetBroker(), *ct, *widget, projection));
-      }
-
-      if (structLoader.get() != NULL)
-      {
-        AddStructLayer(*widget, *structLoader);
-      }
-
-      return widget.release();
-    }
-
-    void RtViewerDemoApplication::AddCtLayer(
-      SliceViewerWidget& widget,
-      OrthancVolumeImage& volume)
-    {
-      size_t layer = widget.AddLayer(
-        new VolumeImageMPRSlicer(IObserver::GetBroker(), volume));
-
-      RenderStyle s;
-      //s.drawGrid_ = true;
-      s.alpha_ = 1;
-      s.windowing_ = ImageWindowing_Bone;
-      widget.SetLayerStyle(layer, s);
-    }
-  }
-}
-
-
-
-#if ORTHANC_ENABLE_WASM==1
-
-#include "Platforms/Wasm/WasmWebService.h"
-#include "Platforms/Wasm/WasmViewport.h"
-
-#include <emscripten/emscripten.h>
-
-//#include "SampleList.h"
-
-
-OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker)
-{
-  return new OrthancStone::Samples::RtViewerDemoApplication(broker);
-}
-
-OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application)
-{
-  return dynamic_cast<OrthancStone::Samples::RtViewerDemoApplication*>(application)->CreateWasmApplicationAdapter(broker);
-}
-
-#else
-
-//#include "SampleList.h"
-#if ORTHANC_ENABLE_SDL==1
-#include "Applications/Sdl/SdlStoneApplicationRunner.h"
-#endif
-#if ORTHANC_ENABLE_QT==1
-#include "Applications/Qt/SampleQtApplicationRunner.h"
-#endif
-#include "Framework/Messages/MessageBroker.h"
-
-int main(int argc, char* argv[])
-{
-  OrthancStone::MessageBroker broker;
-  OrthancStone::Samples::RtViewerDemoApplication sampleStoneApplication(broker);
-
-#if ORTHANC_ENABLE_SDL==1
-  OrthancStone::SdlStoneApplicationRunner sdlApplicationRunner(broker, sampleStoneApplication);
-  return sdlApplicationRunner.Execute(argc, argv);
-#endif
-#if ORTHANC_ENABLE_QT==1
-  OrthancStone::Samples::SampleQtApplicationRunner qtAppRunner(broker, sampleStoneApplication);
-  return qtAppRunner.Execute(argc, argv);
-#endif
-}
-
-
-#endif
-
-
-
-
-
-
-
--- a/Applications/Samples/Deprecated/rt-viewer-demo/nginx.local.conf	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-# Local config to serve the WASM samples static files and reverse proxy Orthanc.
-# Uses port 9977 instead of 80.
-
-# `events` section is mandatory
-events {
-  worker_connections 1024; # Default: 1024
-}
-
-http {
-
-  # prevent nginx sync issues on OSX
-  proxy_buffering off;
-
-  server {
-    listen 9977 default_server;
-    client_max_body_size 4G;
-
-    # location may have to be adjusted depending on your OS and nginx install
-    include /etc/nginx/mime.types;
-    # if not in your system mime.types, add this line to support WASM:
-    # types {
-    #    application/wasm                      wasm; 
-    # }
-
-    # serve WASM static files
-    root build-web/;
-    location / {
-	}
-
-    # reverse proxy orthanc
-	location /orthanc/ {
-		rewrite /orthanc(.*) $1 break;
-		proxy_pass http://127.0.0.1:8042;
-		proxy_set_header Host $http_host;
-		proxy_set_header my-auth-header good-token;
-		proxy_request_buffering off;
-		proxy_max_temp_file_size 0;
-		client_max_body_size 0;
-	}
-
-
-  }
-  
-}
--- a/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.html	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-<!doctype html>
-
-<html lang="us">
-  <head>
-    <meta charset="utf-8" />
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
-    <!-- Disable pinch zoom on mobile devices -->
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
-    <meta name="HandheldFriendly" content="true" />
-
-    <title>Simple Viewer</title>
-    <link href="samples-styles.css" rel="stylesheet" />
-
-<body>
-  <div style="width: 100%; height: 5%">
-    <p>RTSTRUCT viewer demonstration</p>
-  </div>
-  <div style="width: 100%; height: 95%">
-    <canvas id="canvas"></canvas>
-  </div>
-  <script type="text/javascript" src="app-rt-viewer-demo.js"></script>
-</body>
-
-</html>
\ No newline at end of file
--- a/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-import { InitializeWasmApplication } from '../../../Platforms/Wasm/wasm-application-runner';
-
-
-InitializeWasmApplication("RtViewerDemo", "/orthanc");
-
--- a/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.tsconfig.json	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-{
-    "extends" : "./tsconfig-samples",
-    "compilerOptions": {
-    },
-    "include" : [
-        "rt-viewer-demo.ts"
-    ]
-}
--- a/Applications/Samples/Deprecated/rt-viewer-demo/samples-styles.css	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-html, body {
-  width: 100%;
-  height: 100%;
-  margin: 0px;
-  border: 0;
-  overflow: hidden; /*  Disable scrollbars */
-  display: block;  /* No floating content on sides */
-  background-color: black;
-  color: white;
-  font-family: Arial, Helvetica, sans-serif;
-}
-
-canvas {
-  left:0px;
-  top:0px;
-}
\ No newline at end of file
--- a/Applications/Samples/Deprecated/rt-viewer-demo/start-serving-files.sh	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-sudo nginx -p $(pwd) -c nginx.local.conf
-
-echo "Please browse to :"
-
-echo "http://localhost:9977/rt-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa&dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb&struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"
-
-echo "(This requires you have uploaded the correct files to your local Orthanc instance)"
--- a/Applications/Samples/Deprecated/rt-viewer-demo/stop-serving-files.sh	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-#!/bin/bash
-
-sudo nginx -s stop
-
--- a/Applications/Samples/Deprecated/rt-viewer-demo/tsconfig-samples.json	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-{
-    "extends" : "../../../Platforms/Wasm/tsconfig-stone.json",
-    "compilerOptions": {
-        "sourceMap": false,
-        "lib" : [
-            "es2017",
-            "dom",
-            "dom.iterable"
-        ]
-    }
-}
--- a/Applications/Samples/Deprecated/tsconfig-stone.json	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-{
-    "include" : [
-        "../../Platforms/Wasm/stone-framework-loader.ts",
-        "../../Platforms/Wasm/wasm-application-runner.ts",
-        "../../Platforms/Wasm/wasm-viewport.ts"
-    ]
-}
--- a/Applications/Sdl/SdlCairoSurface.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "SdlCairoSurface.h"
-
-#if ORTHANC_ENABLE_SDL == 1
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  SdlCairoSurface::SdlCairoSurface(SdlWindow& window) :
-    window_(window),
-    sdlSurface_(NULL)
-  {
-  }
-
-
-  SdlCairoSurface::~SdlCairoSurface()
-  {
-    if (sdlSurface_)
-    {
-      SDL_FreeSurface(sdlSurface_);
-    }
-  }
-
-
-  void SdlCairoSurface::SetSize(unsigned int width,
-                                unsigned int height)
-  {
-    if (cairoSurface_.get() == NULL ||
-        cairoSurface_->GetWidth() != width ||
-        cairoSurface_->GetHeight() != height)
-    {
-      cairoSurface_.reset(new CairoSurface(width, height, false /* no alpha */));
-
-      // TODO Big endian?
-      static const uint32_t rmask = 0x00ff0000;
-      static const uint32_t gmask = 0x0000ff00;
-      static const uint32_t bmask = 0x000000ff;
-
-      if (sdlSurface_)
-      {
-        SDL_FreeSurface(sdlSurface_);
-      }
-
-      sdlSurface_ = SDL_CreateRGBSurfaceFrom(cairoSurface_->GetBuffer(), width, height, 32,
-                                             cairoSurface_->GetPitch(), rmask, gmask, bmask, 0);
-      if (!sdlSurface_)
-      {
-        LOG(ERROR) << "Cannot create a SDL surface from a Cairo surface";
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-    }
-  }
-
-
-  void SdlCairoSurface::Render(Deprecated::IViewport& viewport)
-  {
-    if (cairoSurface_.get() == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-
-    Orthanc::ImageAccessor target;
-    cairoSurface_->GetWriteableAccessor(target);
-
-    if (viewport.Render(target))
-    {
-      window_.Render(sdlSurface_);
-    }
-  }
-}
-
-#endif
--- a/Applications/Sdl/SdlCairoSurface.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/**
- * 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
-
-#if ORTHANC_ENABLE_SDL == 1
-
-#include "../../Framework/Viewport/SdlWindow.h"
-#include "../../Framework/Wrappers/CairoSurface.h"
-#include "../../Framework/Deprecated/Viewport/IViewport.h"
-
-#include <Core/Compatibility.h>
-
-#include <SDL_render.h>
-#include <boost/thread/mutex.hpp>
-
-namespace OrthancStone
-{
-  class SdlCairoSurface : public boost::noncopyable
-  {
-  private:
-    std::unique_ptr<CairoSurface>  cairoSurface_;
-    SdlWindow&                   window_;
-    SDL_Surface*                 sdlSurface_;
-
-  public:
-    SdlCairoSurface(SdlWindow& window);
-
-    ~SdlCairoSurface();
-
-    void SetSize(unsigned int width,
-                 unsigned int height);
-
-    void Render(Deprecated::IViewport& viewport);
-  };
-}
-
-#endif
--- a/Applications/Sdl/SdlEngine.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,282 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "SdlEngine.h"
-
-#if ORTHANC_ENABLE_SDL == 1
-
-#include <Core/Logging.h>
-
-#include <SDL.h>
-
-namespace OrthancStone
-{
-  void SdlEngine::SetSize(unsigned int width,
-                          unsigned int height)
-  {
-    NativeStoneApplicationContext::GlobalMutexLocker locker(context_);
-    locker.GetCentralViewport().SetSize(width, height);
-    surface_.SetSize(width, height);
-  }
-
-
-  void SdlEngine::RenderFrame()
-  {
-    if (viewportChanged_)
-    {
-      NativeStoneApplicationContext::GlobalMutexLocker locker(context_);
-      surface_.Render(locker.GetCentralViewport());
-
-      viewportChanged_ = false;
-    }
-  }
-
-
-  KeyboardModifiers SdlEngine::GetKeyboardModifiers(const uint8_t* keyboardState,
-                                                    const int scancodeCount)
-  {
-    int result = KeyboardModifiers_None;
-
-    if (keyboardState != NULL)
-    {
-      if (SDL_SCANCODE_LSHIFT < scancodeCount &&
-          keyboardState[SDL_SCANCODE_LSHIFT])
-      {
-        result |= KeyboardModifiers_Shift;
-      }
-
-      if (SDL_SCANCODE_RSHIFT < scancodeCount &&
-          keyboardState[SDL_SCANCODE_RSHIFT])
-      {
-        result |= KeyboardModifiers_Shift;
-      }
-
-      if (SDL_SCANCODE_LCTRL < scancodeCount &&
-          keyboardState[SDL_SCANCODE_LCTRL])
-      {
-        result |= KeyboardModifiers_Control;
-      }
-
-      if (SDL_SCANCODE_RCTRL < scancodeCount &&
-          keyboardState[SDL_SCANCODE_RCTRL])
-      {
-        result |= KeyboardModifiers_Control;
-      }
-
-      if (SDL_SCANCODE_LALT < scancodeCount &&
-          keyboardState[SDL_SCANCODE_LALT])
-      {
-        result |= KeyboardModifiers_Alt;
-      }
-
-      if (SDL_SCANCODE_RALT < scancodeCount &&
-          keyboardState[SDL_SCANCODE_RALT])
-      {
-        result |= KeyboardModifiers_Alt;
-      }
-    }
-
-    return static_cast<KeyboardModifiers>(result);
-  }
-
-
-  SdlEngine::SdlEngine(SdlWindow& window,
-                       NativeStoneApplicationContext& context) :
-    window_(window),
-    context_(context),
-    surface_(window),
-    viewportChanged_(true)
-  {
-  }
-  
-
-  void SdlEngine::Run()
-  {
-    int scancodeCount = 0;
-    const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
-
-    SetSize(window_.GetWidth(), window_.GetHeight());
-    
-    {
-      NativeStoneApplicationContext::GlobalMutexLocker locker(context_);
-      locker.GetCentralViewport().FitContent();
-    }
-    
-    bool stop = false;
-    while (!stop)
-    {
-      RenderFrame();
-
-      SDL_Event event;
-
-      while (!stop &&
-             SDL_PollEvent(&event))
-      {
-        NativeStoneApplicationContext::GlobalMutexLocker locker(context_);
-
-        if (event.type == SDL_QUIT)
-        {
-          stop = true;
-          break;
-        }
-        else if (event.type == SDL_MOUSEBUTTONDOWN)
-        {
-          KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount);
-
-          switch (event.button.button)
-          {
-          case SDL_BUTTON_LEFT:
-            locker.GetCentralViewport().MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>());
-            break;
-            
-          case SDL_BUTTON_RIGHT:
-            locker.GetCentralViewport().MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>());
-            break;
-            
-          case SDL_BUTTON_MIDDLE:
-            locker.GetCentralViewport().MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>());
-            break;
-
-          default:
-            break;
-          }
-        }
-        else if (event.type == SDL_MOUSEMOTION)
-        {
-          locker.GetCentralViewport().MouseMove(event.button.x, event.button.y, std::vector<Deprecated::Touch>());
-        }
-        else if (event.type == SDL_MOUSEBUTTONUP)
-        {
-          locker.GetCentralViewport().MouseUp();
-        }
-        else if (event.type == SDL_WINDOWEVENT)
-        {
-          switch (event.window.event)
-          {
-          case SDL_WINDOWEVENT_LEAVE:
-            locker.GetCentralViewport().MouseLeave();
-            break;
-
-          case SDL_WINDOWEVENT_ENTER:
-            locker.GetCentralViewport().MouseEnter();
-            break;
-
-          case SDL_WINDOWEVENT_SIZE_CHANGED:
-            SetSize(event.window.data1, event.window.data2);
-            break;
-
-          default:
-            break;
-          }
-        }
-        else if (event.type == SDL_MOUSEWHEEL)
-        {
-          KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount);
-
-          int x, y;
-          SDL_GetMouseState(&x, &y);
-
-          if (event.wheel.y > 0)
-          {
-            locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers);
-          }
-          else if (event.wheel.y < 0)
-          {
-            locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers);
-          }
-        }
-        else if (event.type == SDL_KEYDOWN &&
-                 event.key.repeat == 0 /* Ignore key bounce */)
-        {
-          KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount);
-
-          switch (event.key.keysym.sym)
-          {
-          case SDLK_a:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'a', modifiers);  break;
-          case SDLK_b:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'b', modifiers);  break;
-          case SDLK_c:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'c', modifiers);  break;
-          case SDLK_d:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'd', modifiers);  break;
-          case SDLK_e:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'e', modifiers);  break;
-          case SDLK_f:    window_.ToggleMaximize();                         break;
-          case SDLK_g:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'g', modifiers);  break;
-          case SDLK_h:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'h', modifiers);  break;
-          case SDLK_i:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'i', modifiers);  break;
-          case SDLK_j:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'j', modifiers);  break;
-          case SDLK_k:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'k', modifiers);  break;
-          case SDLK_l:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'l', modifiers);  break;
-          case SDLK_m:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'm', modifiers);  break;
-          case SDLK_n:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'n', modifiers);  break;
-          case SDLK_o:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'o', modifiers);  break;
-          case SDLK_p:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'p', modifiers);  break;
-          case SDLK_q:    stop = true;                                      break;
-          case SDLK_r:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'r', modifiers);  break;
-          case SDLK_s:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 's', modifiers);  break;
-          case SDLK_t:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 't', modifiers);  break;
-          case SDLK_u:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'u', modifiers);  break;
-          case SDLK_v:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'v', modifiers);  break;
-          case SDLK_w:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'w', modifiers);  break;
-          case SDLK_x:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'x', modifiers);  break;
-          case SDLK_y:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'y', modifiers);  break;
-          case SDLK_z:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'z', modifiers);  break;
-          case SDLK_KP_0: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '0', modifiers);  break;
-          case SDLK_KP_1: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '1', modifiers);  break;
-          case SDLK_KP_2: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '2', modifiers);  break;
-          case SDLK_KP_3: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '3', modifiers);  break;
-          case SDLK_KP_4: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '4', modifiers);  break;
-          case SDLK_KP_5: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '5', modifiers);  break;
-          case SDLK_KP_6: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '6', modifiers);  break;
-          case SDLK_KP_7: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '7', modifiers);  break;
-          case SDLK_KP_8: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '8', modifiers);  break;
-          case SDLK_KP_9: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '9', modifiers);  break;
-
-          case SDLK_PLUS:
-          case SDLK_KP_PLUS:
-            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '+', modifiers);  break;
-
-          case SDLK_MINUS:
-          case SDLK_KP_MINUS:
-            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '-', modifiers);  break;
-
-          case SDLK_DELETE:
-            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Delete, 0, modifiers);  break;
-          case SDLK_BACKSPACE:
-            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Backspace, 0, modifiers);  break;
-          case SDLK_RIGHT:
-            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Right, 0, modifiers);  break;
-          case SDLK_LEFT:
-            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Left, 0, modifiers);  break;
-          case SDLK_UP:
-            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Up, 0, modifiers);  break;
-          case SDLK_DOWN:
-            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Down, 0, modifiers);  break;
-          default:
-            break;
-          }
-        }
-      }
-
-      // Small delay to avoid using 100% of CPU
-      SDL_Delay(1);
-    }
-  }
-}
-
-#endif
--- a/Applications/Sdl/SdlEngine.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/**
- * 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
-
-#if ORTHANC_ENABLE_SDL == 1
-
-#include "../../Framework/Messages/ObserverBase.h"
-#include "../Generic/NativeStoneApplicationContext.h"
-#include "SdlCairoSurface.h"
-
-namespace OrthancStone
-{
-  class SdlEngine : public ObserverBase<SdlEngine>
-  {
-  private:
-    SdlWindow&                window_;
-    NativeStoneApplicationContext&  context_;
-    SdlCairoSurface           surface_;
-    bool                      viewportChanged_;
-
-    void SetSize(unsigned int width,
-                 unsigned int height);
-    
-    void RenderFrame();
-
-    static KeyboardModifiers GetKeyboardModifiers(const uint8_t* keyboardState,
-                                                  const int scancodeCount);
-
-  public:
-    SdlEngine(SdlWindow& window,
-              NativeStoneApplicationContext& context);
-  
-    void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message)
-    {
-      viewportChanged_ = true;
-    }
-
-    void Run();
-  };
-}
-
-#endif
--- a/Applications/Sdl/SdlOrthancSurface.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "SdlOrthancSurface.h"
-
-#if ORTHANC_ENABLE_SDL == 1
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-#include <Core/Images/Image.h>
-
-#include <SDL_render.h>
-
-namespace OrthancStone
-{
-  SdlOrthancSurface::SdlOrthancSurface(SdlWindow& window) :
-    window_(window),
-    sdlSurface_(NULL)
-  {
-  }
-
-
-  SdlOrthancSurface::~SdlOrthancSurface()
-  {
-    if (sdlSurface_)
-    {
-      SDL_FreeSurface(sdlSurface_);
-    }
-  }
-
-
-  void SdlOrthancSurface::SetSize(unsigned int width,
-                                  unsigned int height)
-  {
-    if (image_.get() == NULL ||
-        image_->GetWidth() != width ||
-        image_->GetHeight() != height)
-    {
-      image_.reset(new Orthanc::Image(Orthanc::PixelFormat_BGRA32, width, height, true));  // (*)
-
-      if (image_->GetPitch() != image_->GetWidth() * 4)
-      {
-        // This should have been ensured by setting "forceMinimalPitch" to "true" (*)
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-
-      // TODO Big endian?
-      static const uint32_t rmask = 0x00ff0000;
-      static const uint32_t gmask = 0x0000ff00;
-      static const uint32_t bmask = 0x000000ff;
-
-      if (sdlSurface_)
-      {
-        SDL_FreeSurface(sdlSurface_);
-      }
-
-      sdlSurface_ = SDL_CreateRGBSurfaceFrom(image_->GetBuffer(), width, height, 32,
-                                             image_->GetPitch(), rmask, gmask, bmask, 0);
-      if (!sdlSurface_)
-      {
-        LOG(ERROR) << "Cannot create a SDL surface from a Orthanc surface";
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-    }
-  }
-
-
-  Orthanc::ImageAccessor& SdlOrthancSurface::GetImage()
-  {
-    if (image_.get() == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-
-    return *image_;
-  }
-
-
-  void SdlOrthancSurface::Render()
-  {
-    if (image_.get() == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-
-    window_.Render(sdlSurface_);
-  }
-}
-
-#endif
--- a/Applications/Sdl/SdlOrthancSurface.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/**
- * 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
-
-#if ORTHANC_ENABLE_SDL == 1
-
-#include "../../Framework/Viewport/SdlWindow.h"
-
-#include <Core/Compatibility.h>
-#include <Core/Images/ImageAccessor.h>
-
-#include <boost/thread/mutex.hpp>
-
-namespace OrthancStone
-{
-  class SdlOrthancSurface : public boost::noncopyable
-  {
-  private:
-    std::unique_ptr<Orthanc::ImageAccessor>  image_;
-    SdlWindow&                             window_;
-    SDL_Surface*                           sdlSurface_;
-
-  public:
-    SdlOrthancSurface(SdlWindow& window);
-
-    ~SdlOrthancSurface();
-
-    void SetSize(unsigned int width,
-                 unsigned int height);
-
-    Orthanc::ImageAccessor& GetImage();
-
-    void Render();
-  };
-}
-
-#endif
--- a/Applications/Sdl/SdlStoneApplicationRunner.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#if ORTHANC_ENABLE_SDL != 1
-#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 1
-#endif
-
-#include "SdlStoneApplicationRunner.h"
-
-#include "../../Platforms/Generic/OracleWebService.h"
-#include "SdlEngine.h"
-
-#include <Core/Logging.h>
-#include <Core/HttpClient.h>
-#include <Core/Toolbox.h>
-#include <Core/OrthancException.h>
-#include <Plugins/Samples/Common/OrthancHttpConnection.h>
-
-#include <boost/program_options.hpp>
-
-namespace OrthancStone
-{
-  void SdlStoneApplicationRunner::Initialize()
-  {
-    SdlWindow::GlobalInitialize();
-  }
-
-  
-  void SdlStoneApplicationRunner::DeclareCommandLineOptions(boost::program_options::options_description& options)
-  {
-    boost::program_options::options_description sdl("SDL options");
-    sdl.add_options()
-        ("width", boost::program_options::value<int>()->default_value(1024), "Initial width of the SDL window")
-        ("height", boost::program_options::value<int>()->default_value(768), "Initial height of the SDL window")
-        ("opengl", boost::program_options::value<bool>()->default_value(true), "Enable OpenGL in SDL")
-        ;
-
-    options.add(sdl);
-  }
-
-  
-  void SdlStoneApplicationRunner::ParseCommandLineOptions(const boost::program_options::variables_map& parameters)
-  {
-    if (!parameters.count("width") ||
-        !parameters.count("height") ||
-        !parameters.count("opengl"))
-    {
-      LOG(ERROR) << "Parameter \"width\", \"height\" or \"opengl\" is missing";
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    int w = parameters["width"].as<int>();
-    int h = parameters["height"].as<int>();
-    if (w <= 0 || h <= 0)
-    {
-      LOG(ERROR) << "Parameters \"width\" and \"height\" must be positive";
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    width_ = static_cast<unsigned int>(w);
-    height_ = static_cast<unsigned int>(h);
-    LOG(WARNING) << "Initial display size: " << width_ << "x" << height_;
-
-    enableOpenGl_ = parameters["opengl"].as<bool>();
-    if (enableOpenGl_)
-    {
-      LOG(WARNING) << "OpenGL is enabled, disable it with option \"--opengl=off\" if the application crashes";
-    }
-    else
-    {
-      LOG(WARNING) << "OpenGL is disabled, enable it with option \"--opengl=on\" for best performance";
-    }
-  }
-
-  
-  void SdlStoneApplicationRunner::Run(NativeStoneApplicationContext& context,
-                                      const std::string& title,
-                                      int argc,
-                                      char* argv[])
-  {
-    /**************************************************************
-     * Run the application inside a SDL window
-     **************************************************************/
-
-    LOG(WARNING) << "Starting the application";
-
-    SdlWindow window(title.c_str(), width_, height_, enableOpenGl_);
-    boost::shared_ptr<SdlEngine> sdl(new SdlEngine(window, context));
-
-    {
-      NativeStoneApplicationContext::GlobalMutexLocker locker(context);
-
-      sdl->Register<Deprecated::IViewport::ViewportChangedMessage>
-        (locker.GetCentralViewport(), &SdlEngine::OnViewportChanged);
-
-      //context.GetCentralViewport().Register(sdl);  // (*)
-    }
-
-    context.Start();
-    sdl->Run();
-
-    LOG(WARNING) << "Stopping the application";
-
-    // Don't move the "Stop()" command below out of the block,
-    // otherwise the application might crash, because the
-    // "SdlEngine" is an observer of the viewport (*) and the
-    // update thread started by "context.Start()" would call a
-    // destructed object (the "SdlEngine" is deleted with the
-    // lexical scope).
-
-    // TODO Is this still true with message broker?
-    context.Stop();
-  }
-
-  
-  void SdlStoneApplicationRunner::Finalize()
-  {
-    SdlWindow::GlobalFinalize();
-  }
-}
--- a/Applications/Sdl/SdlStoneApplicationRunner.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/**
- * 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 "../Generic/NativeStoneApplicationRunner.h"
-
-#if ORTHANC_ENABLE_SDL != 1
-#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 1
-#endif
-
-#include <SDL.h>   // Necessary to avoid undefined reference to `SDL_main'
-
-namespace OrthancStone
-{
-  class SdlStoneApplicationRunner : public NativeStoneApplicationRunner
-  {
-  private:
-    unsigned int  width_;
-    unsigned int  height_;
-    bool          enableOpenGl_;
-    
-  public:
-    SdlStoneApplicationRunner(boost::shared_ptr<IStoneApplication> application) :
-      NativeStoneApplicationRunner(application)
-    {
-    }
-
-    virtual void Initialize();
-    
-    virtual void DeclareCommandLineOptions(boost::program_options::options_description& options);
-    
-    virtual void Run(NativeStoneApplicationContext& context,
-                     const std::string& title,
-                     int argc,
-                     char* argv[]);
-    
-    virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters);
-    
-    virtual void Finalize();
-  };
-}
--- a/Applications/StoneApplicationContext.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "StoneApplicationContext.h"
-
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  void StoneApplicationContext::InitializeOrthanc()
-  {
-    if (webService_ == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-
-    orthanc_.reset(new Deprecated::OrthancApiClient(*webService_, orthancBaseUrl_));
-  }
-
-
-  boost::shared_ptr<Deprecated::IWebService> StoneApplicationContext::GetWebService()
-  {
-    if (webService_ == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    
-    return webService_;
-  }
-
-  
-  boost::shared_ptr<Deprecated::OrthancApiClient> StoneApplicationContext::GetOrthancApiClient()
-  {
-    if (orthanc_.get() == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    
-    return orthanc_;
-  }
-
-  
-  void StoneApplicationContext::SetWebService(boost::shared_ptr<Deprecated::IWebService> webService)
-  {
-    webService_ = webService;
-    InitializeOrthanc();
-  }
-
-
-  void StoneApplicationContext::SetOrthancBaseUrl(const std::string& baseUrl)
-  {
-    // Make sure the base url ends with "/"
-    if (baseUrl.empty() ||
-        baseUrl[baseUrl.size() - 1] != '/')
-    {
-      orthancBaseUrl_ = baseUrl + "/";
-    }
-    else
-    {
-      orthancBaseUrl_ = baseUrl;
-    }
-
-    if (webService_ != NULL)
-    {
-      InitializeOrthanc();
-    }
-  }
-}
--- a/Applications/StoneApplicationContext.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-/**
- * 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 "../Framework/Deprecated/Toolbox/IWebService.h"
-#include "../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
-#include "../Framework/Deprecated/Toolbox/OrthancApiClient.h"
-#include "../Framework/Deprecated/Viewport/WidgetViewport.h"
-
-
-#ifdef _MSC_VER
-  #if _MSC_VER > 1910
-    #define orthanc_override override
-  #else
-    #define orthanc_override
-  #endif
-#elif defined __GNUC__
-  #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
-/* Test for GCC > 3.2.0 */
-  #if GCC_VERSION > 40900
-    #define orthanc_override override
-  #else
-    #define orthanc_override
-  #endif
-#else
-    #define orthanc_override
-#endif
-
-#include <list>
-
-namespace OrthancStone
-{
-  // a StoneApplicationContext contains the services that a StoneApplication
-  // uses and that depends on the environment in which the Application executes.
-  // I.e, the StoneApplicationContext provides a WebService interface such that
-  // the StoneApplication can perform HTTP requests.  In a WASM environment,
-  // the WebService is provided by the browser while, in a native environment,
-  // the WebService is provided by the OracleWebService (a C++ Http client)
-
-  class StoneApplicationContext : public boost::noncopyable
-  {
-  private:
-    boost::shared_ptr<Deprecated::IWebService>     webService_;
-    Deprecated::IDelayedCallExecutor*  delayedCallExecutor_;   // TODO => shared_ptr ??
-    boost::shared_ptr<Deprecated::OrthancApiClient>  orthanc_;
-    std::string                      orthancBaseUrl_;
-
-    void InitializeOrthanc();
-
-  public:
-    StoneApplicationContext() :
-      delayedCallExecutor_(NULL)
-    {
-    }
-
-    virtual ~StoneApplicationContext()
-    {
-    }
-
-    boost::shared_ptr<Deprecated::IWebService> GetWebService();
-
-    boost::shared_ptr<Deprecated::OrthancApiClient> GetOrthancApiClient();
-
-    void SetWebService(boost::shared_ptr<Deprecated::IWebService> webService);
-
-    void SetOrthancBaseUrl(const std::string& baseUrl);
-
-    void SetDelayedCallExecutor(Deprecated::IDelayedCallExecutor& delayedCallExecutor)
-    {
-      delayedCallExecutor_ = &delayedCallExecutor;
-    }
-
-    Deprecated::IDelayedCallExecutor& GetDelayedCallExecutor()
-    {
-      return *delayedCallExecutor_;
-    }
-  };
-}
--- a/Applications/Wasm/StartupParametersBuilder.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-#include "StartupParametersBuilder.h"
-#include <iostream>
-#include <cstdio>
-#include "emscripten/html5.h"
-
-namespace OrthancStone
-{
-  void StartupParametersBuilder::Clear()
-  {
-    startupParameters_.clear();
-  }
-
-  void StartupParametersBuilder::SetStartupParameter(
-    const char* name,
-    const char* value)
-  {
-    startupParameters_.push_back(std::make_tuple(name, value));
-  }
-
-  void StartupParametersBuilder::GetStartupParameters(
-    boost::program_options::variables_map& parameters,
-    const boost::program_options::options_description& options) 
-  {
-    std::vector<std::string> argvStrings(startupParameters_.size() + 1);
-    // argv mirrors pointers to the internal argvStrings buffers.
-    // ******************************************************
-    // THIS IS HIGHLY DANGEROUS SO BEWARE!!!!!!!!!!!!!!
-    // ******************************************************
-    std::vector<const char*> argv(startupParameters_.size() + 1);
-    
-    int argCounter = 0;
-    argvStrings[argCounter] = "dummy.exe";
-    argv[argCounter] = argvStrings[argCounter].c_str();
-    
-    argCounter++;
-    
-    std::string cmdLine = "";
-    for ( StartupParameters::const_iterator it = startupParameters_.begin(); 
-          it != startupParameters_.end(); 
-          it++)
-    {
-      std::stringstream argSs;
-
-      argSs << "--" << std::get<0>(*it);
-      if(std::get<1>(*it).length() > 0)
-        argSs << "=" << std::get<1>(*it);
-      
-      argvStrings[argCounter] = argSs.str();
-      cmdLine = cmdLine + " " + argvStrings[argCounter];
-      std::cout << cmdLine << std::endl;
-      argv[argCounter] = argvStrings[argCounter].c_str();
-      argCounter++;
-    }
-
-
-    std::cout << "simulated cmdLine = \"" << cmdLine.c_str() << "\"\n";
-
-    try
-    {
-      boost::program_options::store(
-        boost::program_options::command_line_parser(argCounter, argv.data()).
-          options(options).allow_unregistered().run(), parameters);
-      boost::program_options::notify(parameters);
-    }
-    catch (boost::program_options::error& e)
-    {
-      std::cerr << "Error while parsing the command-line arguments: " <<
-        e.what() << std::endl;    
-    }
-  }
-}
--- a/Applications/Wasm/StartupParametersBuilder.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/**
- * 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 <boost/program_options.hpp>
-#include <tuple>
-
-#if ORTHANC_ENABLE_SDL == 1
-#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 0
-#endif
-
-namespace OrthancStone
-{
-  // This class is used to generate boost program options from a dico.
-  // In a Wasm context, startup options are passed as URI arguments that
-  // are then passed to this class as a dico.
-  // This class regenerates a fake command-line and parses it to produce
-  // the same output as if the app was started at command-line.
-  class StartupParametersBuilder
-  {
-    typedef std::list<std::tuple<std::string, std::string>> StartupParameters;
-    StartupParameters startupParameters_;
-
-  public:
-
-    void Clear();
-    // Please note that if a parameter is a flag-style one, the value that 
-    // is passed should be an empty string
-    void SetStartupParameter(const char* name, const char* value);
-    void GetStartupParameters(
-      boost::program_options::variables_map& parameters_, 
-      const boost::program_options::options_description& options);
-  };
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Commands/BaseCommands.yml	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,10 @@
+SelectTool:
+  target: Application
+  toolName: string
+  comment: Selects the current application tool
+DownloadDicom:
+  target: SliceViewerWidget
+  comment: Downloads the slice currently displayed in the SliceViewerWidget
+Export:
+  target: IWidget
+  comment: Export the content of the widget
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Generic/GuiAdapter.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,1142 @@
+/**
+ * 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/>.
+ **/
+
+#include "GuiAdapter.h"
+
+#if ORTHANC_ENABLE_OPENGL == 1
+#  include "../../Framework/OpenGL/OpenGLIncludes.h"
+#endif
+
+#if ORTHANC_ENABLE_SDL == 1
+#  include <SDL_video.h>
+#  include <SDL_render.h>
+#  include <SDL.h>
+#endif
+
+#if ORTHANC_ENABLE_THREADS == 1
+#  include "../../Framework/Deprecated/Messages/LockingEmitter.h"
+#endif
+
+#include <Core/Compatibility.h>
+
+namespace OrthancStone
+{
+  std::ostream& operator<<(
+    std::ostream& os, const GuiAdapterKeyboardEvent& event)
+  {
+    os << "sym: " << event.sym << " (" << (int)(event.sym[0]) << ") ctrl: " << event.ctrlKey << ", " <<
+      "shift: " << event.shiftKey << ", " <<
+      "alt: " << event.altKey;
+    return os;
+  }
+
+  std::ostream& operator<<(
+    std::ostream& os, const GuiAdapterMouseEvent& event)
+  {
+    os << "targetX: " << event.targetX << " targetY: " << event.targetY << " button: " << event.button
+      << "ctrlKey: " << event.ctrlKey << "shiftKey: " << event.shiftKey << "altKey: " << event.altKey;
+      
+    return os;
+  }
+
+#if ORTHANC_ENABLE_WASM == 1
+  void GuiAdapter::Run(GuiAdapterRunFunc /*func*/, void* /*cookie*/)
+  {
+  }
+
+  void ConvertFromPlatform(
+    GuiAdapterUiEvent& dest,
+    int                      eventType,
+    const EmscriptenUiEvent& src)
+  {
+    // no data for now
+  }
+
+  void ConvertFromPlatform(
+    GuiAdapterMouseEvent& dest,
+    int                         eventType,
+    const EmscriptenMouseEvent& src)
+  {
+    memset(&dest, 0, sizeof(GuiAdapterMouseEvent));
+    switch (eventType)
+    {
+    case EMSCRIPTEN_EVENT_CLICK:
+      LOG(ERROR) << "Emscripten EMSCRIPTEN_EVENT_CLICK is not supported";
+      ORTHANC_ASSERT(false, "Not supported");
+      break;
+    case EMSCRIPTEN_EVENT_MOUSEDOWN:
+      dest.type = GUIADAPTER_EVENT_MOUSEDOWN;
+      break;
+    case EMSCRIPTEN_EVENT_DBLCLICK:
+      dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK;
+      break;
+    case EMSCRIPTEN_EVENT_MOUSEMOVE:
+      dest.type = GUIADAPTER_EVENT_MOUSEMOVE;
+      break;
+    case EMSCRIPTEN_EVENT_MOUSEUP:
+      dest.type = GUIADAPTER_EVENT_MOUSEUP;
+      break;
+    case EMSCRIPTEN_EVENT_WHEEL:
+      dest.type = GUIADAPTER_EVENT_WHEEL;
+      break;
+
+    default:
+      LOG(ERROR) << "Emscripten event: " << eventType << " is not supported";
+      ORTHANC_ASSERT(false, "Not supported");
+    }
+    //dest.timestamp = src.timestamp;
+    //dest.screenX = src.screenX;
+    //dest.screenY = src.screenY;
+    //dest.clientX = src.clientX;
+    //dest.clientY = src.clientY;
+    dest.ctrlKey = src.ctrlKey;
+    dest.shiftKey = src.shiftKey;
+    dest.altKey = src.altKey;
+    //dest.metaKey = src.metaKey;
+    dest.button = src.button;
+    //dest.buttons = src.buttons;
+    //dest.movementX = src.movementX;
+    //dest.movementY = src.movementY;
+    dest.targetX = src.targetX;
+    dest.targetY = src.targetY;
+    //dest.canvasX = src.canvasX;
+    //dest.canvasY = src.canvasY;
+    //dest.padding = src.padding;
+  }
+
+  void ConvertFromPlatform( GuiAdapterWheelEvent& dest, int eventType, const EmscriptenWheelEvent& src)
+  {
+    ConvertFromPlatform(dest.mouse, eventType, src.mouse);
+    dest.deltaX = src.deltaX;
+    dest.deltaY = src.deltaY;
+    switch (src.deltaMode)
+    {
+    case DOM_DELTA_PIXEL:
+      dest.deltaMode = GUIADAPTER_DELTA_PIXEL;
+      break;
+    case DOM_DELTA_LINE:
+      dest.deltaMode = GUIADAPTER_DELTA_LINE;
+      break;
+    case DOM_DELTA_PAGE:
+      dest.deltaMode = GUIADAPTER_DELTA_PAGE;
+      break;
+    default:
+      ORTHANC_ASSERT(false, "Unknown deltaMode: " << src.deltaMode <<
+        " in wheel event...");
+    }
+    dest.deltaMode = src.deltaMode;
+  }
+
+  void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const EmscriptenKeyboardEvent& src)
+  {
+    dest.sym[0] = src.key[0];
+    dest.sym[1] = 0;
+    dest.ctrlKey = src.ctrlKey;
+    dest.shiftKey = src.shiftKey;
+    dest.altKey = src.altKey;
+  }
+
+  template<typename GenericFunc>
+  struct FuncAdapterPayload
+  {
+    std::string canvasCssSelector;
+    void* userData;
+    GenericFunc callback;
+  };
+
+  template<typename GenericFunc,
+    typename GuiAdapterEvent,
+    typename EmscriptenEvent>
+    EM_BOOL OnEventAdapterFunc(
+      int eventType, const EmscriptenEvent* emEvent, void* userData)
+  {
+    // userData is OnMouseWheelFuncAdapterPayload
+    FuncAdapterPayload<GenericFunc>* payload =
+      reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData);
+    // LOG(INFO) << "OnEventAdapterFunc";
+    // LOG(INFO) << "------------------";
+    // LOG(INFO) << "eventType: " << eventType << " wheelEvent: " << 
+    //   (int)wheelEvent << " userData: " << userData << 
+    //   " payload->userData: " << payload->userData;
+
+    GuiAdapterEvent guiEvent;
+    ConvertFromPlatform(guiEvent, eventType, *emEvent);
+    bool ret = (*(payload->callback))(payload->canvasCssSelector, &guiEvent, payload->userData);
+    return static_cast<EM_BOOL>(ret);
+  }
+
+  template<typename GenericFunc,
+    typename GuiAdapterEvent,
+    typename EmscriptenEvent>
+    EM_BOOL OnEventAdapterFunc2(
+      int /*eventType*/, const EmscriptenEvent* wheelEvent, void* userData)
+  {
+    // userData is OnMouseWheelFuncAdapterPayload
+    FuncAdapterPayload<GenericFunc>* payload =
+      reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData);
+
+    GuiAdapterEvent guiEvent;
+    ConvertFromPlatform(guiEvent, *wheelEvent);
+    bool ret = (*(payload->callback))(payload->canvasCssSelector, &guiEvent, payload->userData);
+    return static_cast<EM_BOOL>(ret);
+  }
+
+  template<typename GenericFunc>
+  EM_BOOL OnEventAdapterFunc3(
+    double time, void* userData)
+  {
+    // userData is OnMouseWheelFuncAdapterPayload
+    FuncAdapterPayload<GenericFunc>* payload =
+      reinterpret_cast<FuncAdapterPayload<GenericFunc>*>(userData);
+    //std::unique_ptr< FuncAdapterPayload<GenericFunc> > deleter(payload);
+    bool ret = (*(payload->callback))(time, payload->userData);
+    return static_cast<EM_BOOL>(ret);
+  }
+
+  /*
+  
+  Explanation
+  ===========
+
+  - in "older" Emscripten, where DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR doesn't exist or is set to 0,
+    the following strings need to be used to register events:
+    - for canvas, the canvas DOM id. In case of <canvas id="mycanvas1" width='640' ...></canvas>", the string needs 
+      to be "mycanvas"
+    - for the window (for key events), the string needs to be "#window"
+  - in newer Emscripten where DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR==1 (or maybe is not there anymore, in the
+    future as of 2020-04-20)
+    - for canvas, the canvas DOM id. In case of <canvas id="mycanvas1" width='640' ...></canvas>", the string needs
+      to be "#mycanvas"  (notice the "number sign", aka "hash", NOT AKA "sharp", as can be read on https://en.wikipedia.org/wiki/Number_sign)
+    - for the window (for key events), the string needs to be EMSCRIPTEN_EVENT_TARGET_WINDOW. I do not mean 
+      "EMSCRIPTEN_EVENT_TARGET_WINDOW", but the #define EMSCRIPTEN_EVENT_TARGET_WINDOW         ((const char*)2) that
+      can be found in emscripten/html5.h
+
+  The code below converts the input canvasId (as in the old emscripten) to the emscripten-compliant one, with the
+  following compile condition : #if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR == 1
+
+  If the DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR build parameter disappears, you might want to refactor this code
+  or continue to pass the DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR compile macro (which is different from the CMake
+  variable)     
+
+  What we are doing below:
+  - in older Emscripten, the registration functions will receive "mycanvas" and "#window" and the callbacks will receive 
+    the same std::string in their payload ("mycanvas" and "#window")
+
+  - in newer Emscripten, the registration functions will receive "#mycanvas" and EMSCRIPTEN_EVENT_TARGET_WINDOW, but 
+    the callbacks will receive "#mycanvas" and "#window" (since it is not possible to store the EMSCRIPTEN_EVENT_TARGET_WINDOW
+    magic value in an std::string, while we still want the callback to be able to change its behavior according to the
+    target element.
+  
+  */
+
+  void convertElementTarget(const char*& outCanvasCssSelectorSz, std::string& outCanvasCssSelector, const std::string& canvasId)
+  {
+    // only "#window" can start with a #
+    if (canvasId[0] == '#')
+    {
+      ORTHANC_ASSERT(canvasId == "#window");
+    }
+#if DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR == 1
+    if (canvasId == "#window")
+    {
+      // we store this in the payload so that the callback can 
+      outCanvasCssSelector = "#window";
+      outCanvasCssSelectorSz = EMSCRIPTEN_EVENT_TARGET_WINDOW;
+    }
+    else
+    {
+      outCanvasCssSelector = "#" + canvasId;
+      outCanvasCssSelectorSz = outCanvasCssSelector.c_str();
+    }
+#else
+    if (canvasId == "#window")
+    {
+      // we store this in the payload so that the callback can 
+      outCanvasCssSelector = "#window";
+      outCanvasCssSelectorSz = outCanvasCssSelector.c_str();;
+    }
+    else
+    {
+      outCanvasCssSelector = canvasId;
+      outCanvasCssSelectorSz = outCanvasCssSelector.c_str();;
+    }
+#endif
+  }
+
+  // resize: (const char* target, void* userData, EM_BOOL useCapture, em_ui_callback_func callback)
+  template<
+    typename GenericFunc,
+    typename GuiAdapterEvent,
+    typename EmscriptenEvent,
+    typename EmscriptenSetCallbackFunc>
+    static void SetCallback(
+      EmscriptenSetCallbackFunc emFunc,
+      std::string canvasId, void* userData, bool capture, GenericFunc func)
+  {
+    std::string canvasCssSelector;
+    const char* canvasCssSelectorSz = NULL;
+    convertElementTarget(canvasCssSelectorSz, canvasCssSelector, canvasId);
+
+    // TODO: write RemoveCallback with an int id that gets returned from here
+
+    // create userdata payload
+    std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload(new FuncAdapterPayload<GenericFunc>());
+    payload->canvasCssSelector = canvasCssSelector;
+    payload->callback = func;
+    payload->userData = userData;
+    void* userDataRaw = reinterpret_cast<void*>(payload.release());
+
+    // call the registration function
+    (*emFunc)(
+      canvasCssSelectorSz,
+      userDataRaw,
+      static_cast<EM_BOOL>(capture),
+      &OnEventAdapterFunc<GenericFunc, GuiAdapterEvent, EmscriptenEvent>,
+      EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD);
+  }
+
+  template<
+    typename GenericFunc,
+    typename GuiAdapterEvent,
+    typename EmscriptenEvent,
+    typename EmscriptenSetCallbackFunc>
+    static void SetCallback2(
+      EmscriptenSetCallbackFunc emFunc,
+      std::string canvasId, void* userData, bool capture, GenericFunc func)
+  {
+    std::string canvasCssSelector;
+    const char* canvasCssSelectorSz = NULL;
+    convertElementTarget(canvasCssSelectorSz, canvasCssSelector, canvasId);
+
+    // TODO: write RemoveCallback with an int id that gets returned from here
+
+    // create userdata payload
+    std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload(new FuncAdapterPayload<GenericFunc>());
+    payload->canvasCssSelector = canvasCssSelector;
+    payload->callback = func;
+    payload->userData = userData;
+    void* userDataRaw = reinterpret_cast<void*>(payload.release());
+
+    // call the registration function
+    (*emFunc)(
+      canvasCssSelectorSz,
+      userDataRaw,
+      static_cast<EM_BOOL>(capture),
+      &OnEventAdapterFunc2<GenericFunc, GuiAdapterEvent, EmscriptenEvent>,
+      EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD);
+  }
+
+  template<
+    typename GenericFunc,
+    typename EmscriptenSetCallbackFunc>
+    static void SetAnimationFrameCallback(
+      EmscriptenSetCallbackFunc emFunc,
+      void* userData, GenericFunc func)
+  {
+    std::unique_ptr<FuncAdapterPayload<GenericFunc> > payload(
+      new FuncAdapterPayload<GenericFunc>()
+    );
+    payload->canvasCssSelector = "UNDEFINED";
+    payload->callback = func;
+    payload->userData = userData;
+    void* userDataRaw = reinterpret_cast<void*>(payload.release());
+    (*emFunc)(
+      &OnEventAdapterFunc3<GenericFunc>,
+      userDataRaw);
+  }
+
+  void GuiAdapter::SetWheelCallback(
+    std::string canvasId, void* userData, bool capture, OnMouseWheelFunc func)
+  {
+    SetCallback<OnMouseWheelFunc, GuiAdapterWheelEvent, EmscriptenWheelEvent>(
+      &emscripten_set_wheel_callback_on_thread,
+      canvasId,
+      userData,
+      capture,
+      func);
+  }
+
+  
+  void GuiAdapter::SetMouseDblClickCallback(
+      std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
+  {
+    SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>(
+      &emscripten_set_dblclick_callback_on_thread,
+      canvasId,
+      userData,
+      capture,
+      func);
+  }
+
+
+  void GuiAdapter::SetMouseDownCallback(
+    std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
+  {
+    SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>(
+      &emscripten_set_mousedown_callback_on_thread,
+      canvasId,
+      userData,
+      capture,
+      func);
+  }
+
+  void GuiAdapter::SetMouseMoveCallback(
+    std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
+  {
+    // LOG(INFO) << "SetMouseMoveCallback -- " << "supplied userData: " << 
+    //   userData;
+
+    SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>(
+      &emscripten_set_mousemove_callback_on_thread,
+      canvasId,
+      userData,
+      capture,
+      func);
+  }
+
+  void GuiAdapter::SetMouseUpCallback(
+    std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
+  {
+    SetCallback<OnMouseEventFunc, GuiAdapterMouseEvent, EmscriptenMouseEvent>(
+      &emscripten_set_mouseup_callback_on_thread,
+      canvasId,
+      userData,
+      capture,
+      func);
+  }
+
+  void GuiAdapter::SetKeyDownCallback(
+    std::string canvasId, void* userData, bool capture, OnKeyDownFunc func)
+  {
+    SetCallback2<OnKeyDownFunc, GuiAdapterKeyboardEvent, EmscriptenKeyboardEvent>(
+      &emscripten_set_keydown_callback_on_thread,
+      canvasId,
+      userData,
+      capture,
+      func);
+  }
+
+  void GuiAdapter::SetKeyUpCallback(
+    std::string canvasId, void* userData, bool capture, OnKeyUpFunc func)
+  {
+    SetCallback2<OnKeyUpFunc, GuiAdapterKeyboardEvent, EmscriptenKeyboardEvent>(
+      &emscripten_set_keyup_callback_on_thread,
+      canvasId,
+      userData,
+      capture,
+      func);
+  }
+
+#if 0
+  // useless under Wasm where canvas resize is handled automatically
+  void GuiAdapter::SetResizeCallback(
+    std::string canvasId, void* userData, bool capture, OnWindowResizeFunc func)
+  {
+    SetCallback<OnWindowResizeFunc, GuiAdapterUiEvent, EmscriptenUiEvent>(
+      &emscripten_set_resize_callback_on_thread,
+      canvasId,
+      userData,
+      capture,
+      func);
+  }
+#endif
+
+  void GuiAdapter::RequestAnimationFrame(
+    OnAnimationFrameFunc func, void* userData)
+  {
+    SetAnimationFrameCallback<OnAnimationFrameFunc>(
+      &emscripten_request_animation_frame_loop,
+      userData,
+      func);
+  }
+
+#if 0
+  void GuiAdapter::SetKeyDownCallback(
+    std::string canvasId, void* userData, bool capture, OnKeyDownFunc func)
+  {
+    emscripten_set_keydown_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func);
+  }
+  void GuiAdapter::SetKeyUpCallback(
+    std::string canvasId, void* userData, bool capture, OnKeyUpFunc func)
+  {
+    emscripten_set_keyup_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func);
+  }
+
+  // handled from within WebAssemblyViewport
+  //void GuiAdapter::SetResizeCallback(std::string canvasId, void* userData, bool capture, OnWindowResizeFunc func)
+  //{
+  //  emscripten_set_resize_callback(canvasId.c_str(), userData, static_cast<EM_BOOL>(capture), func);
+  //}
+
+  void GuiAdapter::RequestAnimationFrame(OnAnimationFrameFunc func, void* userData)
+  {
+    emscripten_request_animation_frame_loop(func, userData);
+  }
+#endif
+
+
+#else
+
+  // SDL ONLY
+  void ConvertFromPlatform(GuiAdapterMouseEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, const SDL_Event& source)
+  {
+    memset(&dest, 0, sizeof(GuiAdapterMouseEvent));
+    switch (source.type)
+    {
+    case SDL_MOUSEBUTTONDOWN:
+      if (source.button.clicks == 1) {
+        dest.type = GUIADAPTER_EVENT_MOUSEDOWN;
+      } else if (source.button.clicks == 2) {
+        dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK;
+      } else {
+        dest.type = GUIADAPTER_EVENT_MOUSEDBLCLICK;
+        LOG(WARNING) << "Multiple-click ignored.";
+      }
+      break;
+    case SDL_MOUSEMOTION:
+      dest.type = GUIADAPTER_EVENT_MOUSEMOVE;
+      break;
+    case SDL_MOUSEBUTTONUP:
+      dest.type = GUIADAPTER_EVENT_MOUSEUP;
+      break;
+    case SDL_MOUSEWHEEL:
+      dest.type = GUIADAPTER_EVENT_WHEEL;
+      break;
+    default:
+      LOG(ERROR) << "SDL event: " << source.type << " is not supported";
+      ORTHANC_ASSERT(false, "Not supported");
+    }
+    //dest.timestamp = src.timestamp;
+    //dest.screenX = src.screenX;
+    //dest.screenY = src.screenY;
+    //dest.clientX = src.clientX;
+    //dest.clientY = src.clientY;
+    dest.ctrlKey = ctrlPressed;
+    dest.shiftKey = shiftPressed;
+    dest.altKey = altPressed;
+    //dest.metaKey = src.metaKey;
+    switch (source.button.button)
+    {
+    case SDL_BUTTON_MIDDLE:
+      dest.button =GUIADAPTER_MOUSEBUTTON_MIDDLE;
+      break;
+
+    case SDL_BUTTON_RIGHT:
+      dest.button = GUIADAPTER_MOUSEBUTTON_RIGHT;
+      break;
+
+    case SDL_BUTTON_LEFT:
+      dest.button = GUIADAPTER_MOUSEBUTTON_LEFT;
+      break;
+
+    default:
+      break;
+    }
+    //dest.buttons = src.buttons;
+    //dest.movementX = src.movementX;
+    //dest.movementY = src.movementY;
+    dest.targetX = source.button.x;
+    dest.targetY = source.button.y;
+    //dest.canvasX = src.canvasX;
+    //dest.canvasY = src.canvasY;
+    //dest.padding = src.padding;
+  }
+
+  void ConvertFromPlatform(
+    GuiAdapterWheelEvent& dest,
+    bool ctrlPressed, bool shiftPressed, bool altPressed,
+    const SDL_Event& source)
+  {
+    ConvertFromPlatform(dest.mouse, ctrlPressed, shiftPressed, altPressed, source);
+    dest.deltaX = source.wheel.x;
+    dest.deltaY = source.wheel.y;
+  }
+
+  void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const SDL_Event& src)
+  {
+    memset(&dest, 0, sizeof(GuiAdapterMouseEvent));
+    switch (src.type)
+    {
+    case SDL_KEYDOWN:
+      dest.type = GUIADAPTER_EVENT_KEYDOWN;
+      break;
+    case SDL_KEYUP:
+      dest.type = GUIADAPTER_EVENT_KEYUP;
+      break;
+    default:
+      LOG(ERROR) << "SDL event: " << src.type << " is not supported";
+      ORTHANC_ASSERT(false, "Not supported");
+    }
+    dest.sym[0] = src.key.keysym.sym;
+    dest.sym[1] = 0;
+
+    if (src.key.keysym.mod & KMOD_CTRL)
+      dest.ctrlKey = true;
+    else
+      dest.ctrlKey = false;
+
+    if (src.key.keysym.mod & KMOD_SHIFT)
+      dest.shiftKey = true;
+    else
+      dest.shiftKey = false;
+
+    if (src.key.keysym.mod & KMOD_ALT)
+      dest.altKey = true;
+    else
+      dest.altKey = false;
+  }
+
+  // SDL ONLY
+  void GuiAdapter::SetSdlResizeCallback(
+    std::string canvasId, void* userData, bool capture, OnSdlWindowResizeFunc func)
+  {
+    resizeHandlers_.push_back(EventHandlerData<OnSdlWindowResizeFunc>(canvasId, func, userData));
+  }
+
+  // SDL ONLY
+  void GuiAdapter::SetMouseDownCallback(
+    std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
+  {
+    mouseDownHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData));
+  }
+
+  // SDL ONLY
+  void GuiAdapter::SetMouseDblClickCallback(
+    std::string canvasId, void* userData, bool capture, OnMouseEventFunc func)
+  {
+    mouseDblCickHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData));
+  }
+
+  // SDL ONLY
+  void GuiAdapter::SetMouseMoveCallback(
+    std::string canvasId, void* userData, bool capture, OnMouseEventFunc  func)
+  {
+    mouseMoveHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData));
+  }
+
+  // SDL ONLY
+  void GuiAdapter::SetMouseUpCallback(
+    std::string canvasId, void* userData, bool capture, OnMouseEventFunc  func)
+  {
+    mouseUpHandlers_.push_back(EventHandlerData<OnMouseEventFunc>(canvasId, func, userData));
+  }
+
+  // SDL ONLY
+  void GuiAdapter::SetWheelCallback(
+    std::string canvasId, void* userData, bool capture, OnMouseWheelFunc  func)
+  {
+    mouseWheelHandlers_.push_back(EventHandlerData<OnMouseWheelFunc>(canvasId, func, userData));
+  }
+
+  // SDL ONLY
+  void GuiAdapter::SetKeyDownCallback(
+    std::string canvasId, void* userData, bool capture, OnKeyDownFunc   func)
+  {
+    keyDownHandlers_.push_back(EventHandlerData<OnKeyDownFunc>(canvasId, func, userData));
+  }
+
+  // SDL ONLY
+  void GuiAdapter::SetKeyUpCallback(
+    std::string canvasId, void* userData, bool capture, OnKeyUpFunc    func)
+  {
+    keyUpHandlers_.push_back(EventHandlerData<OnKeyUpFunc>(canvasId, func, userData));
+  }
+
+  // SDL ONLY
+  void GuiAdapter::SetGenericSdlEventCallback(
+    std::string canvasId, void* userData, bool capture, OnSdlEventCallback func)
+  {
+    sdlEventHandlers_.push_back(EventHandlerData<OnSdlEventCallback>(canvasId, func, userData));
+  }
+
+  // SDL ONLY
+  void GuiAdapter::OnAnimationFrame()
+  {
+    std::vector<size_t> disabledAnimationHandlers;
+    for (size_t i = 0; i < animationFrameHandlers_.size(); i++)
+    {
+      // TODO: fix time 
+      bool goOn = (*(animationFrameHandlers_[i].first))(0, animationFrameHandlers_[i].second);
+
+      // If the function returns false, we need to emulate what happens in Web 
+      // and remove the function from the handlers...
+      if (!goOn)
+        disabledAnimationHandlers.push_back(i);
+    }
+    for (size_t i = 0; i < disabledAnimationHandlers.size(); i++)
+    {
+      ORTHANC_ASSERT(animationFrameHandlers_.begin() + disabledAnimationHandlers[i] < animationFrameHandlers_.end());
+      animationFrameHandlers_.erase(animationFrameHandlers_.begin() + disabledAnimationHandlers[i]);
+    }
+  }
+
+  // SDL ONLY
+  void GuiAdapter::OnResize(unsigned int width, unsigned int height)
+  {
+    for (size_t i = 0; i < resizeHandlers_.size(); i++)
+    {
+      (*(resizeHandlers_[i].func))(
+        resizeHandlers_[i].canvasName, NULL, width, height, resizeHandlers_[i].userData);
+    }
+  }
+
+
+
+  void GuiAdapter::OnSdlGenericEvent(const SDL_Event& sdlEvent)
+  {
+    // Events related to a window are only sent to the related canvas
+    // User events are sent to everyone (we can't filter them here)
+    
+    /*
+    SDL_WindowEvent    SDL_WINDOWEVENT
+    SDL_KeyboardEvent     SDL_KEYDOWN
+                          SDL_KEYUP
+    SDL_TextEditingEvent  SDL_TEXTEDITING
+    SDL_TextInputEvent    SDL_TEXTINPUT
+    SDL_MouseMotionEvent  SDL_MOUSEMOTION
+    SDL_MouseButtonEvent  SDL_MOUSEBUTTONDOWN 
+                          SDL_MOUSEBUTTONUP
+    SDL_MouseWheelEvent   SDL_MOUSEWHEEL
+    SDL_UserEvent         SDL_USEREVENT through ::SDL_LASTEVENT-1
+    */
+
+    // if this string is left empty, it means the message will be sent to
+    // all widgets.
+    // otherwise, it contains the originating message window title
+
+    std::string windowTitle;
+    uint32_t windowId = 0;
+
+    if (sdlEvent.type == SDL_WINDOWEVENT)
+      windowId = sdlEvent.window.windowID;
+    else if (sdlEvent.type == SDL_KEYDOWN || sdlEvent.type == SDL_KEYUP)
+      windowId = sdlEvent.key.windowID;
+    else if (sdlEvent.type == SDL_TEXTEDITING)
+      windowId = sdlEvent.edit.windowID;
+    else if (sdlEvent.type == SDL_TEXTINPUT)
+      windowId = sdlEvent.text.windowID;
+    else if (sdlEvent.type == SDL_MOUSEMOTION)
+      windowId = sdlEvent.motion.windowID;
+    else if (sdlEvent.type == SDL_MOUSEBUTTONDOWN || sdlEvent.type == SDL_MOUSEBUTTONUP)
+      windowId = sdlEvent.button.windowID;
+    else if (sdlEvent.type == SDL_MOUSEWHEEL)
+      windowId = sdlEvent.wheel.windowID;
+    else if (sdlEvent.type >= SDL_USEREVENT && sdlEvent.type <= (SDL_LASTEVENT-1))
+      windowId = sdlEvent.user.windowID;
+
+    if (windowId != 0)
+    {
+      SDL_Window* sdlWindow = SDL_GetWindowFromID(windowId);
+      ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowId << "\" is not a valid SDL window ID!");
+      const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
+      ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowId << "\" has a NULL window title!");
+      windowTitle = windowTitleSz;
+      ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowId << "\" has an empty window title!");
+    }
+
+    for (size_t i = 0; i < sdlEventHandlers_.size(); i++)
+    {
+      // normally, the handlers return a bool indicating whether they
+      // have handled the event or not, but we don't really care about this
+      std::string& canvasName = sdlEventHandlers_[i].canvasName;
+      
+      bool sendEvent = true;
+
+      if (windowTitle != "" && (canvasName != windowTitle))
+        sendEvent = false;
+
+      if (sendEvent)
+      {
+        OnSdlEventCallback func = sdlEventHandlers_[i].func;
+        (*func)(canvasName, sdlEvent, sdlEventHandlers_[i].userData);
+      }
+    }
+  }
+
+  // SDL ONLY
+  void GuiAdapter::OnMouseWheelEvent(uint32_t windowID, const GuiAdapterWheelEvent& event)
+  {
+    // the SDL window name IS the canvas name ("canvas" is used because this lib
+    // is designed for Wasm
+    SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID);
+    ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!");
+
+    const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
+    ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!");
+
+    std::string windowTitle(windowTitleSz);
+    ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!");
+
+    switch (event.mouse.type)
+    {
+    case GUIADAPTER_EVENT_WHEEL:
+      for (size_t i = 0; i < mouseWheelHandlers_.size(); i++)
+      {
+        if (mouseWheelHandlers_[i].canvasName == windowTitle)
+          (*(mouseWheelHandlers_[i].func))(windowTitle, &event, mouseWheelHandlers_[i].userData);
+      }
+      break;
+    default:
+      ORTHANC_ASSERT(false, "Wrong event.type: " << event.mouse.type << " in GuiAdapter::OnMouseWheelEvent(...)");
+      break;
+    }
+  }
+
+
+  void GuiAdapter::OnKeyboardEvent(uint32_t windowID, const GuiAdapterKeyboardEvent& event)
+  {
+    // only one-letter (ascii) keyboard events supported for now
+    ORTHANC_ASSERT(event.sym[0] != 0);
+    ORTHANC_ASSERT(event.sym[1] == 0);
+
+    SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID);
+    ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!");
+
+    const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
+    ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!");
+
+    std::string windowTitle(windowTitleSz);
+    ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!");
+
+    switch (event.type)
+    {
+    case GUIADAPTER_EVENT_KEYDOWN:
+      for (size_t i = 0; i < keyDownHandlers_.size(); i++)
+      {
+        (*(keyDownHandlers_[i].func))(windowTitle, &event, keyDownHandlers_[i].userData);
+      }
+      break;
+    case GUIADAPTER_EVENT_KEYUP:
+      for (size_t i = 0; i < keyUpHandlers_.size(); i++)
+      {
+        (*(keyUpHandlers_[i].func))(windowTitle, &event, keyUpHandlers_[i].userData);
+      }
+      break;
+    default:
+      ORTHANC_ASSERT(false, "Wrong event.type: " << event.type << " in GuiAdapter::OnKeyboardEvent(...)");
+      break;
+    }
+  }
+
+  // SDL ONLY
+  void GuiAdapter::OnMouseEvent(uint32_t windowID, const GuiAdapterMouseEvent& event)
+  {
+    if (windowID == 0)
+    {
+      LOG(WARNING) << "GuiAdapter::OnMouseEvent -- windowID == 0 and event won't be routed!";
+    }
+    else
+    {
+      // the SDL window name IS the canvas name ("canvas" is used because this lib
+      // is designed for Wasm
+      SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID);
+
+      ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!");
+
+      const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
+      ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!");
+
+      std::string windowTitle(windowTitleSz);
+      ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!");
+
+      switch (event.type)
+      {
+      case GUIADAPTER_EVENT_MOUSEDOWN:
+        for (size_t i = 0; i < mouseDownHandlers_.size(); i++)
+        {
+          if (mouseDownHandlers_[i].canvasName == windowTitle)
+            (*(mouseDownHandlers_[i].func))(windowTitle, &event, mouseDownHandlers_[i].userData);
+        }
+        break;
+      case GUIADAPTER_EVENT_MOUSEDBLCLICK:
+        for (size_t i = 0; i < mouseDblCickHandlers_.size(); i++)
+        {
+          if (mouseDblCickHandlers_[i].canvasName == windowTitle)
+            (*(mouseDblCickHandlers_[i].func))(windowTitle, &event, mouseDblCickHandlers_[i].userData);
+        }
+        break;
+      case GUIADAPTER_EVENT_MOUSEMOVE:
+        for (size_t i = 0; i < mouseMoveHandlers_.size(); i++)
+        {
+          if (mouseMoveHandlers_[i].canvasName == windowTitle)
+            (*(mouseMoveHandlers_[i].func))(windowTitle, &event, mouseMoveHandlers_[i].userData);
+        }
+        break;
+      case GUIADAPTER_EVENT_MOUSEUP:
+        for (size_t i = 0; i < mouseUpHandlers_.size(); i++)
+        {
+          if (mouseUpHandlers_[i].canvasName == windowTitle)
+            (*(mouseUpHandlers_[i].func))(windowTitle, &event, mouseUpHandlers_[i].userData);
+        }
+        break;
+      default:
+        ORTHANC_ASSERT(false, "Wrong event.type: " << event.type << " in GuiAdapter::OnMouseEvent(...)");
+        break;
+      }
+    }
+  }
+
+
+  // extern void Debug_SetContextToBeKilled(std::string title);
+  // extern void Debug_SetContextToBeRestored(std::string title);
+
+  // SDL ONLY
+  void GuiAdapter::RequestAnimationFrame(OnAnimationFrameFunc func, void* userData)
+  {
+    animationFrameHandlers_.push_back(std::make_pair(func, userData));
+  }
+
+# if ORTHANC_ENABLE_OPENGL == 1 && !defined(__APPLE__)   /* OpenGL debug is not available on OS X */
+
+  // SDL ONLY
+  static void GLAPIENTRY
+    OpenGLMessageCallback(GLenum source,
+      GLenum type,
+      GLuint id,
+      GLenum severity,
+      GLsizei length,
+      const GLchar * message,
+      const void* userParam)
+  {
+    if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
+    {
+      fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+        (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
+        type, severity, message);
+    }
+  }
+# endif
+
+#if 0
+  // TODO: remove this when generic sdl event handlers are implemented in 
+  // the DoseView
+  // SDL ONLY
+  bool GuiAdapter::IsSdlViewPortRefreshEvent(const SDL_Event& event) const
+  {
+    SDL_Window* sdlWindow = SDL_GetWindowFromID(event.window.windowID);
+
+    ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << event.window.windowID << "\" is not a valid SDL window ID!");
+
+    const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow);
+
+    // now we need to find the DoseView from from the canvas name!
+    // (and retrieve the SdlViewport)
+    boost::shared_ptr<IGuiAdapterWidget> foundWidget;
+    VisitWidgets([&foundWidget, windowTitleSz](auto widget)
+    {
+      if (widget->GetCanvasIdentifier() == std::string(windowTitleSz))
+        foundWidget = widget;
+    });
+    ORTHANC_ASSERT(foundWidget, "The window named: \"" << windowTitleSz << "\" was not found in the registered widgets!");
+    return foundWidget->GetSdlViewport().IsRefreshEvent(event);
+  }
+#endif
+
+  // SDL ONLY
+  void GuiAdapter::Run(GuiAdapterRunFunc func, void* cookie)
+  {
+#if 1
+    // TODO: MAKE THIS DYNAMIC !!! See SdlOpenGLViewport vs Cairo in ViewportWrapper
+# if ORTHANC_ENABLE_OPENGL == 1 && !defined(__APPLE__)
+    glEnable(GL_DEBUG_OUTPUT);
+    glDebugMessageCallback(OpenGLMessageCallback, 0);
+# endif
+#endif
+
+    // Uint32 SDL_GetWindowID(SDL_Window* window)
+    // SDL_Window* SDL_GetWindowFromID(Uint32 id)   // may return NULL
+
+    bool stop = false;
+    while (!stop)
+    {
+      {
+        // TODO: lock all viewports here! (use a scoped object)
+        if(func != NULL)
+          (*func)(cookie);
+        OnAnimationFrame(); // in SDL we must call it
+      }
+
+      while (!stop)
+      {
+        std::vector<SDL_Event> sdlEvents;
+        std::map<Uint32,SDL_Event> userEventsMap;
+        
+        SDL_Event sdlEvent;
+
+        // FIRST: collect all pending events
+        while (SDL_PollEvent(&sdlEvent) != 0)
+        {
+          if ( (sdlEvent.type >= SDL_USEREVENT) && 
+               (sdlEvent.type <= SDL_USEREVENT) )
+          {
+            // we don't want to have multiple events with the same event.type
+            userEventsMap[sdlEvent.type] = sdlEvent;
+          }
+          else
+          {
+            sdlEvents.push_back(sdlEvent);
+          }
+        }
+
+        // SECOND: collect all user events
+        for (std::map<Uint32,SDL_Event>::const_iterator it = userEventsMap.begin(); it != userEventsMap.end(); ++it)
+          sdlEvents.push_back(it->second);
+                
+        // now process the events
+        for (std::vector<SDL_Event>::const_iterator it = sdlEvents.begin(); it != sdlEvents.end(); ++it)
+        {
+          const SDL_Event& sdlEvent = *it;
+          // TODO: lock all viewports here! (use a scoped object)
+
+          if (sdlEvent.type == SDL_QUIT)
+          {
+            // TODO: call exit callbacks here
+            stop = true;
+            break;
+          }
+          else if ((sdlEvent.type == SDL_MOUSEMOTION) ||
+            (sdlEvent.type == SDL_MOUSEBUTTONDOWN) ||
+                   (sdlEvent.type == SDL_MOUSEBUTTONUP))
+          {
+            int scancodeCount = 0;
+            const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
+            bool ctrlPressed(false);
+            bool shiftPressed(false);
+            bool altPressed(false);
+
+            if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL])
+              ctrlPressed = true;
+            if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL])
+              ctrlPressed = true;
+            if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT])
+              shiftPressed = true;
+            if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT])
+              shiftPressed = true;
+            if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT])
+              altPressed = true;
+
+            GuiAdapterMouseEvent dest;
+            ConvertFromPlatform(dest, ctrlPressed, shiftPressed, altPressed, sdlEvent);
+            OnMouseEvent(sdlEvent.window.windowID, dest);
+  #if 0
+            // for reference, how to create trackers
+            if (tracker)
+            {
+              PointerEvent e;
+              e.AddPosition(compositor.GetPixelCenterCoordinates(
+                sdlEvent.button.x, sdlEvent.button.y));
+              tracker->PointerMove(e);
+            }
+  #endif
+          }
+          else if (sdlEvent.type == SDL_MOUSEWHEEL)
+          {
+
+            int scancodeCount = 0;
+            const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
+            bool ctrlPressed(false);
+            bool shiftPressed(false);
+            bool altPressed(false);
+
+            if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL])
+              ctrlPressed = true;
+            if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL])
+              ctrlPressed = true;
+            if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT])
+              shiftPressed = true;
+            if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT])
+              shiftPressed = true;
+            if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT])
+              altPressed = true;
+
+            GuiAdapterWheelEvent dest;
+            ConvertFromPlatform(dest, ctrlPressed, shiftPressed, altPressed, sdlEvent);
+            OnMouseWheelEvent(sdlEvent.window.windowID, dest);
+
+            //KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount);
+
+            //int x, y;
+            //SDL_GetMouseState(&x, &y);
+
+            //if (sdlEvent.wheel.y > 0)
+            //{
+            //  locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers);
+            //}
+            //else if (sdlEvent.wheel.y < 0)
+            //{
+            //  locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers);
+            //}
+          }
+          else if (sdlEvent.type == SDL_WINDOWEVENT &&
+            (sdlEvent.window.event == SDL_WINDOWEVENT_RESIZED ||
+             sdlEvent.window.event == SDL_WINDOWEVENT_SIZE_CHANGED))
+          {
+  #if 0
+            tracker.reset();
+  #endif
+            OnResize(sdlEvent.window.data1, sdlEvent.window.data2);
+          }
+          else if (sdlEvent.type == SDL_KEYDOWN && sdlEvent.key.repeat == 0 /* Ignore key bounce */)
+          {
+            switch (sdlEvent.key.keysym.sym)
+            {
+            case SDLK_f:
+              // window.GetWindow().ToggleMaximize(); //TODO: move to particular handler
+              break;
+
+            // This commented out code was used to debug the context
+            // loss/restoring code (2019-08-10)
+            // case SDLK_k:
+            //   {
+            //     SDL_Window* window = SDL_GetWindowFromID(sdlEvent.window.windowID);
+            //     std::string windowTitle(SDL_GetWindowTitle(window));
+            //     Debug_SetContextToBeKilled(windowTitle);
+            //   }
+            //   break;
+            // case SDLK_l:
+            //   {
+            //     SDL_Window* window = SDL_GetWindowFromID(sdlEvent.window.windowID);
+            //     std::string windowTitle(SDL_GetWindowTitle(window));
+            //     Debug_SetContextToBeRestored(windowTitle);
+            //   }
+            //   break;
+
+            case SDLK_q:
+              stop = true;
+              break;
+
+            default:
+              GuiAdapterKeyboardEvent dest;
+              ConvertFromPlatform(dest, sdlEvent);
+              OnKeyboardEvent(sdlEvent.window.windowID, dest);
+              break;
+            }
+          }
+        
+          OnSdlGenericEvent(sdlEvent);
+        }
+      }
+
+      SDL_Delay(1);
+    }
+  }
+#endif
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Generic/GuiAdapter.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,375 @@
+/**
+ * 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 <string>
+
+#if ORTHANC_ENABLE_WASM != 1
+# ifdef __EMSCRIPTEN__
+#   error __EMSCRIPTEN__ is defined and ORTHANC_ENABLE_WASM != 1
+# endif
+#endif
+
+#if ORTHANC_ENABLE_WASM == 1
+# ifndef __EMSCRIPTEN__
+#   error __EMSCRIPTEN__ is not defined and ORTHANC_ENABLE_WASM == 1
+# endif
+#endif
+
+#if ORTHANC_ENABLE_WASM == 1
+# include <emscripten/html5.h>
+#else
+# if ORTHANC_ENABLE_SDL == 1
+#   include <SDL.h>
+# endif
+#endif
+
+#include "../../Framework/StoneException.h"
+
+#if ORTHANC_ENABLE_THREADS == 1
+# include "../../Framework/Deprecated/Messages/LockingEmitter.h"
+#endif
+
+#include <vector>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+namespace OrthancStone
+{
+#if ORTHANC_ENABLE_SDL == 1
+  class SdlViewport;
+#endif
+
+#if 0
+
+  /**
+  This interface is used to store the widgets that are controlled by the 
+  GuiAdapter and receive event callbacks.
+  The callbacks may possibly be downcast (using dynamic_cast, for safety) \
+  to the actual widget type
+  */
+  class IGuiAdapterWidget
+  {
+  public:
+    virtual ~IGuiAdapterWidget() {}
+    
+#if #if ORTHANC_ENABLE_SDL == 1
+    /**
+    Returns the SdlViewport that this widget contains. If the underlying 
+    viewport type is *not* SDL, then an error is returned.
+    */
+    virtual SdlViewport& GetSdlViewport() = 0;
+#endif
+  };
+
+#endif
+
+  enum GuiAdapterMouseButtonType
+  {
+    GUIADAPTER_MOUSEBUTTON_LEFT = 0,
+    GUIADAPTER_MOUSEBUTTON_MIDDLE = 1,
+    GUIADAPTER_MOUSEBUTTON_RIGHT = 2
+  };
+
+
+  enum GuiAdapterHidEventType
+  {
+    GUIADAPTER_EVENT_MOUSEDOWN      = 1973,
+    GUIADAPTER_EVENT_MOUSEMOVE      = 1974,
+    GUIADAPTER_EVENT_MOUSEDBLCLICK  = 1975,
+    GUIADAPTER_EVENT_MOUSEUP        = 1976,
+    GUIADAPTER_EVENT_WHEEL          = 1977,
+    GUIADAPTER_EVENT_KEYDOWN        = 1978,
+    GUIADAPTER_EVENT_KEYUP          = 1979,
+  };
+
+  const unsigned int GUIADAPTER_DELTA_PIXEL = 2973;
+  const unsigned int GUIADAPTER_DELTA_LINE  = 2974;
+  const unsigned int GUIADAPTER_DELTA_PAGE  = 2975;
+
+  struct GuiAdapterUiEvent;
+  struct GuiAdapterMouseEvent;
+  struct GuiAdapterWheelEvent;
+  struct GuiAdapterKeyboardEvent;
+
+#if 1
+  typedef bool (*OnMouseEventFunc)    (std::string canvasId, const GuiAdapterMouseEvent* mouseEvent, void* userData);
+  typedef bool (*OnMouseWheelFunc)    (std::string canvasId, const GuiAdapterWheelEvent* wheelEvent, void* userData);
+  typedef bool (*OnKeyDownFunc)       (std::string canvasId, const GuiAdapterKeyboardEvent*   keyEvent,   void* userData);
+  typedef bool (*OnKeyUpFunc)         (std::string canvasId, const GuiAdapterKeyboardEvent*   keyEvent,   void* userData);
+  typedef bool (*OnAnimationFrameFunc)(double time, void* userData);
+  
+#if ORTHANC_ENABLE_SDL == 1
+  typedef bool (*OnSdlEventCallback)  (std::string canvasId, const SDL_Event& sdlEvent, void* userData);
+
+  typedef bool (*OnSdlWindowResizeFunc)(std::string canvasId, 
+                                        const GuiAdapterUiEvent* uiEvent, 
+                                        unsigned int width, 
+                                        unsigned int height, 
+                                        void* userData);
+
+
+#endif
+
+#else
+
+#if ORTHANC_ENABLE_WASM == 1
+  typedef EM_BOOL (*OnMouseEventFunc)(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData);
+  typedef EM_BOOL (*OnMouseWheelFunc)(int eventType, const EmscriptenWheelEvent* wheelEvent, void* userData);
+  typedef EM_BOOL (*OnKeyDownFunc)   (int eventType, const EmscriptenKeyboardEvent* keyEvent, void* userData);
+  typedef EM_BOOL (*OnKeyUpFunc)     (int eventType, const EmscriptenKeyboardEvent* keyEvent, void* userData);
+
+  typedef EM_BOOL (*OnAnimationFrameFunc)(double time, void* userData);
+  typedef EM_BOOL (*OnWindowResizeFunc)(int eventType, const EmscriptenUiEvent* uiEvent, void* userData);
+#else
+  typedef bool    (*OnMouseEventFunc)(int eventType, const SDL_Event* mouseEvent, void* userData);
+  typedef bool    (*OnMouseWheelFunc)(int eventType, const SDL_Event* wheelEvent, void* userData);
+  typedef bool    (*OnKeyDownFunc)   (int eventType, const SDL_Event* keyEvent,   void* userData);
+  typedef bool    (*OnKeyUpFunc)     (int eventType, const SDL_Event* keyEvent,   void* userData);
+
+  typedef bool    (*OnAnimationFrameFunc)(double time, void* userData);
+  typedef bool    (*OnWindowResizeFunc)(int eventType, const GuiAdapterUiEvent* uiEvent, void* userData);
+#endif
+
+#endif
+  struct GuiAdapterMouseEvent
+  {
+    GuiAdapterHidEventType type;
+    //double                   timestamp;
+    //long                     screenX;
+    //long                     screenY;
+    //long                     clientX;
+    //long                     clientY;
+    bool                     ctrlKey;
+    bool                     shiftKey;
+    bool                     altKey;
+    //bool                     metaKey;
+    unsigned short           button;
+    //unsigned short           buttons;
+    //long                     movementX;
+    //long                     movementY;
+    long                     targetX;
+    long                     targetY;
+    // canvasX and canvasY are deprecated - there no longer exists a Module['canvas'] object, so canvasX/Y are no longer reported (register a listener on canvas directly to get canvas coordinates, or translate manually)
+    //long                     canvasX;
+    //long                     canvasY;
+    //long                     padding;
+
+  public:
+    GuiAdapterMouseEvent()
+      : ctrlKey(false),
+        shiftKey(false),
+        altKey(false)
+    {
+    }
+  };
+
+  struct GuiAdapterWheelEvent {
+    GuiAdapterMouseEvent mouse;
+    double deltaX;
+    double deltaY;
+    unsigned long deltaMode;
+  };
+
+  // we don't use any data now
+  struct GuiAdapterUiEvent {};
+
+  // EmscriptenKeyboardEvent
+  struct GuiAdapterKeyboardEvent
+  {
+    GuiAdapterHidEventType type;
+    char sym[32];
+    bool ctrlKey;
+    bool shiftKey;
+    bool altKey;
+  };
+
+  std::ostream& operator<<(std::ostream& os, const GuiAdapterKeyboardEvent& event);
+  std::ostream& operator<<(std::ostream& os, const GuiAdapterMouseEvent& event);
+
+  /*
+    Mousedown event trigger when either the left or right (or middle) mouse is pressed 
+    on the object;
+
+    Mouseup event trigger when either the left or right (or middle) mouse is released
+    above the object after triggered mousedown event and held.
+
+    Click event trigger when the only left mouse button is pressed and released on the
+    same object, requires the Mousedown and Mouseup event happened before Click event.
+
+    The normal expect trigger order: onMousedown >> onMouseup >> onClick
+
+    Testing in Chrome v58, the time between onMouseup and onClick events are around 
+    7ms to 15ms
+
+    FROM: https://codingrepo.com/javascript/2017/05/19/javascript-difference-mousedown-mouseup-click-events/
+  */
+#if ORTHANC_ENABLE_WASM == 1
+  void ConvertFromPlatform(GuiAdapterUiEvent& dest, int eventType, const EmscriptenUiEvent& src);
+
+  void ConvertFromPlatform(GuiAdapterMouseEvent& dest, int eventType, const EmscriptenMouseEvent& src);
+  
+  void ConvertFromPlatform(GuiAdapterWheelEvent& dest, int eventType, const EmscriptenWheelEvent& src);
+
+  void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const EmscriptenKeyboardEvent& src);
+#else
+
+# if ORTHANC_ENABLE_SDL == 1
+  void ConvertFromPlatform(GuiAdapterMouseEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, const SDL_Event& source);
+
+  void ConvertFromPlatform(GuiAdapterWheelEvent& dest, bool ctrlPressed, bool shiftPressed, bool altPressed, const SDL_Event& source);
+
+  void ConvertFromPlatform(GuiAdapterKeyboardEvent& dest, const SDL_Event& source);
+
+# endif
+
+#endif
+
+  typedef void (*GuiAdapterRunFunc)(void*);
+
+  class GuiAdapter
+  {
+  public:
+    GuiAdapter()
+    {
+      static int instanceCount = 0;
+      ORTHANC_ASSERT(instanceCount == 0);
+      instanceCount = 1;
+    }
+
+    /**
+      emscripten_set_resize_callback("EMSCRIPTEN_EVENT_TARGET_WINDOW", NULL, false, OnWindowResize);
+
+      emscripten_set_wheel_callback("#mycanvas1", widget1_.get(), false, OnXXXMouseWheel);
+      emscripten_set_wheel_callback("#mycanvas2", widget2_.get(), false, OnXXXMouseWheel);
+      emscripten_set_wheel_callback("#mycanvas3", widget3_.get(), false, OnXXXMouseWheel);
+
+      emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyDown); ---> NO!
+      emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyUp);
+
+      emscripten_request_animation_frame_loop(OnAnimationFrame, NULL);
+    
+      SDL:
+      see https://wiki.libsdl.org/SDL_CaptureMouse
+
+    */
+
+    void SetMouseDownCallback       (std::string canvasId, void* userData, bool capture, OnMouseEventFunc   func);
+    void SetMouseDblClickCallback   (std::string canvasId, void* userData, bool capture, OnMouseEventFunc   func);
+    void SetMouseMoveCallback       (std::string canvasId, void* userData, bool capture, OnMouseEventFunc   func);
+    void SetMouseUpCallback         (std::string canvasId, void* userData, bool capture, OnMouseEventFunc   func);
+    void SetWheelCallback           (std::string canvasId, void* userData, bool capture, OnMouseWheelFunc   func);
+    void SetKeyDownCallback         (std::string canvasId, void* userData, bool capture, OnKeyDownFunc      func);
+    void SetKeyUpCallback           (std::string canvasId, void* userData, bool capture, OnKeyUpFunc        func);
+
+#if ORTHANC_ENABLE_SDL == 1
+
+    void SetGenericSdlEventCallback (std::string canvasId, void* userData, bool capture, OnSdlEventCallback func);
+
+    typedef bool (*OnSdlEventCallback)  (std::string canvasId, const SDL_Event& sdlEvent, void* userData);
+
+    // if you pass "#window", then any Window resize will trigger the callback
+    // (this special string is converted to EMSCRIPTEN_EVENT_TARGET_WINDOW in DOM, when DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1)
+    void SetSdlResizeCallback(std::string canvasId, 
+                              void* userData, 
+                              bool capture, 
+                              OnSdlWindowResizeFunc func);
+#endif
+
+    void RequestAnimationFrame(OnAnimationFrameFunc func, void* userData);
+
+    // TODO: implement and call to remove canvases [in SDL, although code should be generic]
+    void SetOnExitCallback();
+
+    /**
+    Under SDL, this function does NOT return until all windows have been closed.
+    Under wasm, it returns without doing anything, since the event loop is managed
+    by the browser.
+    */
+    void Run(GuiAdapterRunFunc func = NULL, void* cookie = NULL);
+
+  private:
+
+#if ORTHANC_ENABLE_SDL == 1
+    /**
+    Gives observers a chance to react based on generic event handlers. This 
+    is used, for instance, when the viewport lock interface is invalidated.
+    */
+    void OnSdlGenericEvent(const SDL_Event& sdlEvent);
+#endif
+
+    /**
+    In SDL, this executes all the registered headers
+    */
+    void OnAnimationFrame();
+
+    //void RequestAnimationFrame(OnAnimationFrameFunc func, void* userData);
+    std::vector<std::pair<OnAnimationFrameFunc, void*> >  
+      animationFrameHandlers_;
+    
+    void OnResize(unsigned int width, unsigned int height);
+
+#if ORTHANC_ENABLE_SDL == 1
+    template<typename Func>
+    struct EventHandlerData
+    {
+      EventHandlerData(std::string canvasName, Func func, void* userData) 
+        : canvasName(canvasName)
+        , func(func)
+        , userData(userData)
+      {
+      }
+
+      std::string canvasName;
+      Func        func;
+      void*       userData;
+    };
+    std::vector<EventHandlerData<OnSdlWindowResizeFunc> > resizeHandlers_;
+    std::vector<EventHandlerData<OnMouseEventFunc  > >    mouseDownHandlers_;
+    std::vector<EventHandlerData<OnMouseEventFunc  > >    mouseDblCickHandlers_;
+    std::vector<EventHandlerData<OnMouseEventFunc  > >    mouseMoveHandlers_;
+    std::vector<EventHandlerData<OnMouseEventFunc  > >    mouseUpHandlers_;
+    std::vector<EventHandlerData<OnMouseWheelFunc  > >    mouseWheelHandlers_;
+    std::vector<EventHandlerData<OnKeyDownFunc > >        keyDownHandlers_;
+    std::vector<EventHandlerData<OnKeyUpFunc > >          keyUpHandlers_;
+    std::vector<EventHandlerData<OnSdlEventCallback > >   sdlEventHandlers_;
+
+    /**
+    This executes all the registered headers if needed (in wasm, the browser
+    deals with this)
+    */
+    void OnMouseEvent(uint32_t windowID, const GuiAdapterMouseEvent& event);
+
+    void OnKeyboardEvent(uint32_t windowID, const GuiAdapterKeyboardEvent& event);
+
+    /**
+    Same remark as OnMouseEvent
+    */
+    void OnMouseWheelEvent(uint32_t windowID, const GuiAdapterWheelEvent& event);
+
+#endif
+
+    /**
+    This executes all the registered headers if needed (in wasm, the browser
+    deals with this)
+    */
+    void ViewportsUpdateSize();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Generic/NativeStoneApplicationContext.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,78 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "NativeStoneApplicationContext.h"
+#include "../../Platforms/Generic/OracleWebService.h"
+
+namespace OrthancStone
+{
+  void NativeStoneApplicationContext::GlobalMutexLocker::SetCentralWidget(
+    boost::shared_ptr<Deprecated::IWidget> widget)
+  {
+    that_.centralViewport_.SetCentralWidget(widget);
+  }
+
+
+  void NativeStoneApplicationContext::UpdateThread(NativeStoneApplicationContext* that)
+  {
+    while (!that->stopped_)
+    {
+      {
+        GlobalMutexLocker locker(*that);
+        locker.GetCentralViewport().DoAnimation();
+      }
+      
+      boost::this_thread::sleep(boost::posix_time::milliseconds(that->updateDelayInMs_));
+    }
+  }
+  
+
+  NativeStoneApplicationContext::NativeStoneApplicationContext() :
+    stopped_(true),
+    updateDelayInMs_(100)   // By default, 100ms between each refresh of the content
+  {
+    srand(static_cast<unsigned int>(time(NULL))); 
+  }
+
+
+  void NativeStoneApplicationContext::Start()
+  {
+    boost::recursive_mutex::scoped_lock lock(globalMutex_);
+    
+    if (stopped_ &&
+        centralViewport_.HasAnimation())
+    {
+      stopped_ = false;
+      updateThread_ = boost::thread(UpdateThread, this);
+    }
+  }
+
+
+  void NativeStoneApplicationContext::Stop()
+  {
+    stopped_ = true;
+    
+    if (updateThread_.joinable())
+    {
+      updateThread_.join();
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Generic/NativeStoneApplicationContext.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,78 @@
+/**
+ * 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 "../../Framework/Deprecated/Viewport/WidgetViewport.h"
+#include "../../Framework/Deprecated/Volumes/ISlicedVolume.h"
+#include "../../Framework/Deprecated/Volumes/IVolumeLoader.h"
+
+#include <list>
+#include <boost/thread.hpp>
+#include "../StoneApplicationContext.h"
+
+namespace OrthancStone
+{
+  class NativeStoneApplicationContext : public StoneApplicationContext
+  {
+  private:
+    static void UpdateThread(NativeStoneApplicationContext* that);
+
+    boost::recursive_mutex    globalMutex_;
+    Deprecated::WidgetViewport  centralViewport_;
+    boost::thread   updateThread_;
+    bool            stopped_;
+    unsigned int    updateDelayInMs_;
+
+  public:
+    class GlobalMutexLocker: public boost::noncopyable
+    {
+    private:
+      NativeStoneApplicationContext&        that_;
+      boost::recursive_mutex::scoped_lock   lock_;
+      
+    public:
+      GlobalMutexLocker(NativeStoneApplicationContext& that) :
+        that_(that),
+        lock_(that.globalMutex_)
+      {
+      }
+
+      void SetCentralWidget(boost::shared_ptr<Deprecated::IWidget> widget);
+
+      Deprecated::IViewport& GetCentralViewport() 
+      {
+        return that_.centralViewport_;
+      }
+
+      void SetUpdateDelay(unsigned int delayInMs)
+      {
+        that_.updateDelayInMs_ = delayInMs;
+      }
+    };
+
+    NativeStoneApplicationContext();
+
+    void Start();
+
+    void Stop();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Generic/NativeStoneApplicationRunner.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,265 @@
+/**
+ * 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/>.
+ **/
+
+
+#if ORTHANC_ENABLE_THREADS != 1
+#error this file shall be included only with the ORTHANC_ENABLE_THREADS set to 1
+#endif
+
+#include "NativeStoneApplicationRunner.h"
+
+#include "../../Framework/Deprecated/Toolbox/MessagingToolbox.h"
+#include "../../Platforms/Generic/OracleWebService.h"
+#include "../../Platforms/Generic/OracleDelayedCallExecutor.h"
+#include "NativeStoneApplicationContext.h"
+
+#include <Core/Logging.h>
+#include <Core/HttpClient.h>
+#include <Core/Toolbox.h>
+#include <Core/OrthancException.h>
+#include <Plugins/Samples/Common/OrthancHttpConnection.h>
+
+#include <boost/program_options.hpp>
+
+namespace OrthancStone
+{
+  // Anonymous namespace to avoid clashes against other compilation modules
+  namespace
+  {
+    class LogStatusBar : public Deprecated::IStatusBar
+    {
+    public:
+      virtual void ClearMessage()
+      {
+      }
+
+      virtual void SetMessage(const std::string& message)
+      {
+        LOG(WARNING) << message;
+      }
+    };
+  }
+
+  int NativeStoneApplicationRunner::Execute(int argc,
+                                            char* argv[])
+  {
+    /******************************************************************
+     * Initialize all the subcomponents of Orthanc Stone
+     ******************************************************************/
+
+    Orthanc::Logging::Initialize();
+    Orthanc::Toolbox::InitializeOpenSsl();
+    Orthanc::HttpClient::GlobalInitialize();
+
+    Initialize();
+
+    /******************************************************************
+     * Declare and parse the command-line options of the application
+     ******************************************************************/
+
+    boost::program_options::options_description options;
+
+    { // generic options
+      boost::program_options::options_description generic("Generic options");
+      generic.add_options()
+          ("help", "Display this help and exit")
+          ("verbose", "Be verbose in logs")
+          ("orthanc", boost::program_options::value<std::string>()->
+            default_value("http://localhost:8042/"),
+           "URL to the Orthanc server")
+          ("username", "Username for the Orthanc server")
+          ("password", "Password for the Orthanc server")
+          ("https-verify", boost::program_options::value<bool>()->
+            default_value(true), "Check HTTPS certificates")
+          ;
+
+      options.add(generic);
+    }
+
+    // platform specific options
+    DeclareCommandLineOptions(options);
+    
+    // application specific options
+    application_->DeclareStartupOptions(options);
+
+    boost::program_options::variables_map parameters;
+    bool error = false;
+
+    try
+    {
+      boost::program_options::store(
+        boost::program_options::command_line_parser(argc, argv).
+          options(options).allow_unregistered().run(), parameters);
+      boost::program_options::notify(parameters);
+    }
+    catch (boost::program_options::error& e)
+    {
+      LOG(ERROR) << 
+        "Error while parsing the command-line arguments: " << e.what();
+      error = true;
+    }
+
+
+    /******************************************************************
+     * Configure the application with the command-line parameters
+     ******************************************************************/
+
+    if (error || parameters.count("help"))
+    {
+      std::cout << std::endl;
+
+      std::cout << options << "\n";
+      return error ? -1 : 0;
+    }
+
+    if (parameters.count("https-verify") &&
+        !parameters["https-verify"].as<bool>())
+    {
+      LOG(WARNING) << "Turning off verification of HTTPS certificates (unsafe)";
+      Orthanc::HttpClient::ConfigureSsl(false, "");
+    }
+
+    LOG(ERROR) << "???????? if (parameters.count(\"verbose\"))";
+    if (parameters.count("verbose"))
+    {
+      LOG(ERROR) << "parameters.count(\"verbose\") != 0";
+      Orthanc::Logging::EnableInfoLevel(true);
+      LOG(INFO) << "Verbose logs are enabled";
+    }
+
+    LOG(ERROR) << "???????? if (parameters.count(\"trace\"))";
+    if (parameters.count("trace"))
+    {
+      LOG(ERROR) << "parameters.count(\"trace\") != 0";
+      Orthanc::Logging::EnableTraceLevel(true);
+      VLOG(1) << "Trace logs are enabled";
+    }
+
+    ParseCommandLineOptions(parameters);
+
+    bool success = true;
+    try
+    {
+      /****************************************************************
+       * Initialize the connection to the Orthanc server
+       ****************************************************************/
+
+      Orthanc::WebServiceParameters webServiceParameters;
+
+      if (parameters.count("orthanc"))
+      {
+        webServiceParameters.SetUrl(parameters["orthanc"].as<std::string>());
+      }
+
+      if (parameters.count("username") && parameters.count("password"))
+      {
+        webServiceParameters.SetCredentials(parameters["username"].
+          as<std::string>(),
+          parameters["password"].as<std::string>());
+      }
+
+      LOG(WARNING) << "URL to the Orthanc REST API: " << 
+        webServiceParameters.GetUrl();
+
+      {
+        OrthancPlugins::OrthancHttpConnection orthanc(webServiceParameters);
+        if (!Deprecated::MessagingToolbox::CheckOrthancVersion(orthanc))
+        {
+          LOG(ERROR) << "Your version of Orthanc is incompatible with Stone of "
+            << "Orthanc, please upgrade";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+        }
+      }
+
+
+      /****************************************************************
+       * Initialize the application
+       ****************************************************************/
+
+      LOG(WARNING) << "Creating the widgets of the application";
+
+      LogStatusBar statusBar;
+
+      NativeStoneApplicationContext context;
+
+      {
+        // use multiple threads to execute asynchronous tasks like 
+        // download content
+        Deprecated::Oracle oracle(6); 
+        oracle.Start();
+
+        {
+          boost::shared_ptr<Deprecated::OracleWebService> webService
+            (new Deprecated::OracleWebService(oracle, webServiceParameters, context));
+          context.SetWebService(webService);
+          context.SetOrthancBaseUrl(webServiceParameters.GetUrl());
+
+          Deprecated::OracleDelayedCallExecutor delayedExecutor(oracle, context);
+          context.SetDelayedCallExecutor(delayedExecutor);
+
+          application_->Initialize(&context, statusBar, parameters);
+
+          {
+            NativeStoneApplicationContext::GlobalMutexLocker locker(context);
+            locker.SetCentralWidget(application_->GetCentralWidget());
+            locker.GetCentralViewport().SetStatusBar(statusBar);
+          }
+
+          std::string title = application_->GetTitle();
+          if (title.empty())
+          {
+            title = "Stone of Orthanc";
+          }
+
+          /****************************************************************
+           * Run the application
+           ****************************************************************/
+
+          Run(context, title, argc, argv);
+
+          /****************************************************************
+           * Finalize the application
+           ****************************************************************/
+
+          oracle.Stop();
+        }
+      }
+
+      LOG(WARNING) << "The application is stopping";
+      application_->Finalize();
+    }
+    catch (Orthanc::OrthancException& e)
+    {
+      LOG(ERROR) << "EXCEPTION: " << e.What();
+      success = false;
+    }
+
+
+    /******************************************************************
+     * Finalize all the subcomponents of Orthanc Stone
+     ******************************************************************/
+
+    Finalize();
+    Orthanc::HttpClient::GlobalFinalize();
+    Orthanc::Toolbox::FinalizeOpenSsl();
+
+    return (success ? 0 : -1);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Generic/NativeStoneApplicationRunner.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,55 @@
+/**
+ * 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 "../IStoneApplication.h"
+
+#if ORTHANC_ENABLE_THREADS != 1
+#error this file shall be included only with the ORTHANC_ENABLE_THREADS set to 1
+#endif
+
+namespace OrthancStone
+{
+  class NativeStoneApplicationContext;
+
+  class NativeStoneApplicationRunner
+  {
+  protected:
+    boost::shared_ptr<IStoneApplication>  application_;
+    
+  public:
+    NativeStoneApplicationRunner(boost::shared_ptr<IStoneApplication> application)
+      : application_(application)
+    {
+    }
+    int Execute(int argc,
+                char* argv[]);
+
+    virtual void Initialize() = 0;
+    virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) = 0;
+    virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters) = 0;
+
+    virtual void Run(NativeStoneApplicationContext& context, const std::string& title, int argc, char* argv[]) = 0;
+    virtual void Finalize() = 0;
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Generic/Scene2DInteractor.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,52 @@
+/**
+ * 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 "../../Framework/Scene2D/PointerEvent.h"
+#include "../../Framework/Scene2DViewport/ViewportController.h"
+//#include "../../Framework/Scene2D/Internals/CompositorHelper.h"
+#include "GuiAdapter.h"
+
+
+namespace OrthancStone
+{
+
+  class Scene2DInteractor
+  {
+  protected:
+    boost::shared_ptr<ViewportController>       viewportController_;
+//    boost::shared_ptr<ICompositor>              compositor_;
+
+  public:
+    Scene2DInteractor(boost::shared_ptr<ViewportController> viewportController) :
+      viewportController_(viewportController)
+    {}
+
+//    void SetCompositor(boost::shared_ptr<ICompositor> compositor)
+//    {
+//      compositor_ = compositor;
+//    }
+
+    virtual bool OnMouseEvent(const GuiAdapterMouseEvent& guiEvent, const PointerEvent& pointerEvent) = 0; // returns true if it has handled the event
+    virtual bool OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent) = 0; // returns true if it has handled the event
+    virtual bool OnWheelEvent(const GuiAdapterWheelEvent& guiEvent) = 0; // returns true if it has handled the event
+
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/IStoneApplication.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,74 @@
+/**
+ * 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 "StoneApplicationContext.h"
+#include "../Framework/Deprecated/Viewport/WidgetViewport.h"
+
+#include <boost/program_options.hpp>
+#include <json/json.h>
+
+namespace OrthancStone
+{
+#if ORTHANC_ENABLE_QT==1
+  class QStoneMainWindow;
+#endif
+
+  // a StoneApplication is an application that can actually be executed
+  // in multiple environments.  i.e: it can run natively integrated in a QtApplication
+  // or it can be executed as part of a WebPage when compiled into WebAssembly.
+  class IStoneApplication : public boost::noncopyable
+  {
+  protected:
+    StoneApplicationContext* context_;
+
+  public:
+    virtual ~IStoneApplication()
+    {
+    }
+
+    virtual void DeclareStartupOptions(boost::program_options::options_description& options) = 0;
+    virtual void Initialize(StoneApplicationContext* context,
+                            Deprecated::IStatusBar& statusBar,
+                            const boost::program_options::variables_map& parameters) = 0;
+
+    /**
+      This method is meant to process messages received from the outside world (i.e. GUI)
+    */
+    virtual void HandleSerializedMessage(const char* data) = 0;
+
+#if ORTHANC_ENABLE_WASM==1
+    virtual void InitializeWasm() {}  // specific initialization when the app is running in WebAssembly.  This is called after the other Initialize()
+#endif
+#if ORTHANC_ENABLE_QT==1
+      virtual QStoneMainWindow* CreateQtMainWindow() = 0;
+#endif
+
+    virtual std::string GetTitle() const = 0;
+    
+    virtual void SetCentralWidget(boost::shared_ptr<Deprecated::IWidget> widget) = 0;
+    
+    virtual boost::shared_ptr<Deprecated::IWidget> GetCentralWidget() = 0;
+    
+    virtual void Finalize() = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Qt/QCairoWidget.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,238 @@
+/**
+ * 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/>.
+ **/
+
+#include "QCairoWidget.h"
+
+#include <QPainter>
+#include <QPaintEvent>
+
+#include <stdexcept>
+
+
+QCairoWidget::StoneObserver::StoneObserver(QCairoWidget& that,
+                                           Deprecated::IViewport& viewport,
+                                           OrthancStone::MessageBroker& broker) :
+  OrthancStone::IObserver(broker),
+  that_(that)
+{
+  // get notified each time the content of the central viewport changes
+  viewport.RegisterObserverCallback(
+    new OrthancStone::Callable<StoneObserver, Deprecated::IViewport::ViewportChangedMessage>
+    (*this, &StoneObserver::OnViewportChanged));
+}
+
+
+QCairoWidget::QCairoWidget(QWidget *parent) :
+  QWidget(parent),
+  context_(NULL)
+{
+  setFocusPolicy(Qt::StrongFocus); // catch keyPressEvents
+}
+
+
+void QCairoWidget::SetContext(OrthancStone::NativeStoneApplicationContext& context)
+{
+  context_ = &context;
+
+  {
+    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
+    observer_.reset(new StoneObserver(*this,
+                                      locker.GetCentralViewport(),
+                                      locker.GetMessageBroker()));
+  }
+}
+
+
+void QCairoWidget::paintEvent(QPaintEvent* /*event*/)
+{
+  QPainter painter(this);
+
+  if (image_.get() != NULL &&
+      context_ != NULL)
+  {
+    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
+    Deprecated::IViewport& viewport = locker.GetCentralViewport();
+    Orthanc::ImageAccessor a;
+    surface_.GetWriteableAccessor(a);
+    viewport.Render(a);
+    painter.drawImage(0, 0, *image_);
+  }
+  else
+  {
+    painter.fillRect(rect(), Qt::red);
+  }
+}
+
+OrthancStone::KeyboardModifiers GetKeyboardModifiers(QInputEvent* event)
+{
+  Qt::KeyboardModifiers qtModifiers = event->modifiers();
+  int stoneModifiers = static_cast<int>(OrthancStone::KeyboardModifiers_None);
+  if ((qtModifiers & Qt::AltModifier) != 0)
+  {
+    stoneModifiers |= static_cast<int>(OrthancStone::KeyboardModifiers_Alt);
+  }
+  if ((qtModifiers & Qt::ControlModifier) != 0)
+  {
+    stoneModifiers |= static_cast<int>(OrthancStone::KeyboardModifiers_Control);
+  }
+  if ((qtModifiers & Qt::ShiftModifier) != 0)
+  {
+    stoneModifiers |= static_cast<int>(OrthancStone::KeyboardModifiers_Shift);
+  }
+  return static_cast<OrthancStone::KeyboardModifiers>(stoneModifiers);
+}
+
+void QCairoWidget::mousePressEvent(QMouseEvent* event)
+{
+  OrthancStone::KeyboardModifiers stoneModifiers = GetKeyboardModifiers(event);
+
+  OrthancStone::MouseButton button;
+
+  switch (event->button())
+  {
+    case Qt::LeftButton:
+      button = OrthancStone::MouseButton_Left;
+      break;
+
+    case Qt::RightButton:
+      button = OrthancStone::MouseButton_Right;
+      break;
+
+    case Qt::MiddleButton:
+      button = OrthancStone::MouseButton_Middle;
+      break;
+
+    default:
+      return;  // Unsupported button
+  }
+
+  {
+    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
+    locker.GetCentralViewport().MouseDown(button, event->pos().x(), event->pos().y(), stoneModifiers, std::vector<Deprecated::Touch>());
+  }
+}
+
+
+void QCairoWidget::mouseReleaseEvent(QMouseEvent* /*eventNotUsed*/)
+{
+  OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
+  locker.GetCentralViewport().MouseLeave();
+}
+
+
+void QCairoWidget::mouseMoveEvent(QMouseEvent* event)
+{
+  OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
+  locker.GetCentralViewport().MouseMove(event->pos().x(), event->pos().y(), std::vector<Deprecated::Touch>());
+}
+
+
+void QCairoWidget::wheelEvent(QWheelEvent * event)
+{
+  OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
+
+  OrthancStone::KeyboardModifiers stoneModifiers = GetKeyboardModifiers(event);
+
+  if (event->orientation() == Qt::Vertical)
+  {
+    if (event->delta() < 0)  // TODO: compare direction with SDL and make sure we send the same directions
+    {
+       locker.GetCentralViewport().MouseWheel(OrthancStone::MouseWheelDirection_Up, event->pos().x(), event->pos().y(), stoneModifiers);
+    }
+    else
+    {
+      locker.GetCentralViewport().MouseWheel(OrthancStone::MouseWheelDirection_Down, event->pos().x(), event->pos().y(), stoneModifiers);
+    }
+  }
+}
+
+void QCairoWidget::keyPressEvent(QKeyEvent *event)
+{
+  using namespace OrthancStone;
+
+  OrthancStone::KeyboardModifiers stoneModifiers = GetKeyboardModifiers(event);
+
+  OrthancStone::KeyboardKeys keyType = OrthancStone::KeyboardKeys_Generic;
+  char keyChar = event->text()[0].toLatin1();
+
+#define CASE_QT_KEY_TO_ORTHANC(qt, o) case qt: keyType = o; break;
+  if (keyChar == 0)
+  {
+    switch (event->key())
+    {
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_Up, KeyboardKeys_Up);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_Down, KeyboardKeys_Down);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_Left, KeyboardKeys_Left);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_Right, KeyboardKeys_Right);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F1, KeyboardKeys_F1);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F2, KeyboardKeys_F2);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F3, KeyboardKeys_F3);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F4, KeyboardKeys_F4);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F5, KeyboardKeys_F5);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F6, KeyboardKeys_F6);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F7, KeyboardKeys_F7);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F8, KeyboardKeys_F8);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F9, KeyboardKeys_F9);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F10, KeyboardKeys_F10);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F11, KeyboardKeys_F11);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_F12, KeyboardKeys_F12);
+    default:
+      break;
+    }
+  }
+  else if (keyChar == 127)
+  {
+    switch (event->key())
+    {
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_Delete, KeyboardKeys_Delete);
+      CASE_QT_KEY_TO_ORTHANC(Qt::Key_Backspace, KeyboardKeys_Backspace);
+    default:
+      break;
+    }
+  }
+
+  {
+    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);    
+    locker.GetCentralViewport().KeyPressed(keyType, keyChar, stoneModifiers);
+  }
+}
+
+
+void QCairoWidget::resizeEvent(QResizeEvent* event)
+{
+  grabGesture(Qt::PanGesture);
+  QWidget::resizeEvent(event);
+
+  if (event)
+  {
+    surface_.SetSize(event->size().width(), event->size().height(), true);
+
+    image_.reset(new QImage(reinterpret_cast<uchar*>(surface_.GetBuffer()),
+                            event->size().width(), 
+                            event->size().height(),
+                            surface_.GetPitch(),
+                            QImage::Format_RGB32));
+
+    {
+      OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
+      locker.GetCentralViewport().SetSize(event->size().width(), event->size().height());
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Qt/QCairoWidget.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,88 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+4 * 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 "../../Applications/Generic/NativeStoneApplicationContext.h"
+#include "../../Framework/Wrappers/CairoSurface.h"
+#include "../../Framework/Deprecated/Widgets/IWidget.h"
+
+#include <QWidget>
+#include <memory>
+#include <cassert>
+
+class QCairoWidget : public QWidget
+{
+  Q_OBJECT
+
+private:
+  class StoneObserver : public OrthancStone::IObserver
+  {
+  private:
+    QCairoWidget& that_;
+    
+  public:
+    StoneObserver(QCairoWidget& that,
+                  Deprecated::IViewport& viewport,
+                  OrthancStone::MessageBroker& broker);
+
+    void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message)
+    {
+      that_.OnViewportChanged();
+    }
+  };
+  
+  std::unique_ptr<QImage>         image_;
+  OrthancStone::CairoSurface    surface_;
+  OrthancStone::NativeStoneApplicationContext* context_;
+  std::unique_ptr<StoneObserver>  observer_;
+
+protected:
+  virtual void paintEvent(QPaintEvent *event);
+
+  virtual void resizeEvent(QResizeEvent *event);
+
+  virtual void mouseMoveEvent(QMouseEvent *event);
+
+  virtual void mousePressEvent(QMouseEvent *event);
+
+  virtual void mouseReleaseEvent(QMouseEvent *event);
+
+  virtual void wheelEvent(QWheelEvent *event);
+
+  virtual void keyPressEvent(QKeyEvent *event);
+
+public:
+  explicit QCairoWidget(QWidget *parent);
+ 
+  void SetContext(OrthancStone::NativeStoneApplicationContext& context);
+
+  void OnViewportChanged()
+  {
+    update();  // schedule a repaint (handled by Qt)
+    emit ContentChanged();
+  }
+
+signals:
+  void ContentChanged();
+                                               
+public slots:
+
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Qt/QStoneMainWindow.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,43 @@
+/**
+ * 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/>.
+ **/
+
+#include "QStoneMainWindow.h"
+
+namespace OrthancStone
+{
+
+  QStoneMainWindow::QStoneMainWindow(NativeStoneApplicationContext& context,
+                                     QWidget *parent) :
+    QMainWindow(parent),
+    context_(context),
+    cairoCentralWidget_(NULL)
+  {
+  }
+
+  void QStoneMainWindow::SetCentralStoneWidget(QCairoWidget& centralWidget)
+  {
+    cairoCentralWidget_ = &centralWidget;
+    cairoCentralWidget_->SetContext(context_);
+  }
+
+  QStoneMainWindow::~QStoneMainWindow()
+  {
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Qt/QStoneMainWindow.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,45 @@
+/**
+ * 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 <QMainWindow>
+
+#include "QCairoWidget.h"
+#include "../Generic/NativeStoneApplicationContext.h"
+
+namespace OrthancStone
+{
+  class QStoneMainWindow : public QMainWindow
+  {
+    Q_OBJECT
+
+  private:
+    OrthancStone::NativeStoneApplicationContext& context_;
+    QCairoWidget          *cairoCentralWidget_;
+
+  protected:  // you must inherit this class
+    QStoneMainWindow(NativeStoneApplicationContext& context, QWidget *parent = 0);
+    void SetCentralStoneWidget(QCairoWidget& centralWidget);
+
+  public:
+    virtual ~QStoneMainWindow();
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Qt/QtStoneApplicationRunner.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,67 @@
+/**
+ * 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/>.
+ **/
+
+
+#if ORTHANC_ENABLE_QT != 1
+#error this file shall be included only with the ORTHANC_ENABLE_QT set to 1
+#endif
+
+#include "QtStoneApplicationRunner.h"
+#include <boost/program_options.hpp>
+#include <QApplication>
+
+#include "../../Framework/Deprecated/Toolbox/MessagingToolbox.h"
+
+#include <Core/Logging.h>
+#include <Core/HttpClient.h>
+#include <Core/Toolbox.h>
+#include <Plugins/Samples/Common/OrthancHttpConnection.h>
+#include "../../Platforms/Generic/OracleWebService.h"
+
+
+namespace OrthancStone
+{
+  void QtStoneApplicationRunner::Initialize()
+  {
+  }
+
+  void QtStoneApplicationRunner::DeclareCommandLineOptions(boost::program_options::options_description& options)
+  {
+  }
+
+  void QtStoneApplicationRunner::Run(NativeStoneApplicationContext& context, const std::string& title, int argc, char* argv[])
+  {
+    context.Start();
+
+    QApplication qtApplication(argc, argv);
+    window_.reset(application_.CreateQtMainWindow());
+
+    window_->show();
+    qtApplication.exec();
+
+    context.Stop();
+  }
+
+  void QtStoneApplicationRunner::Finalize()
+  {
+  }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Qt/QtStoneApplicationRunner.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,54 @@
+/**
+ * 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 "../Generic/NativeStoneApplicationRunner.h"
+#include "QStoneMainWindow.h"
+
+#if ORTHANC_ENABLE_QT != 1
+#error this file shall be included only with the ORTHANC_ENABLE_QT set to 1
+#endif
+
+namespace OrthancStone
+{
+  class QtStoneApplicationRunner : public NativeStoneApplicationRunner
+  {
+  protected:
+    std::unique_ptr<QStoneMainWindow> window_;
+
+  public:
+    QtStoneApplicationRunner(MessageBroker& broker,
+                             IStoneApplication& application)
+      : NativeStoneApplicationRunner(broker, application)
+    {
+    }
+
+
+    virtual void Initialize();
+
+    virtual void DeclareCommandLineOptions(boost::program_options::options_description& options);
+    virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters) {}
+    virtual void Run(NativeStoneApplicationContext& context, const std::string& title, int argc, char* argv[]);
+    virtual void Finalize();
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/BasicPetCtFusionApplication.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,202 @@
+/**
+ * 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 "SampleInteractor.h"
+
+#include <Core/Logging.h>
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    class BasicPetCtFusionApplication : public SampleApplicationBase
+    {
+    private:
+      class Interactor : public SampleInteractor
+      {
+      public:
+        static void SetStyle(LayeredSceneWidget& widget,
+                             bool ct,
+                             bool pet)
+        {
+          if (ct)
+          {
+            RenderStyle style;
+            style.windowing_ = ImageWindowing_Bone;
+            widget.SetLayerStyle(0, style);
+          }
+          else
+          {
+            RenderStyle style;
+            style.visible_ = false;
+            widget.SetLayerStyle(0, style);
+          }
+
+          if (ct && pet)
+          {
+            RenderStyle style;
+            style.applyLut_ = true;
+            style.alpha_ = 0.5;
+            widget.SetLayerStyle(1, style);
+          }
+          else if (pet)
+          {
+            RenderStyle style;
+            style.applyLut_ = true;
+            widget.SetLayerStyle(1, style);
+          }
+          else
+          {
+            RenderStyle style;
+            style.visible_ = false;
+            widget.SetLayerStyle(1, style);
+          }
+        }
+
+
+        static bool IsVisible(LayeredSceneWidget& widget,
+                              size_t layer)
+        {
+          RenderStyle style = widget.GetLayerStyle(layer);
+          return style.visible_;
+        }
+
+
+        static void ToggleInterpolation(LayeredSceneWidget& widget,
+                                        size_t layer)
+        {
+          RenderStyle style = widget.GetLayerStyle(layer);
+         
+          if (style.interpolation_ == ImageInterpolation_Bilinear)
+          {
+            style.interpolation_ = ImageInterpolation_Nearest;
+          }
+          else
+          {
+            style.interpolation_ = ImageInterpolation_Bilinear;
+          }
+
+          widget.SetLayerStyle(layer, style);
+        }
+
+
+        Interactor(VolumeImage& volume,
+                   VolumeProjection projection, 
+                   bool reverse) :
+          SampleInteractor(volume, projection, reverse)
+        {
+        }
+
+
+        virtual void KeyPressed(WorldSceneWidget& widget,
+                                char key,
+                                KeyboardModifiers modifiers,
+                                IStatusBar* statusBar)
+        {
+          LayeredSceneWidget& layered = dynamic_cast<LayeredSceneWidget&>(widget);
+
+          switch (key)
+          {
+            case 'c':
+              // Toggle the visibility of the CT layer
+              SetStyle(layered, !IsVisible(layered, 0), IsVisible(layered, 1));
+              break;
+
+            case 'p':
+              // Toggle the visibility of the PET layer
+              SetStyle(layered, IsVisible(layered, 0), !IsVisible(layered, 1));
+              break;
+
+            case 'i':
+            {
+              // Toggle on/off the interpolation
+              ToggleInterpolation(layered, 0);
+              ToggleInterpolation(layered, 1);
+              break;
+            }
+
+            default:
+              break;
+          }
+        }
+      };
+
+
+    public:
+      virtual void DeclareCommandLineOptions(boost::program_options::options_description& options)
+      {
+        boost::program_options::options_description generic("Sample options");
+        generic.add_options()
+          ("ct", boost::program_options::value<std::string>(), 
+           "Orthanc ID of the CT series")
+          ("pet", boost::program_options::value<std::string>(), 
+           "Orthanc ID of the PET series")
+          ("threads", boost::program_options::value<unsigned int>()->default_value(3), 
+           "Number of download threads for the CT series")
+          ;
+
+        options.add(generic);    
+      }
+
+      virtual void Initialize(BasicApplicationContext& context,
+                              IStatusBar& statusBar,
+                              const boost::program_options::variables_map& parameters)
+      {
+        using namespace OrthancStone;
+
+        if (parameters.count("ct") != 1 ||
+            parameters.count("pet") != 1)
+        {
+          LOG(ERROR) << "The series ID is missing";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+
+        std::string ct = parameters["ct"].as<std::string>();
+        std::string pet = parameters["pet"].as<std::string>();
+        unsigned int threads = parameters["threads"].as<unsigned int>();
+
+        VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads);
+        VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1);
+
+        // Take the PET volume as the reference for the slices
+        std::unique_ptr<Interactor> interactor(new Interactor(petVolume, VolumeProjection_Axial, false /* don't reverse normal */));
+
+        std::unique_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget);
+        widget->AddLayer(new VolumeImage::LayerFactory(ctVolume));
+        widget->AddLayer(new VolumeImage::LayerFactory(petVolume));
+        widget->SetSlice(interactor->GetCursor().GetCurrentSlice());
+        widget->SetInteractor(*interactor);
+
+        Interactor::SetStyle(*widget, true, true);   // Initially, show both CT and PET layers
+
+        context.AddInteractor(interactor.release());
+        context.SetCentralWidget(widget.release());
+
+        statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode");
+        statusBar.SetMessage("Use the key \"c\" to show/hide the CT layer");
+        statusBar.SetMessage("Use the key \"p\" to show/hide the PET layer");
+        statusBar.SetMessage("Use the key \"i\" to toggle the smoothing of the images");
+      }
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/CMakeLists.txt	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,292 @@
+# Usage (Linux):
+# to build the WASM samples
+# source ~/Downloads/emsdk/emsdk_env.sh && cmake -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc -DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON
+# to build the Qt samples
+
+cmake_minimum_required(VERSION 2.8.3)
+project(OrthancStone)
+
+include(../../../Resources/CMake/OrthancStoneParameters.cmake)
+
+set(ENABLE_STONE_DEPRECATED ON)  # Need deprecated classes for these samples
+set(EMSCRIPTEN_SET_LLVM_WASM_BACKEND ON)
+
+include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
+DownloadPackage(
+  "a24b8136b8f3bb93f166baf97d9328de"
+  "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip"
+  "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83")
+
+set(ORTHANC_STONE_APPLICATION_RESOURCES
+  UBUNTU_FONT  ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf
+  )
+
+if (OPENSSL_NO_CAPIENG)
+add_definitions(-DOPENSSL_NO_CAPIENG=1)
+endif()
+
+
+# the following block has been borrowed from orthanc/**/Compiler.cmake
+if (MSVC_MULTIPLE_PROCESSES)
+# "If you omit the processMax argument in the /MP option, the
+# compiler obtains the number of effective processors from the
+# operating system, and then creates one process per effective
+# processor"
+# https://blog.kitware.com/cmake-building-with-all-your-cores/
+# https://docs.microsoft.com/en-us/cpp/build/reference/mp-build-with-multiple-processes
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
+endif()
+
+#set(ENABLE_DCMTK ON)
+
+set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application")
+set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application")
+set(ENABLE_WASM OFF CACHE BOOL "Target WASM application")
+
+if (ENABLE_WASM)
+  #####################################################################
+  ## Configuration of the Emscripten compiler for WebAssembly target
+  #####################################################################
+
+  set(WASM_FLAGS "-s WASM=1")
+  set(WASM_FLAGS "${WASM_FLAGS} -s STRICT=1") # drops support for all deprecated build options
+  set(WASM_FLAGS "${WASM_FLAGS} -s FILESYSTEM=1") # if we don't include it, gen_uuid.c fails to build because srand, getpid(), ... are not defined
+  set(WASM_FLAGS "${WASM_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") # actually enable exception catching 
+  set(WASM_FLAGS "${WASM_FLAGS} -s ERROR_ON_MISSING_LIBRARIES=1")
+  
+  if (CMAKE_BUILD_TYPE MATCHES DEBUG)
+    set(WASM_FLAGS "${WASM_FLAGS} -g4") # generate debug information
+    set(WASM_FLAGS "${WASM_FLAGS} -s ASSERTIONS=2") # more runtime checks
+  else()
+    set(WASM_FLAGS "${WASM_FLAGS} -Os") # optimize for web (speed and size)
+  endif()
+
+  set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module")
+
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}")
+
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${WASM_FLAGS}")  # not always clear which flags are for the compiler and which one are for the linker -> pass them all to the linker too
+  # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Applications/Samples/samples-library.js")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"'")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_STACK=128000000")
+
+  add_definitions(-DORTHANC_ENABLE_WASM=1)
+  set(ORTHANC_SANDBOXED ON)
+
+elseif (ENABLE_QT OR ENABLE_SDL)
+
+  set(ENABLE_NATIVE ON)
+  set(ORTHANC_SANDBOXED OFF)
+  set(ENABLE_CRYPTO_OPTIONS ON)
+  set(ENABLE_GOOGLE_TEST ON)
+  set(ENABLE_WEB_CLIENT ON)
+
+else()
+  set(ENABLE_NATIVE ON)
+  set(ENABLE_OPENGL OFF)
+  
+endif()
+
+
+#####################################################################
+## Configuration for Orthanc
+#####################################################################
+
+# include(../../Resources/CMake/Version.cmake)
+
+if (ORTHANC_STONE_VERSION STREQUAL "mainline")
+  set(ORTHANC_FRAMEWORK_VERSION "mainline")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
+else()
+  set(ORTHANC_FRAMEWORK_VERSION "1.4.1")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
+endif()
+
+set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")")
+set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"")
+set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
+
+add_definitions(
+  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
+  )
+
+
+#####################################################################
+## Build a static library containing the Orthanc Stone framework
+#####################################################################
+
+
+LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
+
+include(../../../Resources/CMake/OrthancStoneConfiguration.cmake)
+
+add_library(OrthancStone STATIC
+  ${ORTHANC_STONE_SOURCES}
+  )
+
+#####################################################################
+## Build all the sample applications
+#####################################################################
+
+include_directories(${ORTHANC_STONE_ROOT})
+
+# files common to all samples
+list(APPEND SAMPLE_APPLICATIONS_SOURCES
+  ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleInteractor.h
+  ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleApplicationBase.h
+  )
+
+if (ENABLE_QT)
+  list(APPEND SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleQtApplicationRunner.h
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindow.cpp
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.cpp
+    )
+
+  ORTHANC_QT_WRAP_UI(SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindow.ui
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.ui
+    )
+
+  ORTHANC_QT_WRAP_CPP(SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.h
+    ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.h
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindow.h
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.h
+    )
+endif()
+
+if (ENABLE_NATIVE)
+  list(APPEND SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleMainNative.cpp
+    )
+
+elseif (ENABLE_WASM)
+
+  list(APPEND SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SampleMainWasm.cpp
+    ${STONE_WASM_SOURCES}
+    )
+endif()
+
+
+macro(BuildSingleFileSample Target Header Sample)
+  add_executable(${Target}
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/${Header}
+    ${SAMPLE_APPLICATIONS_SOURCES}
+    )
+  set_target_properties(${Target} PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=${Sample})
+  target_link_libraries(${Target} OrthancStone)
+endmacro()
+
+
+if (ENABLE_SDL)
+  #BuildSingleFileSample(OrthancStoneEmpty EmptyApplication.h 1)
+  #BuildSingleFileSample(OrthancStoneTestPattern TestPatternApplication.h 2)
+  BuildSingleFileSample(OrthancStoneSingleFrame SingleFrameApplication.h 3)
+  #BuildSingleFileSample(OrthancStoneSingleVolume SingleVolumeApplication.h 4)
+  #BuildSingleFileSample(OrthancStoneBasicPetCtFusion 5)
+  #BuildSingleFileSample(OrthancStoneSynchronizedSeries 6)
+  #BuildSingleFileSample(OrthancStoneLayoutPetCtFusion 7)
+  BuildSingleFileSample(OrthancStoneSimpleViewerSingleFile SimpleViewerApplicationSingleFile.h 8)  # we keep that one just as a sample before we convert another sample to this pattern
+  BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9)
+endif()
+  
+##### SimpleViewer sample (Qt and WASM only) #######
+
+if (ENABLE_QT OR ENABLE_WASM)
+
+    if (ENABLE_QT)
+      list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.cpp
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/mainQt.cpp
+        )
+
+      ORTHANC_QT_WRAP_UI(SIMPLE_VIEWER_APPLICATION_SOURCES
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui
+        )
+
+      ORTHANC_QT_WRAP_CPP(SIMPLE_VIEWER_APPLICATION_SOURCES
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.h
+        )
+
+elseif (ENABLE_WASM)
+        list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES
+            ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Wasm/mainWasm.cpp
+            ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp
+            ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h
+            ${STONE_WASM_SOURCES}
+          )
+    endif()
+
+    add_executable(OrthancStoneSimpleViewer
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/AppStatus.h
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.cpp
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.h
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.cpp
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.h
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.cpp
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.h
+      ${SIMPLE_VIEWER_APPLICATION_SOURCES}
+      )
+    target_link_libraries(OrthancStoneSimpleViewer OrthancStone)
+
+    BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9)
+endif()
+
+#####################################################################
+## Build the unit tests
+#####################################################################
+
+if (ENABLE_NATIVE)
+  add_executable(UnitTests
+    ${GOOGLE_TEST_SOURCES}
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/GenericToolboxTests.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/ImageToolboxTests.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/PixelTestPatternsTests.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStrategy.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStructureSet.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/UnitTestsMain.cpp
+    )
+
+  target_link_libraries(UnitTests OrthancStone)
+
+  add_custom_command(
+    TARGET UnitTests
+    POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E copy
+      "${ORTHANC_STONE_ROOT}/UnitTestsSources/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json" 
+      "$<TARGET_FILE_DIR:UnitTests>/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json"
+    )
+
+endif()
+
+#####################################################################
+## Generate the documentation if Doxygen is present
+#####################################################################
+
+find_package(Doxygen)
+if (DOXYGEN_FOUND)
+  configure_file(
+    ${ORTHANC_STONE_ROOT}/Resources/OrthancStone.doxygen
+    ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen
+    @ONLY)
+
+  add_custom_target(doc
+    ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen
+    COMMENT "Generating documentation with Doxygen" VERBATIM
+    )
+else()
+  message("Doxygen not found. The documentation will not be built.")
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/CMakeLists.txt.old	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,248 @@
+# Usage: see README file
+
+cmake_minimum_required(VERSION 2.8.3)
+
+# Automatically link Qt executables to qtmain target on Windows
+# ("OLD" == do not link)
+if(POLICY CMP0020)
+  cmake_policy(SET CMP0020 OLD)
+endif()
+
+# Only interpret if() arguments as variables or keywords when unquoted.
+# NEW = do NOT dereference *quoted* variables
+if(POLICY CMP0054)
+  cmake_policy(SET CMP0054 NEW)
+endif()
+
+project(OrthancStone)
+
+include(../../Resources/CMake/OrthancStoneParameters.cmake)
+
+#set(ENABLE_DCMTK ON)
+
+set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application")
+set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application")
+set(ENABLE_WASM OFF CACHE BOOL "Target WASM application")
+
+# TODO: replace or compute STONE_SOURCES_DIR from CMAKE_CURRENT_LIST_FILE
+
+if (ENABLE_WASM)
+  #####################################################################
+  ## Configuration of the Emscripten compiler for WebAssembly target
+  #####################################################################
+
+  set(WASM_FLAGS "-s WASM=1 -O0 -g0")
+  message("*****************************************************************************")
+  message("WARNING: optimizations are disabled in emcc!!! Enable them for production use")
+  message("*****************************************************************************")
+  set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module")
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js  -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
+
+  # Handling of memory
+  #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")  # Resize
+  #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912")  # 512MB
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"' -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=536870912 -s TOTAL_STACK=128000000")  # 512MB + resize
+  #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=1073741824")  # 1GB + resize
+
+  # To debug exceptions
+  #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DEMANGLE_SUPPORT=1 -s ASSERTIONS=2")
+
+  add_definitions(-DORTHANC_ENABLE_WASM=1)
+  set(ORTHANC_SANDBOXED ON)
+
+elseif (ENABLE_QT OR ENABLE_SDL)
+
+  set(ENABLE_NATIVE ON)
+  set(ORTHANC_SANDBOXED OFF)
+  set(ENABLE_CRYPTO_OPTIONS ON)
+  set(ENABLE_GOOGLE_TEST ON)
+  set(ENABLE_WEB_CLIENT ON)
+
+endif()
+
+#####################################################################
+## Configuration for Orthanc
+#####################################################################
+
+# include(../../Resources/CMake/Version.cmake)
+
+if (ORTHANC_STONE_VERSION STREQUAL "mainline")
+  set(ORTHANC_FRAMEWORK_VERSION "mainline")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
+else()
+  set(ORTHANC_FRAMEWORK_VERSION "1.4.1")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
+endif()
+
+set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")")
+set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"")
+set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
+
+#####################################################################
+## Build a static library containing the Orthanc Stone framework
+#####################################################################
+
+LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
+
+include(../../Resources/CMake/OrthancStoneConfiguration.cmake)
+
+add_library(OrthancStone STATIC
+  ${ORTHANC_STONE_SOURCES}
+  )
+
+#####################################################################
+## Build all the sample applications
+#####################################################################
+
+include_directories(${ORTHANC_STONE_ROOT})
+
+# files common to all samples
+list(APPEND SAMPLE_APPLICATIONS_SOURCES
+  ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleInteractor.h
+  ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleApplicationBase.h
+  )
+
+if (ENABLE_QT)
+  list(APPEND SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleQtApplicationRunner.h
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.cpp
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.cpp
+    )
+
+  ORTHANC_QT_WRAP_UI(SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.ui
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.ui
+    )
+
+  ORTHANC_QT_WRAP_CPP(SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.h
+    ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.h
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.h
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.h
+    )
+endif()
+
+if (ENABLE_NATIVE)
+  list(APPEND SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleMainNative.cpp
+    )
+
+elseif (ENABLE_WASM)
+
+  list(APPEND SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleMainWasm.cpp
+    ${STONE_WASM_SOURCES}
+    )
+endif()
+
+
+macro(BuildSingleFileSample Target Header Sample)
+  add_executable(${Target}
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/${Header}
+    ${SAMPLE_APPLICATIONS_SOURCES}
+    )
+  set_target_properties(${Target} PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=${Sample})
+  target_link_libraries(${Target} OrthancStone)
+  
+  if (ENABLE_QT AND (CMAKE_SYSTEM_NAME STREQUAL "Windows"))
+    message("(ENABLE_QT and (CMAKE_SYSTEM_NAME matches \"Windows\")) is true")
+    add_custom_command(
+      TARGET ${Target} POST_BUILD
+      COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Core> $<TARGET_FILE_DIR:${Target}>
+      COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Widgets> $<TARGET_FILE_DIR:${Target}>
+      COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Qt5::Gui> $<TARGET_FILE_DIR:${Target}>
+    )
+  endif()
+endmacro()
+
+#BuildSingleFileSample(OrthancStoneEmpty EmptyApplication.h 1)
+#BuildSingleFileSample(OrthancStoneTestPattern TestPatternApplication.h 2)
+BuildSingleFileSample(OrthancStoneSingleFrame SingleFrameApplication.h 3)
+#BuildSingleFileSample(OrthancStoneSingleVolume SingleVolumeApplication.h 4)
+#BuildSingleFileSample(OrthancStoneBasicPetCtFusion 5)
+#BuildSingleFileSample(OrthancStoneSynchronizedSeries 6)
+#BuildSingleFileSample(OrthancStoneLayoutPetCtFusion 7)
+BuildSingleFileSample(OrthancStoneSimpleViewerSingleFile SimpleViewerApplicationSingleFile.h 8)  # we keep that one just as a sample before we convert another sample to this pattern
+BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9)
+
+##### SimpleViewer sample (Qt and WASM only) #######
+
+if (ENABLE_QT OR ENABLE_WASM)
+
+      # GenerateCodeFromFlatBufferSchema("${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ApplicationCommands.fbs")
+
+      list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES ${FLATC_AUTOGENERATED_SOURCES})
+      message(STATUS "SIMPLE_VIEWER_APPLICATION_SOURCES = ${SIMPLE_VIEWER_APPLICATION_SOURCES}")
+      message(STATUS "FLATC_AUTOGENERATED_SOURCES = ${FLATC_AUTOGENERATED_SOURCES}")
+
+    if (ENABLE_QT)
+      list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.cpp
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.ui
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/mainQt.cpp
+        )
+
+      ORTHANC_QT_WRAP_UI(SIMPLE_VIEWER_APPLICATION_SOURCES
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.ui
+        )
+
+      ORTHANC_QT_WRAP_CPP(SIMPLE_VIEWER_APPLICATION_SOURCES
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.h
+        )
+
+elseif (ENABLE_WASM)
+        list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES
+            ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/mainWasm.cpp
+            ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp
+            ${STONE_WASM_SOURCES}
+          )
+    endif()
+
+    add_executable(OrthancStoneSimpleViewer
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/AppStatus.h
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Messages.h
+      ${SIMPLE_VIEWER_APPLICATION_SOURCES}
+      )
+    target_link_libraries(OrthancStoneSimpleViewer OrthancStone)
+
+endif()
+
+#####################################################################
+## Build the unit tests
+#####################################################################
+
+if (ENABLE_NATIVE)
+  add_executable(UnitTests
+    ${GOOGLE_TEST_SOURCES}
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestExceptions.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/UnitTestsMain.cpp
+    )
+
+  target_link_libraries(UnitTests OrthancStone)
+endif()
+
+#####################################################################
+## Generate the documentation if Doxygen is present
+#####################################################################
+
+find_package(Doxygen)
+if (DOXYGEN_FOUND)
+  configure_file(
+    ${ORTHANC_STONE_ROOT}/Resources/OrthancStone.doxygen
+    ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen
+    @ONLY)
+
+  add_custom_target(doc
+    ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen
+    COMMENT "Generating documentation with Doxygen" VERBATIM
+    )
+else()
+  message("Doxygen not found. The documentation will not be built.")
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/EmptyApplication.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,58 @@
+/**
+ * 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 "SampleApplicationBase.h"
+
+#include "../../../Framework/Widgets/EmptyWidget.h"
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    class EmptyApplication : public SampleApplicationBase
+    {
+    public:
+      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
+      {
+        boost::program_options::options_description generic("Sample options");
+        generic.add_options()
+          ("red", boost::program_options::value<int>()->default_value(255), "Background color: red channel")
+          ("green", boost::program_options::value<int>()->default_value(0), "Background color: green channel")
+          ("blue", boost::program_options::value<int>()->default_value(0), "Background color: blue channel")
+          ;
+
+        options.add(generic);    
+      }
+
+      virtual void Initialize(IStatusBar& statusBar,
+                              const boost::program_options::variables_map& parameters)
+      {
+        int red = parameters["red"].as<int>();
+        int green = parameters["green"].as<int>();
+        int blue = parameters["blue"].as<int>();
+
+        context_->SetCentralWidget(new EmptyWidget(red, green, blue));
+      }
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/LayoutPetCtFusionApplication.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,398 @@
+/**
+ * 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 "SampleInteractor.h"
+
+#include "../../../Framework/Layers/ReferenceLineFactory.h"
+#include "../../../Framework/Layers/DicomStructureSetSlicer.h"
+#include "../../../Framework/Widgets/LayoutWidget.h"
+
+#include <Core/Logging.h>
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    class LayoutPetCtFusionApplication : 
+      public SampleApplicationBase,
+      public LayeredSceneWidget::ISliceObserver,
+      public WorldSceneWidget::IWorldObserver
+    {
+    private:
+      class Interactor : public SampleInteractor
+      {
+      private:
+        LayoutPetCtFusionApplication& that_;
+
+      public:
+        Interactor(LayoutPetCtFusionApplication& that,
+                   VolumeImage& volume,
+                   VolumeProjection projection, 
+                   bool reverse) :
+          SampleInteractor(volume, projection, reverse),
+          that_(that)
+        {
+        }
+
+        virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
+                                                            const SliceGeometry& slice,
+                                                            const ViewportGeometry& view,
+                                                            MouseButton button,
+                                                            double x,
+                                                            double y,
+                                                            IStatusBar* statusBar)
+        {
+          if (button == MouseButton_Left)
+          {
+            // Center the sibling views over the clicked point
+            Vector p = slice.MapSliceToWorldCoordinates(x, y);
+
+            if (statusBar != NULL)
+            {
+              char buf[64];
+              sprintf(buf, "Click on coordinates (%.02f,%.02f,%.02f) in cm", p[0] / 10.0, p[1] / 10.0, p[2] / 10.0);
+              statusBar->SetMessage(buf);
+            }
+
+            that_.interactorAxial_->LookupSliceContainingPoint(*that_.ctAxial_, p);
+            that_.interactorCoronal_->LookupSliceContainingPoint(*that_.ctCoronal_, p);
+            that_.interactorSagittal_->LookupSliceContainingPoint(*that_.ctSagittal_, p);
+          }
+
+          return NULL;
+        }
+
+        virtual void KeyPressed(WorldSceneWidget& widget,
+                                char key,
+                                KeyboardModifiers modifiers,
+                                IStatusBar* statusBar)
+        {
+          if (key == 's')
+          {
+            that_.FitContent();
+          }
+        }
+      };
+
+      bool                 processingEvent_;
+      Interactor*          interactorAxial_;
+      Interactor*          interactorCoronal_;
+      Interactor*          interactorSagittal_;
+      LayeredSceneWidget*  ctAxial_;
+      LayeredSceneWidget*  ctCoronal_;
+      LayeredSceneWidget*  ctSagittal_;
+      LayeredSceneWidget*  petAxial_;
+      LayeredSceneWidget*  petCoronal_;
+      LayeredSceneWidget*  petSagittal_;
+      LayeredSceneWidget*  fusionAxial_;
+      LayeredSceneWidget*  fusionCoronal_;
+      LayeredSceneWidget*  fusionSagittal_;
+
+
+      void FitContent()
+      {
+        petAxial_->FitContent();
+        petCoronal_->FitContent();
+        petSagittal_->FitContent();
+      }
+
+
+      void AddLayer(LayeredSceneWidget& widget,
+                    VolumeImage& volume,
+                    bool isCt)
+      {
+        size_t layer;
+        widget.AddLayer(layer, new VolumeImage::LayerFactory(volume));
+
+        if (isCt)
+        {
+          RenderStyle style; 
+          style.windowing_ = ImageWindowing_Bone;
+          widget.SetLayerStyle(layer, style);
+        }
+        else
+        {
+          RenderStyle style; 
+          style.applyLut_ = true;
+          style.alpha_ = (layer == 0 ? 1.0f : 0.5f);
+          widget.SetLayerStyle(layer, style);
+        }
+      }
+
+
+      void ConnectSiblingLocations(LayeredSceneWidget& axial,
+                                   LayeredSceneWidget& coronal,
+                                   LayeredSceneWidget& sagittal)
+      {
+        ReferenceLineFactory::Configure(axial, coronal);
+        ReferenceLineFactory::Configure(axial, sagittal);
+        ReferenceLineFactory::Configure(coronal, sagittal);
+      }
+
+
+      void SynchronizeView(const WorldSceneWidget& source,
+                           const ViewportGeometry& view,
+                           LayeredSceneWidget& widget1,
+                           LayeredSceneWidget& widget2,
+                           LayeredSceneWidget& widget3)
+      {
+        if (&source == &widget1 ||
+            &source == &widget2 ||
+            &source == &widget3)
+        {
+          if (&source != &widget1)
+          {
+            widget1.SetView(view);
+          }
+
+          if (&source != &widget2)
+          {
+            widget2.SetView(view);
+          }
+
+          if (&source != &widget3)
+          {
+            widget3.SetView(view);
+          }
+        }
+      }
+
+
+      void SynchronizeSlice(const LayeredSceneWidget& source,
+                            const SliceGeometry& slice,
+                            LayeredSceneWidget& widget1,
+                            LayeredSceneWidget& widget2,
+                            LayeredSceneWidget& widget3)
+      {
+        if (&source == &widget1 ||
+            &source == &widget2 ||
+            &source == &widget3)
+        {
+          if (&source != &widget1)
+          {
+            widget1.SetSlice(slice);
+          }
+
+          if (&source != &widget2)
+          {
+            widget2.SetSlice(slice);
+          }
+
+          if (&source != &widget3)
+          {
+            widget3.SetSlice(slice);
+          }
+        }
+      }
+
+
+      LayeredSceneWidget* CreateWidget()
+      {
+        std::unique_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget);
+        widget->Register(dynamic_cast<WorldSceneWidget::IWorldObserver&>(*this));
+        widget->Register(dynamic_cast<LayeredSceneWidget::ISliceObserver&>(*this));
+        return widget.release();
+      }
+
+
+      void CreateLayout(BasicApplicationContext& context)
+      {
+        std::unique_ptr<OrthancStone::LayoutWidget> layout(new OrthancStone::LayoutWidget);
+        layout->SetBackgroundCleared(true);
+        //layout->SetBackgroundColor(255,0,0);
+        layout->SetPadding(5);
+
+        OrthancStone::LayoutWidget& layoutA = dynamic_cast<OrthancStone::LayoutWidget&>
+          (layout->AddWidget(new OrthancStone::LayoutWidget));
+        layoutA.SetPadding(0, 0, 0, 0, 5);
+        layoutA.SetVertical();
+        petAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutA.AddWidget(CreateWidget()));
+        OrthancStone::LayoutWidget& layoutA2 = dynamic_cast<OrthancStone::LayoutWidget&>
+          (layoutA.AddWidget(new OrthancStone::LayoutWidget));
+        layoutA2.SetPadding(0, 0, 0, 0, 5);
+        petSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutA2.AddWidget(CreateWidget()));
+        petCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutA2.AddWidget(CreateWidget()));
+
+        OrthancStone::LayoutWidget& layoutB = dynamic_cast<OrthancStone::LayoutWidget&>
+          (layout->AddWidget(new OrthancStone::LayoutWidget));
+        layoutB.SetPadding(0, 0, 0, 0, 5);
+        layoutB.SetVertical();
+        ctAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutB.AddWidget(CreateWidget()));
+        OrthancStone::LayoutWidget& layoutB2 = dynamic_cast<OrthancStone::LayoutWidget&>
+          (layoutB.AddWidget(new OrthancStone::LayoutWidget));
+        layoutB2.SetPadding(0, 0, 0, 0, 5);
+        ctSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutB2.AddWidget(CreateWidget()));
+        ctCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutB2.AddWidget(CreateWidget()));
+
+        OrthancStone::LayoutWidget& layoutC = dynamic_cast<OrthancStone::LayoutWidget&>
+          (layout->AddWidget(new OrthancStone::LayoutWidget));
+        layoutC.SetPadding(0, 0, 0, 0, 5);
+        layoutC.SetVertical();
+        fusionAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutC.AddWidget(CreateWidget()));
+        OrthancStone::LayoutWidget& layoutC2 = dynamic_cast<OrthancStone::LayoutWidget&>
+          (layoutC.AddWidget(new OrthancStone::LayoutWidget));
+        layoutC2.SetPadding(0, 0, 0, 0, 5);
+        fusionSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutC2.AddWidget(CreateWidget()));
+        fusionCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutC2.AddWidget(CreateWidget()));
+  
+        context.SetCentralWidget(layout.release());
+      }
+
+
+    public:
+      virtual void DeclareCommandLineOptions(boost::program_options::options_description& options)
+      {
+        boost::program_options::options_description generic("Sample options");
+        generic.add_options()
+          ("ct", boost::program_options::value<std::string>(), 
+           "Orthanc ID of the CT series")
+          ("pet", boost::program_options::value<std::string>(), 
+           "Orthanc ID of the PET series")
+          ("rt", boost::program_options::value<std::string>(), 
+           "Orthanc ID of the DICOM RT-STRUCT series (optional)")
+          ("threads", boost::program_options::value<unsigned int>()->default_value(3), 
+           "Number of download threads for the CT series")
+          ;
+
+        options.add(generic);    
+      }
+
+      virtual void Initialize(BasicApplicationContext& context,
+                              IStatusBar& statusBar,
+                              const boost::program_options::variables_map& parameters)
+      {
+        using namespace OrthancStone;
+
+        processingEvent_ = true;
+
+        if (parameters.count("ct") != 1 ||
+            parameters.count("pet") != 1)
+        {
+          LOG(ERROR) << "The series ID is missing";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+
+        std::string ct = parameters["ct"].as<std::string>();
+        std::string pet = parameters["pet"].as<std::string>();
+        unsigned int threads = parameters["threads"].as<unsigned int>();
+
+        VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads);
+        VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1);
+
+        // Take the PET volume as the reference for the slices
+        interactorAxial_ = &dynamic_cast<Interactor&>
+          (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Axial, false)));
+        interactorCoronal_ = &dynamic_cast<Interactor&>
+          (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Coronal, false)));
+        interactorSagittal_ = &dynamic_cast<Interactor&>
+          (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Sagittal, true)));
+
+        CreateLayout(context);
+
+        AddLayer(*ctAxial_, ctVolume, true);
+        AddLayer(*ctCoronal_, ctVolume, true);
+        AddLayer(*ctSagittal_, ctVolume, true);
+
+        AddLayer(*petAxial_, petVolume, false);
+        AddLayer(*petCoronal_, petVolume, false);
+        AddLayer(*petSagittal_, petVolume, false);
+
+        AddLayer(*fusionAxial_, ctVolume, true);
+        AddLayer(*fusionAxial_, petVolume, false);
+        AddLayer(*fusionCoronal_, ctVolume, true);
+        AddLayer(*fusionCoronal_, petVolume, false);
+        AddLayer(*fusionSagittal_, ctVolume, true);
+        AddLayer(*fusionSagittal_, petVolume, false);
+
+        if (parameters.count("rt") == 1)
+        {
+          DicomStructureSet& rtStruct = context.AddStructureSet(parameters["rt"].as<std::string>());
+
+          Vector p = rtStruct.GetStructureCenter(0);
+          interactorAxial_->GetCursor().LookupSliceContainingPoint(p);
+
+          ctAxial_->AddLayer(new DicomStructureSetSlicer(rtStruct));
+          petAxial_->AddLayer(new DicomStructureSetSlicer(rtStruct));
+          fusionAxial_->AddLayer(new DicomStructureSetSlicer(rtStruct));
+        }        
+
+        ConnectSiblingLocations(*ctAxial_, *ctCoronal_, *ctSagittal_); 
+        ConnectSiblingLocations(*petAxial_, *petCoronal_, *petSagittal_); 
+        ConnectSiblingLocations(*fusionAxial_, *fusionCoronal_, *fusionSagittal_); 
+
+        interactorAxial_->AddWidget(*ctAxial_);
+        interactorAxial_->AddWidget(*petAxial_);
+        interactorAxial_->AddWidget(*fusionAxial_);
+        
+        interactorCoronal_->AddWidget(*ctCoronal_);
+        interactorCoronal_->AddWidget(*petCoronal_);
+        interactorCoronal_->AddWidget(*fusionCoronal_);
+        
+        interactorSagittal_->AddWidget(*ctSagittal_);
+        interactorSagittal_->AddWidget(*petSagittal_);
+        interactorSagittal_->AddWidget(*fusionSagittal_);
+
+        processingEvent_ = false;
+
+        statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode");
+        statusBar.SetMessage("Use the key \"s\" to reinitialize the layout");
+      }
+
+      virtual void NotifySizeChange(const WorldSceneWidget& source,
+                                    ViewportGeometry& view)
+      {
+        view.FitContent();
+      }
+
+      virtual void NotifyViewChange(const WorldSceneWidget& source,
+                                    const ViewportGeometry& view)
+      {
+        if (!processingEvent_)  // Avoid reentrant calls
+        {
+          processingEvent_ = true;
+
+          SynchronizeView(source, view, *ctAxial_, *petAxial_, *fusionAxial_);
+          SynchronizeView(source, view, *ctCoronal_, *petCoronal_, *fusionCoronal_);
+          SynchronizeView(source, view, *ctSagittal_, *petSagittal_, *fusionSagittal_);
+          
+          processingEvent_ = false;
+        }
+      }
+
+      virtual void NotifySliceContentChange(const LayeredSceneWidget& source,
+                                     const SliceGeometry& slice)
+      {
+        if (!processingEvent_)  // Avoid reentrant calls
+        {
+          processingEvent_ = true;
+
+          SynchronizeSlice(source, slice, *ctAxial_, *petAxial_, *fusionAxial_);
+          SynchronizeSlice(source, slice, *ctCoronal_, *petCoronal_, *fusionCoronal_);
+          SynchronizeSlice(source, slice, *ctSagittal_, *petSagittal_, *fusionSagittal_);
+          
+          processingEvent_ = false;
+        }
+      }
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Qt/SampleMainWindow.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,53 @@
+/**
+ * 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/>.
+ **/
+
+#include "SampleMainWindow.h"
+
+/**
+ * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as
+ * this makes CMake unable to detect when the UI file changes.
+ **/
+#include <ui_SampleMainWindow.h>
+#include "../../../Applications/Samples/SampleApplicationBase.h"
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+
+    SampleMainWindow::SampleMainWindow(
+      OrthancStone::NativeStoneApplicationContext& context,
+      OrthancStone::Samples::SampleSingleCanvasApplicationBase& stoneSampleApplication,
+      QWidget *parent) :
+      QStoneMainWindow(context, parent),
+      ui_(new Ui::SampleMainWindow),
+      stoneSampleApplication_(stoneSampleApplication)
+    {
+      ui_->setupUi(this);
+      SetCentralStoneWidget(*ui_->cairoCentralWidget);
+    }
+
+    SampleMainWindow::~SampleMainWindow()
+    {
+      delete ui_;
+    }
+
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Qt/SampleMainWindow.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,50 @@
+/**
+ * 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 "../../../Qt/QCairoWidget.h"
+#include "../../../Qt/QStoneMainWindow.h"
+
+namespace Ui 
+{
+  class SampleMainWindow;
+}
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+
+    class SampleSingleCanvasApplicationBase;
+
+    class SampleMainWindow : public QStoneMainWindow
+    {
+      Q_OBJECT
+
+    private:
+      Ui::SampleMainWindow*   ui_;
+      SampleSingleCanvasApplicationBase&  stoneSampleApplication_;
+
+    public:
+      explicit SampleMainWindow(OrthancStone::NativeStoneApplicationContext& context, SampleSingleCanvasApplicationBase& stoneSampleApplication, QWidget *parent = 0);
+      ~SampleMainWindow();
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Qt/SampleMainWindow.ui	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SampleMainWindow</class>
+ <widget class="QMainWindow" name="SampleMainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>903</width>
+    <height>634</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>500</width>
+    <height>300</height>
+   </size>
+  </property>
+  <property name="baseSize">
+   <size>
+    <width>500</width>
+    <height>300</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Stone of Orthanc</string>
+  </property>
+  <property name="layoutDirection">
+   <enum>Qt::LeftToRight</enum>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <property name="sizePolicy">
+    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+     <horstretch>0</horstretch>
+     <verstretch>0</verstretch>
+    </sizepolicy>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::LeftToRight</enum>
+   </property>
+   <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0">
+    <property name="sizeConstraint">
+     <enum>QLayout::SetDefaultConstraint</enum>
+    </property>
+    <item>
+     <widget class="QCairoWidget" name="cairoCentralWidget">
+      <property name="minimumSize">
+       <size>
+        <width>0</width>
+        <height>500</height>
+       </size>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>903</width>
+     <height>22</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuTest">
+    <property name="title">
+     <string>Test</string>
+    </property>
+   </widget>
+   <addaction name="menuTest"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QCairoWidget</class>
+   <extends>QGraphicsView</extends>
+   <header location="global">QCairoWidget.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,96 @@
+/**
+ * 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/>.
+ **/
+
+#include "SampleMainWindow.h"
+
+/**
+ * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as
+ * this makes CMake unable to detect when the UI file changes.
+ **/
+#include <ui_SampleMainWindowWithButtons.h>
+#include "../../../Applications/Samples/SampleApplicationBase.h"
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+
+    SampleMainWindowWithButtons::SampleMainWindowWithButtons(
+      OrthancStone::NativeStoneApplicationContext& context,
+      OrthancStone::Samples::SampleSingleCanvasWithButtonsApplicationBase& stoneSampleApplication,
+      QWidget *parent) :
+      QStoneMainWindow(context, parent),
+      ui_(new Ui::SampleMainWindowWithButtons),
+      stoneSampleApplication_(stoneSampleApplication)
+    {
+      ui_->setupUi(this);
+      SetCentralStoneWidget(*ui_->cairoCentralWidget);
+
+#if QT_VERSION >= 0x050000
+      connect(ui_->toolButton1, &QToolButton::clicked, this, &SampleMainWindowWithButtons::tool1Clicked);
+      connect(ui_->toolButton2, &QToolButton::clicked, this, &SampleMainWindowWithButtons::tool2Clicked);
+      connect(ui_->pushButton1, &QPushButton::clicked, this, &SampleMainWindowWithButtons::pushButton1Clicked);
+      connect(ui_->pushButton1, &QPushButton::clicked, this, &SampleMainWindowWithButtons::pushButton2Clicked);
+#else
+      connect(ui_->toolButton1, SIGNAL(clicked()), this, SLOT(tool1Clicked()));
+      connect(ui_->toolButton2, SIGNAL(clicked()), this, SLOT(tool2Clicked()));
+      connect(ui_->pushButton1, SIGNAL(clicked()), this, SLOT(pushButton1Clicked()));
+      connect(ui_->pushButton1, SIGNAL(clicked()), this, SLOT(pushButton2Clicked()));
+#endif
+
+      std::string pushButton1Name;
+      std::string pushButton2Name;
+      std::string tool1Name;
+      std::string tool2Name;
+      stoneSampleApplication_.GetButtonNames(pushButton1Name, pushButton2Name, tool1Name, tool2Name);
+
+      ui_->toolButton1->setText(QString::fromStdString(tool1Name));
+      ui_->toolButton2->setText(QString::fromStdString(tool2Name));
+      ui_->pushButton1->setText(QString::fromStdString(pushButton1Name));
+      ui_->pushButton2->setText(QString::fromStdString(pushButton2Name));
+    }
+
+    SampleMainWindowWithButtons::~SampleMainWindowWithButtons()
+    {
+      delete ui_;
+    }
+
+    void SampleMainWindowWithButtons::tool1Clicked()
+    {
+      stoneSampleApplication_.OnTool1Clicked();
+    }
+
+    void SampleMainWindowWithButtons::tool2Clicked()
+    {
+      stoneSampleApplication_.OnTool2Clicked();
+    }
+
+    void SampleMainWindowWithButtons::pushButton1Clicked()
+    {
+      stoneSampleApplication_.OnPushButton1Clicked();
+    }
+
+    void SampleMainWindowWithButtons::pushButton2Clicked()
+    {
+      stoneSampleApplication_.OnPushButton2Clicked();
+    }
+
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,56 @@
+/**
+ * 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 "../../../Qt/QCairoWidget.h"
+#include "../../../Qt/QStoneMainWindow.h"
+
+namespace Ui 
+{
+  class SampleMainWindowWithButtons;
+}
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+
+    class SampleSingleCanvasWithButtonsApplicationBase;
+
+    class SampleMainWindowWithButtons : public QStoneMainWindow
+    {
+      Q_OBJECT
+
+    private:
+      Ui::SampleMainWindowWithButtons*   ui_;
+      SampleSingleCanvasWithButtonsApplicationBase&  stoneSampleApplication_;
+
+    public:
+      explicit SampleMainWindowWithButtons(OrthancStone::NativeStoneApplicationContext& context, SampleSingleCanvasWithButtonsApplicationBase& stoneSampleApplication, QWidget *parent = 0);
+      ~SampleMainWindowWithButtons();
+
+    private slots:
+      void tool1Clicked();
+      void tool2Clicked();
+      void pushButton1Clicked();
+      void pushButton2Clicked();
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.ui	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SampleMainWindowWithButtons</class>
+ <widget class="QMainWindow" name="SampleMainWindowWithButtons">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>903</width>
+    <height>634</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>500</width>
+    <height>300</height>
+   </size>
+  </property>
+  <property name="baseSize">
+   <size>
+    <width>500</width>
+    <height>300</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Stone of Orthanc</string>
+  </property>
+  <property name="layoutDirection">
+   <enum>Qt::LeftToRight</enum>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <property name="sizePolicy">
+    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+     <horstretch>0</horstretch>
+     <verstretch>0</verstretch>
+    </sizepolicy>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::LeftToRight</enum>
+   </property>
+   <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0">
+    <property name="sizeConstraint">
+     <enum>QLayout::SetDefaultConstraint</enum>
+    </property>
+    <item>
+     <widget class="QCairoWidget" name="cairoCentralWidget">
+      <property name="minimumSize">
+       <size>
+        <width>0</width>
+        <height>500</height>
+       </size>
+      </property>
+     </widget>
+    </item>
+    <item>
+     <widget class="QGroupBox" name="horizontalGroupBox">
+      <property name="minimumSize">
+       <size>
+        <width>0</width>
+        <height>100</height>
+       </size>
+      </property>
+      <property name="maximumSize">
+       <size>
+        <width>16777215</width>
+        <height>100</height>
+       </size>
+      </property>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <item>
+        <widget class="QToolButton" name="toolButton1">
+         <property name="text">
+          <string>tool1</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="toolButton2">
+         <property name="text">
+          <string>tool2</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="pushButton1">
+         <property name="text">
+          <string>action1</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="pushButton2">
+         <property name="text">
+          <string>action2</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>903</width>
+     <height>22</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuTest">
+    <property name="title">
+     <string>Test</string>
+    </property>
+   </widget>
+   <addaction name="menuTest"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QCairoWidget</class>
+   <extends>QGraphicsView</extends>
+   <header location="global">QCairoWidget.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Qt/SampleQtApplicationRunner.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,50 @@
+/**
+ * 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 "../../../Qt/QtStoneApplicationRunner.h"
+
+#if ORTHANC_ENABLE_QT != 1
+#error this file shall be included only with the ORTHANC_ENABLE_QT set to 1
+#endif
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    class SampleQtApplicationRunner : public OrthancStone::QtStoneApplicationRunner
+    {
+    protected:
+      virtual void InitializeMainWindow(OrthancStone::NativeStoneApplicationContext& context)
+      {
+        window_.reset(application_.CreateQtMainWindow());
+      }
+    public:
+      SampleQtApplicationRunner(MessageBroker& broker,
+                                SampleApplicationBase& application)
+        : OrthancStone::QtStoneApplicationRunner(broker, application)
+      {
+      }
+
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SampleApplicationBase.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,133 @@
+/**
+ * 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 "../../../Applications/IStoneApplication.h"
+#include "../../../Framework/Deprecated/Widgets/WorldSceneWidget.h"
+
+#if ORTHANC_ENABLE_WASM==1
+#include "../../../Platforms/Wasm/WasmPlatformApplicationAdapter.h"
+#include "../../../Platforms/Wasm/Defaults.h"
+#endif
+
+#if ORTHANC_ENABLE_QT==1
+#include "Qt/SampleMainWindow.h"
+#include "Qt/SampleMainWindowWithButtons.h"
+#endif
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    class SampleApplicationBase : public IStoneApplication
+    {
+    private:
+      boost::shared_ptr<Deprecated::IWidget>  mainWidget_;
+
+    public:
+      virtual void Initialize(StoneApplicationContext* context,
+                              Deprecated::IStatusBar& statusBar,
+                              const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE
+      {
+      }
+
+      virtual std::string GetTitle() const ORTHANC_OVERRIDE
+      {
+        return "Stone of Orthanc - Sample";
+      }
+
+      /**
+       * In the basic samples, the commands are handled by the platform adapter and NOT
+       * by the application handler
+      */
+      virtual void HandleSerializedMessage(const char* data) ORTHANC_OVERRIDE {};
+
+
+      virtual void Finalize() ORTHANC_OVERRIDE {}
+
+      virtual void SetCentralWidget(boost::shared_ptr<Deprecated::IWidget> widget) ORTHANC_OVERRIDE
+      {
+        mainWidget_ = widget;
+      }
+
+      virtual boost::shared_ptr<Deprecated::IWidget> GetCentralWidget() ORTHANC_OVERRIDE
+      {
+        return mainWidget_;
+      }
+
+#if ORTHANC_ENABLE_WASM==1
+      // default implementations for a single canvas named "canvas" in the HTML and an emtpy WasmApplicationAdapter
+
+      virtual void InitializeWasm() ORTHANC_OVERRIDE
+      {
+        AttachWidgetToWasmViewport("canvas", mainWidget_);
+      }
+
+      virtual WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(MessageBroker& broker)
+      {
+        return new WasmPlatformApplicationAdapter(broker, *this);
+      }
+#endif
+
+    };
+
+    // this application actually works in Qt and WASM
+    class SampleSingleCanvasWithButtonsApplicationBase : public SampleApplicationBase
+    {
+public:
+      virtual void OnPushButton1Clicked() {}
+      virtual void OnPushButton2Clicked() {}
+      virtual void OnTool1Clicked() {}
+      virtual void OnTool2Clicked() {}
+
+      virtual void GetButtonNames(std::string& pushButton1,
+                                  std::string& pushButton2,
+                                  std::string& tool1,
+                                  std::string& tool2
+                                  ) {
+        pushButton1 = "action1";
+        pushButton2 = "action2";
+        tool1 = "tool1";
+        tool2 = "tool2";
+      }
+
+#if ORTHANC_ENABLE_QT==1
+      virtual QStoneMainWindow* CreateQtMainWindow() {
+        return new SampleMainWindowWithButtons(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this);
+      }
+#endif
+
+    };
+
+    // this application actually works in SDL and WASM
+    class SampleSingleCanvasApplicationBase : public SampleApplicationBase
+    {
+public:
+
+#if ORTHANC_ENABLE_QT==1
+      virtual QStoneMainWindow* CreateQtMainWindow() {
+        return new SampleMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this);
+      }
+#endif
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SampleInteractor.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,131 @@
+/**
+ * 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 "SampleApplicationBase.h"
+
+#include "../../../Framework/Widgets/LayeredSceneWidget.h"
+#include "../../../Framework/Widgets/IWorldSceneInteractor.h"
+#include "../../../Framework/Toolbox/ParallelSlicesCursor.h"
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    /**
+     * This is a basic mouse interactor for sample applications. It
+     * contains a set of parallel slices in the 3D space. The mouse
+     * wheel events make the widget change the slice that is
+     * displayed.
+     **/
+    class SampleInteractor : public IWorldSceneInteractor
+    {
+    private:
+      ParallelSlicesCursor   cursor_;
+
+    public:
+      SampleInteractor(VolumeImage& volume,
+                       VolumeProjection projection, 
+                       bool reverse)
+      {
+        std::unique_ptr<ParallelSlices> slices(volume.GetGeometry(projection, reverse));
+        cursor_.SetGeometry(*slices);
+      }
+
+      SampleInteractor(ISeriesLoader& series, 
+                       bool reverse)
+      {
+        if (reverse)
+        {
+          std::unique_ptr<ParallelSlices> slices(series.GetGeometry().Reverse());
+          cursor_.SetGeometry(*slices);
+        }
+        else
+        {
+          cursor_.SetGeometry(series.GetGeometry());
+        }
+      }
+
+      SampleInteractor(const ParallelSlices& slices)
+      {
+        cursor_.SetGeometry(slices);
+      }
+
+      ParallelSlicesCursor& GetCursor()
+      {
+        return cursor_;
+      }
+
+      void AddWidget(LayeredSceneWidget& widget)
+      {
+        widget.SetInteractor(*this);
+        widget.SetSlice(cursor_.GetCurrentSlice());
+      }
+
+      virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
+                                                          const ViewportGeometry& view,
+                                                          MouseButton button,
+                                                          double x,
+                                                          double y,
+                                                          IStatusBar* statusBar)
+      {
+        return NULL;
+      }
+
+      virtual void MouseOver(CairoContext& context,
+                             WorldSceneWidget& widget,
+                             const ViewportGeometry& view,
+                             double x,
+                             double y,
+                             IStatusBar* statusBar)
+      {
+      }
+
+      virtual void MouseWheel(WorldSceneWidget& widget,
+                              MouseWheelDirection direction,
+                              KeyboardModifiers modifiers,
+                              IStatusBar* statusBar)
+      {
+        if (cursor_.ApplyWheelEvent(direction, modifiers))
+        {
+          dynamic_cast<LayeredSceneWidget&>(widget).SetSlice(cursor_.GetCurrentSlice());
+        }
+      }
+
+      virtual void KeyPressed(WorldSceneWidget& widget,
+                              char key,
+                              KeyboardModifiers modifiers,
+                              IStatusBar* statusBar)
+      {
+      }
+
+      void LookupSliceContainingPoint(LayeredSceneWidget& widget,
+                                      const Vector& p)
+      {
+        if (cursor_.LookupSliceContainingPoint(p))
+        {
+          widget.SetSlice(cursor_.GetCurrentSlice());
+        }
+      }
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SampleList.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,41 @@
+// The macro "ORTHANC_STONE_SAMPLE" must be set by the CMake script
+
+#if ORTHANC_STONE_SAMPLE == 1
+#include "EmptyApplication.h"
+typedef OrthancStone::Samples::EmptyApplication SampleApplication;
+
+#elif ORTHANC_STONE_SAMPLE == 2
+#include "TestPatternApplication.h"
+typedef OrthancStone::Samples::TestPatternApplication SampleApplication;
+
+#elif ORTHANC_STONE_SAMPLE == 3
+#include "SingleFrameApplication.h"
+typedef OrthancStone::Samples::SingleFrameApplication SampleApplication;
+
+#elif ORTHANC_STONE_SAMPLE == 4
+#include "SingleVolumeApplication.h"
+typedef OrthancStone::Samples::SingleVolumeApplication SampleApplication;
+
+#elif ORTHANC_STONE_SAMPLE == 5
+#include "BasicPetCtFusionApplication.h"
+typedef OrthancStone::Samples::BasicPetCtFusionApplication SampleApplication;
+
+#elif ORTHANC_STONE_SAMPLE == 6
+#include "SynchronizedSeriesApplication.h"
+typedef OrthancStone::Samples::SynchronizedSeriesApplication SampleApplication;
+
+#elif ORTHANC_STONE_SAMPLE == 7
+#include "LayoutPetCtFusionApplication.h"
+typedef OrthancStone::Samples::LayoutPetCtFusionApplication SampleApplication;
+
+#elif ORTHANC_STONE_SAMPLE == 8
+#include "SimpleViewerApplicationSingleFile.h"
+typedef OrthancStone::Samples::SimpleViewerApplication SampleApplication;
+
+#elif ORTHANC_STONE_SAMPLE == 9
+#include "SingleFrameEditorApplication.h"
+typedef OrthancStone::Samples::SingleFrameEditorApplication SampleApplication;
+
+#else
+#error Please set the ORTHANC_STONE_SAMPLE macro
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SampleMainNative.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,44 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "SampleList.h"
+#if ORTHANC_ENABLE_SDL==1
+#include "../../Sdl/SdlStoneApplicationRunner.h"
+#endif
+#if ORTHANC_ENABLE_QT==1
+#include "Qt/SampleQtApplicationRunner.h"
+#endif
+
+int main(int argc, char* argv[]) 
+{
+  boost::shared_ptr<SampleApplication> sampleStoneApplication(new SampleApplication);
+
+#if ORTHANC_ENABLE_SDL==1
+  OrthancStone::SdlStoneApplicationRunner sdlApplicationRunner(sampleStoneApplication);
+  return sdlApplicationRunner.Execute(argc, argv);
+#endif
+  
+#if ORTHANC_ENABLE_QT==1
+  OrthancStone::Samples::SampleQtApplicationRunner qtAppRunner(sampleStoneApplication);
+  return qtAppRunner.Execute(argc, argv);
+#endif
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SampleMainWasm.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,37 @@
+/**
+ * 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/>.
+ **/
+
+#include "Platforms/Wasm/WasmWebService.h"
+#include "Platforms/Wasm/WasmViewport.h"
+
+#include <emscripten/emscripten.h>
+
+#include "SampleList.h"
+
+
+OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker) 
+{
+  return new SampleApplication(broker);
+}
+
+OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application)
+{
+  return dynamic_cast<SampleApplication*>(application)->CreateWasmApplicationAdapter(broker);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Samples-status.md	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,103 @@
+Executable versions
+================
+Generic options
+----------------------
+```
+("help", "Display this help and exit")
+("verbose", "Be verbose in logs")
+("orthanc", boost::program_options::value<std::string>()
+  ->default_value("http://localhost:8042/"),
+  "URL to the Orthanc server")
+("username", "Username for the Orthanc server")
+("password", "Password for the Orthanc server")
+("https-verify", boost::program_options::value<bool>()
+  ->default_value(true), "Check HTTPS certificates")
+```
+OrthancStoneSimpleViewer
+-------------------------------------
+- Options:
+    ```
+    - "studyId", std::string, "Orthanc ID of the study"
+    ```
+- study loading works OK
+- Invert does not work:
+```
+void SimpleViewerApplication::ExecuteAction(SimpleViewerApplication::Actions action)
+  {
+    // TODO
+  }
+```
+
+OrthancStoneSimpleViewerSingleFile
+-------------------------------------
+- Options:
+    ```
+    - "studyId", std::string, "Orthanc ID of the study"
+    ```
+
+Study loading works.
+
+The `line` and `circle` buttons work and call this:
+```
+virtual void OnTool1Clicked()
+{
+  currentTool_ = Tools_LineMeasure;
+}
+
+virtual void OnTool2Clicked()
+{
+  currentTool_ = Tools_CircleMeasure;
+}
+```
+The `action1` and `action2` buttons are not connected
+
+The following is displayed in the console at launch time:
+```
+W0313 12:20:12.790449 NativeStoneApplicationRunner.cpp:55] Use the key "s" to reinitialize the layout
+W0313 12:20:12.790449 NativeStoneApplicationRunner.cpp:55] Use the key "n" to go to next image in the main viewport
+```
+However, when looking at `MainWidgetInteractor::KeyPressed` (`SimpleViewerApplicationSingleFile.h:169`), only the following is processed:
+- 's': reset layout
+- 'l': select line tool
+- 'c': select circle tool
+
+OrthancStoneSingleFrame
+-------------------------------------
+```
+generic.add_options()
+("instance", boost::program_options::value<std::string>(), 
+"Orthanc ID of the instance")
+("frame", boost::program_options::value<unsigned int>()
+  ->default_value(0),
+"Number of the frame, for multi-frame DICOM instances")
+("smooth", boost::program_options::value<bool>()
+  ->default_value(true), 
+"Enable bilinear interpolation to smooth the image");
+```
+only key handled in `KeyPressed` is `s` to call `widget.FitContent()`
+
+
+OrthancStoneSingleFrameEditor
+-------------------------------------
+```
+generic.add_options()
+("instance", boost::program_options::value<std::string>(),
+"Orthanc ID of the instance")
+("frame", boost::program_options::value<unsigned int>()
+  ->default_value(0),
+"Number of the frame, for multi-frame DICOM instances");
+```
+Available commands in `KeyPressed` (`SingleFrameEditorApplication.h:280`): 
+- 'a' widget.FitContent()
+- 'c' Crop tool
+- 'm' Mask tool
+- 'd' dump to json and diplay result (?)
+- 'e' export current view to Dicom with dummy tags (?)
+- 'i' wdiget.SwitchInvert
+- 't' Move tool
+- 'n' switch between nearest and bilinear interpolation
+- 'r' Rotate tool
+- 's' Resize tool
+- 'w' Windowing tool
+- 'ctrl+y' redo
+- 'ctrl+z' undo
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/AppStatus.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <string>
+
+
+namespace SimpleViewer
+{
+  struct AppStatus
+  {
+    std::string patientId;
+    std::string studyDescription;
+    std::string currentInstanceIdInMainViewport;
+    // note: if you add members here, update the serialization code below and deserialization in simple-viewer.ts -> onAppStatusUpdated()
+
+
+    AppStatus()
+    {
+    }
+
+    void ToJson(Json::Value &output) const
+    {
+      output["patientId"] = patientId;
+      output["studyDescription"] = studyDescription;
+      output["currentInstanceIdInMainViewport"] = currentInstanceIdInMainViewport;
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,111 @@
+/**
+ * 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/>.
+ **/
+
+#include "MainWidgetInteractor.h"
+
+#include "SimpleViewerApplication.h"
+
+namespace SimpleViewer {
+
+  Deprecated::IWorldSceneMouseTracker* MainWidgetInteractor::CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
+                                                                    const Deprecated::ViewportGeometry& view,
+                                                                    MouseButton button,
+                                                                    KeyboardModifiers modifiers,
+                                                                    int viewportX,
+                                                                    int viewportY,
+                                                                    double x,
+                                                                    double y,
+                                                                    Deprecated::IStatusBar* statusBar,
+                                                                    const std::vector<Deprecated::Touch>& displayTouches)
+  {
+    if (button == MouseButton_Left)
+    {
+      if (application_.GetCurrentTool() == Tool_LineMeasure)
+      {
+        return new Deprecated::LineMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(),
+                                      x, y, 255, 0, 0, application_.GetFont());
+      }
+      else if (application_.GetCurrentTool() == Tool_CircleMeasure)
+      {
+        return new Deprecated::CircleMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(),
+                                        x, y, 255, 0, 0, application_.GetFont());
+      }
+      else if (application_.GetCurrentTool() == Tool_Crop)
+      {
+        // TODO
+      }
+      else if (application_.GetCurrentTool() == Tool_Windowing)
+      {
+        // TODO
+      }
+      else if (application_.GetCurrentTool() == Tool_Zoom)
+      {
+        // TODO
+      }
+      else if (application_.GetCurrentTool() == Tool_Pan)
+      {
+        // TODO
+      }
+    }
+    return NULL;
+  }
+
+  void MainWidgetInteractor::MouseOver(CairoContext& context,
+                                       Deprecated::WorldSceneWidget& widget,
+                                       const Deprecated::ViewportGeometry& view,
+                                       double x,
+                                       double y,
+                                       Deprecated::IStatusBar* statusBar)
+  {
+    if (statusBar != NULL)
+    {
+      Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
+
+      char buf[64];
+      sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)",
+              p[0] / 10.0, p[1] / 10.0, p[2] / 10.0);
+      statusBar->SetMessage(buf);
+    }
+  }
+
+  void MainWidgetInteractor::MouseWheel(Deprecated::WorldSceneWidget& widget,
+                                        MouseWheelDirection direction,
+                                        KeyboardModifiers modifiers,
+                                        Deprecated::IStatusBar* statusBar)
+  {
+  }
+
+  void MainWidgetInteractor::KeyPressed(Deprecated::WorldSceneWidget& widget,
+                                        KeyboardKeys key,
+                                        char keyChar,
+                                        KeyboardModifiers modifiers,
+                                        Deprecated::IStatusBar* statusBar)
+  {
+    switch (keyChar)
+    {
+    case 's':
+      widget.FitContent();
+      break;
+
+    default:
+      break;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,76 @@
+/**
+ * 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 "../../../../Framework/Deprecated/Widgets/IWorldSceneInteractor.h"
+
+using namespace OrthancStone;
+
+namespace SimpleViewer {
+
+  class SimpleViewerApplication;
+
+  class MainWidgetInteractor : public Deprecated::IWorldSceneInteractor
+  {
+  private:
+    SimpleViewerApplication&  application_;
+
+  public:
+    MainWidgetInteractor(SimpleViewerApplication&  application) :
+      application_(application)
+    {
+    }
+
+    /**
+        WorldSceneWidget: 
+    */
+    virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
+                                                                    const Deprecated::ViewportGeometry& view,
+                                                                    MouseButton button,
+                                                                    KeyboardModifiers modifiers,
+                                                                    int viewportX,
+                                                                    int viewportY,
+                                                                    double x,
+                                                                    double y,
+                                                                    Deprecated::IStatusBar* statusBar,
+                                                                    const std::vector<Deprecated::Touch>& displayTouches);
+
+    virtual void MouseOver(CairoContext& context,
+                           Deprecated::WorldSceneWidget& widget,
+                           const Deprecated::ViewportGeometry& view,
+                           double x,
+                           double y,
+                           Deprecated::IStatusBar* statusBar);
+
+    virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
+                            MouseWheelDirection direction,
+                            KeyboardModifiers modifiers,
+                            Deprecated::IStatusBar* statusBar);
+
+    virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
+                            KeyboardKeys key,
+                            char keyChar,
+                            KeyboardModifiers modifiers,
+                            Deprecated::IStatusBar* statusBar);
+  };
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,109 @@
+/**
+ * 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/>.
+ **/
+
+#include "SimpleViewerMainWindow.h"
+
+/**
+ * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as
+ * this makes CMake unable to detect when the UI file changes.
+ **/
+#include <ui_SimpleViewerMainWindow.h>
+#include "../../SimpleViewerApplication.h"
+
+
+namespace SimpleViewer
+{
+  template<typename T, typename U>
+  bool ExecuteCommand(U* handler, const T& command)
+  {
+    std::string serializedCommand = StoneSerialize(command);
+    StoneDispatchToHandler(serializedCommand, handler);
+  }
+
+  SimpleViewerMainWindow::SimpleViewerMainWindow(
+    OrthancStone::NativeStoneApplicationContext& context,
+    SimpleViewerApplication& stoneApplication,
+    QWidget *parent) :
+    QStoneMainWindow(context, parent),
+    ui_(new Ui::SimpleViewerMainWindow),
+    stoneApplication_(stoneApplication)
+  {
+    ui_->setupUi(this);
+    SetCentralStoneWidget(*ui_->cairoCentralWidget);
+
+#if QT_VERSION >= 0x050000
+    connect(ui_->toolButtonCrop, &QToolButton::clicked, this, &SimpleViewerMainWindow::cropClicked);
+    connect(ui_->pushButtonUndoCrop, &QToolButton::clicked, this, &SimpleViewerMainWindow::undoCropClicked);
+    connect(ui_->toolButtonLine, &QToolButton::clicked, this, &SimpleViewerMainWindow::lineClicked);
+    connect(ui_->toolButtonCircle, &QToolButton::clicked, this, &SimpleViewerMainWindow::circleClicked);
+    connect(ui_->toolButtonWindowing, &QToolButton::clicked, this, &SimpleViewerMainWindow::windowingClicked);
+    connect(ui_->pushButtonRotate, &QPushButton::clicked, this, &SimpleViewerMainWindow::rotateClicked);
+    connect(ui_->pushButtonInvert, &QPushButton::clicked, this, &SimpleViewerMainWindow::invertClicked);
+#else
+    connect(ui_->toolButtonCrop, SIGNAL(clicked()), this, SLOT(cropClicked()));
+    connect(ui_->toolButtonLine, SIGNAL(clicked()), this, SLOT(lineClicked()));
+    connect(ui_->toolButtonCircle, SIGNAL(clicked()), this, SLOT(circleClicked()));
+    connect(ui_->toolButtonWindowing, SIGNAL(clicked()), this, SLOT(windowingClicked()));
+    connect(ui_->pushButtonUndoCrop, SIGNAL(clicked()), this, SLOT(undoCropClicked()));
+    connect(ui_->pushButtonRotate, SIGNAL(clicked()), this, SLOT(rotateClicked()));
+    connect(ui_->pushButtonInvert, SIGNAL(clicked()), this, SLOT(invertClicked()));
+#endif
+  }
+
+  SimpleViewerMainWindow::~SimpleViewerMainWindow()
+  {
+    delete ui_;
+  }
+
+  void SimpleViewerMainWindow::cropClicked()
+  {
+    stoneApplication_.ExecuteCommand(SelectTool(Tool_Crop));
+  }
+
+  void SimpleViewerMainWindow::undoCropClicked()
+  {
+    stoneApplication_.ExecuteCommand(Action(ActionType_UndoCrop));
+  }
+
+  void SimpleViewerMainWindow::lineClicked()
+  {
+    stoneApplication_.ExecuteCommand(SelectTool(Tool_LineMeasure));
+  }
+
+  void SimpleViewerMainWindow::circleClicked()
+  {
+    stoneApplication_.ExecuteCommand(SelectTool(Tool_CircleMeasure));
+  }
+
+  void SimpleViewerMainWindow::windowingClicked()
+  {
+    stoneApplication_.ExecuteCommand(SelectTool(Tool_Windowing));
+  }
+
+  void SimpleViewerMainWindow::rotateClicked()
+  {
+    stoneApplication_.ExecuteCommand(Action(ActionType_Rotate));
+  }
+
+  void SimpleViewerMainWindow::invertClicked()
+  {
+    stoneApplication_.ExecuteCommand(Action(ActionType_Invert));
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,57 @@
+/**
+ * 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 <Applications/Qt/QCairoWidget.h>
+#include <Applications/Qt/QStoneMainWindow.h>
+
+namespace Ui 
+{
+  class SimpleViewerMainWindow;
+}
+
+using namespace OrthancStone;
+
+namespace SimpleViewer
+{
+  class SimpleViewerApplication;
+
+  class SimpleViewerMainWindow : public QStoneMainWindow
+  {
+    Q_OBJECT
+
+  private:
+    Ui::SimpleViewerMainWindow*   ui_;
+    SimpleViewerApplication&      stoneApplication_;
+
+  public:
+    explicit SimpleViewerMainWindow(OrthancStone::NativeStoneApplicationContext& context, SimpleViewerApplication& stoneApplication, QWidget *parent = 0);
+    ~SimpleViewerMainWindow();
+
+  private slots:
+    void cropClicked();
+    void undoCropClicked();
+    void rotateClicked();
+    void windowingClicked();
+    void lineClicked();
+    void circleClicked();
+    void invertClicked();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SimpleViewerMainWindow</class>
+ <widget class="QMainWindow" name="SimpleViewerMainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>903</width>
+    <height>634</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>500</width>
+    <height>300</height>
+   </size>
+  </property>
+  <property name="baseSize">
+   <size>
+    <width>500</width>
+    <height>300</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Stone of Orthanc</string>
+  </property>
+  <property name="layoutDirection">
+   <enum>Qt::LeftToRight</enum>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <property name="sizePolicy">
+    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+     <horstretch>0</horstretch>
+     <verstretch>0</verstretch>
+    </sizepolicy>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::LeftToRight</enum>
+   </property>
+   <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0">
+    <property name="sizeConstraint">
+     <enum>QLayout::SetDefaultConstraint</enum>
+    </property>
+    <item>
+     <widget class="QCairoWidget" name="cairoCentralWidget">
+      <property name="minimumSize">
+       <size>
+        <width>0</width>
+        <height>500</height>
+       </size>
+      </property>
+     </widget>
+    </item>
+    <item>
+     <widget class="QGroupBox" name="horizontalGroupBox">
+      <property name="minimumSize">
+       <size>
+        <width>0</width>
+        <height>100</height>
+       </size>
+      </property>
+      <property name="maximumSize">
+       <size>
+        <width>16777215</width>
+        <height>100</height>
+       </size>
+      </property>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <item>
+        <widget class="QToolButton" name="toolButtonWindowing">
+         <property name="text">
+          <string>windowing</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="toolButtonCrop">
+         <property name="text">
+          <string>crop</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="pushButtonUndoCrop">
+         <property name="text">
+          <string>undo crop</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="toolButtonLine">
+         <property name="text">
+          <string>line</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="toolButtonCircle">
+         <property name="text">
+          <string>circle</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="pushButtonRotate">
+         <property name="text">
+          <string>rotate</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="pushButtonInvert">
+         <property name="text">
+          <string>invert</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>903</width>
+     <height>22</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuTest">
+    <property name="title">
+     <string>Test</string>
+    </property>
+   </widget>
+   <addaction name="menuTest"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QCairoWidget</class>
+   <extends>QGraphicsView</extends>
+   <header location="global">QCairoWidget.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Qt/mainQt.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,14 @@
+#include "Applications/Qt/QtStoneApplicationRunner.h"
+
+#include "../../SimpleViewerApplication.h"
+#include "Framework/Messages/MessageBroker.h"
+
+
+int main(int argc, char* argv[]) 
+{
+  OrthancStone::MessageBroker broker;
+  SimpleViewer::SimpleViewerApplication stoneApplication(broker);
+
+  OrthancStone::QtStoneApplicationRunner qtAppRunner(broker, stoneApplication);
+  return qtAppRunner.Execute(argc, argv);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,225 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "SimpleViewerApplication.h"
+
+#if ORTHANC_ENABLE_QT == 1
+#  include "Qt/SimpleViewerMainWindow.h"
+#endif
+
+#if ORTHANC_ENABLE_WASM == 1
+#  include <Platforms/Wasm/WasmViewport.h>
+#endif
+
+namespace SimpleViewer
+{
+
+  void SimpleViewerApplication::Initialize(StoneApplicationContext* context,
+                                           Deprecated::IStatusBar& statusBar,
+                                           const boost::program_options::variables_map& parameters)
+  {
+    context_ = context;
+    statusBar_ = &statusBar;
+
+    {// initialize viewports and layout
+      mainLayout_ = new Deprecated::LayoutWidget("main-layout");
+      mainLayout_->SetPadding(10);
+      mainLayout_->SetBackgroundCleared(true);
+      mainLayout_->SetBackgroundColor(0, 0, 0);
+      mainLayout_->SetHorizontal();
+
+      thumbnailsLayout_ = new Deprecated::LayoutWidget("thumbnail-layout");
+      thumbnailsLayout_->SetPadding(10);
+      thumbnailsLayout_->SetBackgroundCleared(true);
+      thumbnailsLayout_->SetBackgroundColor(50, 50, 50);
+      thumbnailsLayout_->SetVertical();
+
+      mainWidget_ = new Deprecated::SliceViewerWidget(IObserver::GetBroker(), "main-viewport");
+      //mainWidget_->RegisterObserver(*this);
+
+      // hierarchy
+      mainLayout_->AddWidget(thumbnailsLayout_);
+      mainLayout_->AddWidget(mainWidget_);
+
+      // sources
+      smartLoader_.reset(new Deprecated::SmartLoader(IObserver::GetBroker(), context->GetOrthancApiClient()));
+      smartLoader_->SetImageQuality(Deprecated::SliceImageQuality_FullPam);
+
+      mainLayout_->SetTransmitMouseOver(true);
+      mainWidgetInteractor_.reset(new MainWidgetInteractor(*this));
+      mainWidget_->SetInteractor(*mainWidgetInteractor_);
+      thumbnailInteractor_.reset(new ThumbnailInteractor(*this));
+    }
+
+    statusBar.SetMessage("Use the key \"s\" to reinitialize the layout");
+    statusBar.SetMessage("Use the key \"n\" to go to next image in the main viewport");
+
+
+    if (parameters.count("studyId") < 1)
+    {
+      LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc";
+      context->GetOrthancApiClient().GetJsonAsync("/studies", new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyListReceived));
+    }
+    else
+    {
+      SelectStudy(parameters["studyId"].as<std::string>());
+    }
+  }
+
+
+  void SimpleViewerApplication::DeclareStartupOptions(boost::program_options::options_description& options)
+  {
+    boost::program_options::options_description generic("Sample options");
+    generic.add_options()
+        ("studyId", boost::program_options::value<std::string>(),
+         "Orthanc ID of the study")
+        ;
+
+    options.add(generic);
+  }
+
+  void SimpleViewerApplication::OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
+  {
+    const Json::Value& response = message.GetJson();
+
+    if (response.isArray() &&
+        response.size() >= 1)
+    {
+      SelectStudy(response[0].asString());
+    }
+  }
+  void SimpleViewerApplication::OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
+  {
+    const Json::Value& response = message.GetJson();
+
+    if (response.isObject() && response["Series"].isArray())
+    {
+      for (size_t i=0; i < response["Series"].size(); i++)
+      {
+        context_->GetOrthancApiClient().GetJsonAsync("/series/" + response["Series"][(int)i].asString(), new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnSeriesReceived));
+      }
+    }
+  }
+
+  void SimpleViewerApplication::OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
+  {
+    const Json::Value& response = message.GetJson();
+
+    if (response.isObject() &&
+        response["Instances"].isArray() &&
+        response["Instances"].size() > 0)
+    {
+      // keep track of all instances IDs
+      const std::string& seriesId = response["ID"].asString();
+      seriesTags_[seriesId] = response;
+      instancesIdsPerSeriesId_[seriesId] = std::vector<std::string>();
+      for (size_t i = 0; i < response["Instances"].size(); i++)
+      {
+        const std::string& instanceId = response["Instances"][static_cast<int>(i)].asString();
+        instancesIdsPerSeriesId_[seriesId].push_back(instanceId);
+      }
+
+      // load the first instance in the thumbnail
+      LoadThumbnailForSeries(seriesId, instancesIdsPerSeriesId_[seriesId][0]);
+
+      // if this is the first thumbnail loaded, load the first instance in the mainWidget
+      if (mainWidget_->GetLayerCount() == 0)
+      {
+        smartLoader_->SetFrameInWidget(*mainWidget_, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
+      }
+    }
+  }
+
+  void SimpleViewerApplication::LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId)
+  {
+    LOG(INFO) << "Loading thumbnail for series " << seriesId;
+    
+    Deprecated::SliceViewerWidget* thumbnailWidget = 
+      new Deprecated::SliceViewerWidget(IObserver::GetBroker(), "thumbnail-series-" + seriesId);
+    thumbnails_.push_back(thumbnailWidget);
+    thumbnailsLayout_->AddWidget(thumbnailWidget);
+    
+    thumbnailWidget->RegisterObserverCallback(
+      new Callable<SimpleViewerApplication, Deprecated::SliceViewerWidget::GeometryChangedMessage>
+      (*this, &SimpleViewerApplication::OnWidgetGeometryChanged));
+    
+    smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0);
+    thumbnailWidget->SetInteractor(*thumbnailInteractor_);
+  }
+
+  void SimpleViewerApplication::SelectStudy(const std::string& studyId)
+  {
+    context_->GetOrthancApiClient().GetJsonAsync("/studies/" + studyId, new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyReceived));
+  }
+
+  void SimpleViewerApplication::OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message)
+  {
+    // TODO: The "const_cast" could probably be replaced by "mainWidget_"
+    const_cast<Deprecated::SliceViewerWidget&>(message.GetOrigin()).FitContent();
+  }
+
+  void SimpleViewerApplication::SelectSeriesInMainViewport(const std::string& seriesId)
+  {
+    smartLoader_->SetFrameInWidget(*mainWidget_, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
+  }
+
+  bool SimpleViewerApplication::Handle(const StoneSampleCommands::SelectTool& value)
+  {
+    currentTool_ = value.tool;
+    return true;
+  }
+
+  bool SimpleViewerApplication::Handle(const StoneSampleCommands::Action& value)
+  {
+    switch (value.type)
+    {
+    case ActionType_Invert:
+      // TODO
+      break;
+    case ActionType_UndoCrop:
+      // TODO
+      break;
+    case ActionType_Rotate:
+      // TODO
+      break;
+    default:
+      throw std::runtime_error("Action type not supported");
+    }
+    return true;
+  }
+
+#if ORTHANC_ENABLE_QT==1
+  QStoneMainWindow* SimpleViewerApplication::CreateQtMainWindow()
+  {
+    return new SimpleViewerMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this);
+  }
+#endif
+
+#if ORTHANC_ENABLE_WASM==1
+  void SimpleViewerApplication::InitializeWasm() {
+
+    AttachWidgetToWasmViewport("canvasThumbnails", thumbnailsLayout_);
+    AttachWidgetToWasmViewport("canvasMain", mainWidget_);
+  }
+#endif
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,175 @@
+/**
+ * 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
+
+ /*
+ This header contains the command definitions for the sample applications
+ */
+#include "Applications/Samples/StoneSampleCommands_generated.hpp"
+using namespace StoneSampleCommands;
+
+#include "Applications/IStoneApplication.h"
+
+#include "../../../../Framework/Deprecated/Layers/CircleMeasureTracker.h"
+#include "../../../../Framework/Deprecated/Layers/LineMeasureTracker.h"
+#include "../../../../Framework/Deprecated/SmartLoader.h"
+#include "../../../../Framework/Deprecated/Widgets/LayoutWidget.h"
+#include "../../../../Framework/Deprecated/Widgets/SliceViewerWidget.h"
+#include "../../../../Framework/Messages/IObserver.h"
+
+#if ORTHANC_ENABLE_WASM==1
+#include "Platforms/Wasm/WasmPlatformApplicationAdapter.h"
+#include "Platforms/Wasm/Defaults.h"
+#endif
+
+#if ORTHANC_ENABLE_QT==1
+#include "Qt/SimpleViewerMainWindow.h"
+#endif
+
+#include <Core/Images/Font.h>
+#include <Core/Logging.h>
+
+#include "ThumbnailInteractor.h"
+#include "MainWidgetInteractor.h"
+#include "AppStatus.h"
+
+using namespace OrthancStone;
+
+
+namespace SimpleViewer
+{
+
+  class SimpleViewerApplication
+    : public IStoneApplication
+    , public IObserver
+    , public IObservable
+    , public StoneSampleCommands::IHandler
+  {
+  public:
+
+    struct StatusUpdatedMessage : public IMessage
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+
+      const AppStatus& status_;
+
+      StatusUpdatedMessage(const AppStatus& status)
+        : status_(status)
+      {
+      }
+    };
+
+  private:
+    Tool                                currentTool_;
+
+    std::unique_ptr<MainWidgetInteractor> mainWidgetInteractor_;
+    std::unique_ptr<ThumbnailInteractor>  thumbnailInteractor_;
+    Deprecated::LayoutWidget*                       mainLayout_;
+    Deprecated::LayoutWidget*                       thumbnailsLayout_;
+    Deprecated::SliceViewerWidget*                  mainWidget_;
+    std::vector<Deprecated::SliceViewerWidget*>     thumbnails_;
+    std::map<std::string, std::vector<std::string> > instancesIdsPerSeriesId_;
+    std::map<std::string, Json::Value>  seriesTags_;
+    unsigned int                        currentInstanceIndex_;
+    Deprecated::WidgetViewport*       wasmViewport1_;
+    Deprecated::WidgetViewport*       wasmViewport2_;
+
+    Deprecated::IStatusBar*                         statusBar_;
+    std::unique_ptr<Deprecated::SmartLoader>          smartLoader_;
+
+    Orthanc::Font                       font_;
+
+  public:
+    SimpleViewerApplication(MessageBroker& broker) :
+      IObserver(broker),
+      IObservable(broker),
+      currentTool_(StoneSampleCommands::Tool_LineMeasure),
+      mainLayout_(NULL),
+      currentInstanceIndex_(0),
+      wasmViewport1_(NULL),
+      wasmViewport2_(NULL)
+    {
+      font_.LoadFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
+    }
+
+    virtual void Finalize() ORTHANC_OVERRIDE {}
+    virtual Deprecated::IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainLayout_;}
+
+    virtual void DeclareStartupOptions(boost::program_options::options_description& options) ORTHANC_OVERRIDE;
+    virtual void Initialize(StoneApplicationContext* context,
+                            Deprecated::IStatusBar& statusBar,
+                            const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE;
+
+    void OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message);
+
+    void OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message);
+
+    void OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message);
+
+    void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId);
+
+    void SelectStudy(const std::string& studyId);
+
+    void OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message);
+
+    void SelectSeriesInMainViewport(const std::string& seriesId);
+
+
+    Tool GetCurrentTool() const
+    {
+      return currentTool_;
+    }
+
+    const Orthanc::Font& GetFont() const
+    {
+      return font_;
+    }
+
+    // ExecuteAction method was empty (its body was a single "TODO" comment)
+    virtual bool Handle(const SelectTool& value) ORTHANC_OVERRIDE;
+    virtual bool Handle(const Action& value) ORTHANC_OVERRIDE;
+
+    template<typename T>
+    bool ExecuteCommand(const T& cmd)
+    {
+      std::string cmdStr = StoneSampleCommands::StoneSerialize(cmd);
+      return StoneSampleCommands::StoneDispatchToHandler(cmdStr, this);
+    }
+
+    virtual void HandleSerializedMessage(const char* data) ORTHANC_OVERRIDE
+    {
+      StoneSampleCommands::StoneDispatchToHandler(data, this);
+    }
+
+    virtual std::string GetTitle() const ORTHANC_OVERRIDE {return "SimpleViewer";}
+
+#if ORTHANC_ENABLE_WASM==1
+    virtual void InitializeWasm() ORTHANC_OVERRIDE;
+#endif
+
+#if ORTHANC_ENABLE_QT==1
+    virtual QStoneMainWindow* CreateQtMainWindow();
+#endif
+  };
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,46 @@
+/**
+ * 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/>.
+ **/
+
+#include "ThumbnailInteractor.h"
+
+#include "SimpleViewerApplication.h"
+
+namespace SimpleViewer {
+
+  Deprecated::IWorldSceneMouseTracker* ThumbnailInteractor::CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
+                                                                   const Deprecated::ViewportGeometry& view,
+                                                                   MouseButton button,
+                                                                   KeyboardModifiers modifiers,
+                                                                   int viewportX,
+                                                                   int viewportY,
+                                                                   double x,
+                                                                   double y,
+                                                                   Deprecated::IStatusBar* statusBar,
+                                                                   const std::vector<Deprecated::Touch>& displayTouches)
+  {
+    if (button == MouseButton_Left)
+    {
+      statusBar->SetMessage("selected thumbnail " + widget.GetName());
+      std::string seriesId = widget.GetName().substr(strlen("thumbnail-series-"));
+      application_.SelectSeriesInMainViewport(seriesId);
+    }
+    return NULL;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,77 @@
+/**
+ * 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 "../../../../Framework/Deprecated/Widgets/IWorldSceneInteractor.h"
+
+using namespace OrthancStone;
+
+namespace SimpleViewer {
+
+  class SimpleViewerApplication;
+
+  class ThumbnailInteractor : public Deprecated::IWorldSceneInteractor
+  {
+  private:
+    SimpleViewerApplication&  application_;
+  public:
+    ThumbnailInteractor(SimpleViewerApplication&  application) :
+      application_(application)
+    {
+    }
+
+    virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
+                                                                    const Deprecated::ViewportGeometry& view,
+                                                                    MouseButton button,
+                                                                    KeyboardModifiers modifiers,
+                                                                    int viewportX,
+                                                                    int viewportY,
+                                                                    double x,
+                                                                    double y,
+                                                                    Deprecated::IStatusBar* statusBar,
+                                                                    const std::vector<Deprecated::Touch>& displayTouches);
+
+    virtual void MouseOver(CairoContext& context,
+                           Deprecated::WorldSceneWidget& widget,
+                           const Deprecated::ViewportGeometry& view,
+                           double x,
+                           double y,
+                           Deprecated::IStatusBar* statusBar)
+    {}
+
+    virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
+                            MouseWheelDirection direction,
+                            KeyboardModifiers modifiers,
+                            Deprecated::IStatusBar* statusBar)
+    {}
+
+    virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
+                            KeyboardKeys key,
+                            char keyChar,
+                            KeyboardModifiers modifiers,
+                            Deprecated::IStatusBar* statusBar)
+    {}
+
+  };
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,51 @@
+/**
+ * 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/>.
+ **/
+
+#include "SimpleViewerWasmApplicationAdapter.h"
+
+namespace SimpleViewer
+{
+
+  SimpleViewerWasmApplicationAdapter::SimpleViewerWasmApplicationAdapter(MessageBroker &broker, SimpleViewerApplication &application)
+      : WasmPlatformApplicationAdapter(broker, application),
+        viewerApplication_(application)
+  {
+    application.RegisterObserverCallback(new Callable<SimpleViewerWasmApplicationAdapter, SimpleViewerApplication::StatusUpdatedMessage>(*this, &SimpleViewerWasmApplicationAdapter::OnStatusUpdated));
+  }
+
+  void SimpleViewerWasmApplicationAdapter::OnStatusUpdated(const SimpleViewerApplication::StatusUpdatedMessage &message)
+  {
+    Json::Value statusJson;
+    message.status_.ToJson(statusJson);
+
+    Json::Value event;
+    event["event"] = "appStatusUpdated";
+    event["data"] = statusJson;
+
+    Json::StreamWriterBuilder builder;
+    std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
+    std::ostringstream outputStr;
+
+    writer->write(event, &outputStr);
+
+    NotifyStatusUpdateFromCppToWebWithString(outputStr.str());
+  }
+
+} // namespace SimpleViewer
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,43 @@
+/**
+ * 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 <string>
+#include <Framework/Messages/IObserver.h>
+#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h>
+
+#include "../../SimpleViewerApplication.h"
+
+namespace SimpleViewer {
+
+  class SimpleViewerWasmApplicationAdapter : public WasmPlatformApplicationAdapter
+    {
+      SimpleViewerApplication&  viewerApplication_;
+
+    public:
+      SimpleViewerWasmApplicationAdapter(MessageBroker& broker, SimpleViewerApplication& application);
+
+    private:
+      void OnStatusUpdated(const SimpleViewerApplication::StatusUpdatedMessage& message);
+
+    };
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Wasm/mainWasm.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,38 @@
+/**
+ * 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/>.
+ **/
+
+#include "Platforms/Wasm/WasmWebService.h"
+#include "Platforms/Wasm/WasmViewport.h"
+
+#include <emscripten/emscripten.h>
+
+#include "../../SimpleViewerApplication.h"
+#include "SimpleViewerWasmApplicationAdapter.h"
+
+
+OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker) {
+  
+  return new SimpleViewer::SimpleViewerApplication(broker);
+}
+
+OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, IStoneApplication* application)
+{
+  return new SimpleViewer::SimpleViewerWasmApplicationAdapter(broker, *(dynamic_cast<SimpleViewer::SimpleViewerApplication*>(application)));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.html	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,43 @@
+<!doctype html>
+
+<html lang="us">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <!-- Disable pinch zoom on mobile devices -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <meta name="HandheldFriendly" content="true" />
+
+    <title>Simple Viewer</title>
+    <link href="styles.css" rel="stylesheet" />
+
+<body>
+  <div id="breadcrumb">
+    <span id="label-patient-id"></span>
+    <span id="label-study-description"></span>
+    <span id="label-series-description"></span>
+  </div>
+  <div style="height: calc(100% - 50px)">
+    <div style="width: 20%; height: 100%; display: inline-block">
+      <canvas id="canvasThumbnails"></canvas>
+    </div>
+    <div style="width: 70%; height: 100%; display: inline-block">
+      <canvas id="canvasMain"></canvas>
+    </div>
+  </div>
+  <div id="toolbox" style="height: 50px">
+    <button tool-selector="line-measure" class="tool-selector">line</button>
+    <button tool-selector="circle-measure" class="tool-selector">circle</button>
+    <button tool-selector="crop" class="tool-selector">crop</button>
+    <button tool-selector="windowing" class="tool-selector">windowing</button>
+    <button tool-selector="zoom" class="tool-selector">zoom</button>
+    <button tool-selector="pan" class="tool-selector">pan</button>
+    <button action-trigger="rotate-left" class="action-trigger">rotate left</button>
+    <button action-trigger="rotate-right" class="action-trigger">rotate right</button>
+    <button action-trigger="invert" class="action-trigger">invert</button>
+  </div>
+  <script type="text/javascript" src="app-simple-viewer.js"></script>
+</body>
+
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,81 @@
+import wasmApplicationRunner = require('../../../../Platforms/Wasm/wasm-application-runner');
+
+wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSimpleViewer", "/orthanc");
+
+function SelectTool(toolName: string) {
+  var command = {
+    command: "selectTool:" + toolName,
+    commandType: "generic-no-arg-command",
+    args: {
+    }                                                                                                                       
+  };
+  wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command));
+}
+
+function PerformAction(actionName: string) {
+  var command = {
+    command: "action:" + actionName,
+    commandType: "generic-no-arg-command",
+    args: {
+    }
+  };
+  wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command));
+}
+
+class SimpleViewerUI {
+
+  private _labelPatientId: HTMLSpanElement;
+  private _labelStudyDescription: HTMLSpanElement;
+
+  public constructor() {
+    // install "SelectTool" handlers
+    document.querySelectorAll("[tool-selector]").forEach((e) => {
+      (e as HTMLButtonElement).addEventListener("click", () => {
+        SelectTool(e.attributes["tool-selector"].value);
+      });
+    });
+
+    // install "PerformAction" handlers
+    document.querySelectorAll("[action-trigger]").forEach((e) => {
+      (e as HTMLButtonElement).addEventListener("click", () => {
+        PerformAction(e.attributes["action-trigger"].value);
+      });
+    });
+
+    // connect all ui elements to members
+    this._labelPatientId = document.getElementById("label-patient-id") as HTMLSpanElement;
+    this._labelStudyDescription = document.getElementById("label-study-description") as HTMLSpanElement;
+  }
+
+  public onAppStatusUpdated(status: any) {
+    this._labelPatientId.innerText = status["patientId"];
+    this._labelStudyDescription.innerText = status["studyDescription"];
+    // this.highlighThumbnail(status["currentInstanceIdInMainViewport"]);
+  }
+
+}
+
+var ui = new SimpleViewerUI();
+
+// this method is called "from the C++ code" when the StoneApplication is updated.
+// it can be used to update the UI of the application
+function UpdateWebApplicationWithString(statusUpdateMessageString: string) {
+  console.log("updating web application with string: ", statusUpdateMessageString);
+  let statusUpdateMessage = JSON.parse(statusUpdateMessageString);
+
+  if ("event" in statusUpdateMessage) {
+    let eventName = statusUpdateMessage["event"];
+    if (eventName == "appStatusUpdated") {
+      ui.onAppStatusUpdated(statusUpdateMessage["data"]);
+    }
+  }
+}
+
+function UpdateWebApplicationWithSerializedMessage(statusUpdateMessageString: string) {
+  console.log("updating web application with serialized message: ", statusUpdateMessageString);
+  console.log("<not supported in the simple viewer!>");
+}
+
+// make it available to other js scripts in the application
+(<any> window).UpdateWebApplicationWithString = UpdateWebApplicationWithString;
+(<any> window).UpdateWebApplicationWithSerializedMessage = UpdateWebApplicationWithSerializedMessage;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Wasm/styles.css	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,54 @@
+html, body {
+    width: 100%;
+    height: 100%;
+    margin: 0px;
+    border: 0;
+    overflow: hidden; /*  Disable scrollbars */
+    display: block;  /* No floating content on sides */
+    background-color: black;
+    color: white;
+    font-family: Arial, Helvetica, sans-serif;
+}
+
+canvas {
+    left:0px;
+    top:0px;
+}
+
+#canvas-group {
+    padding:5px;
+    background-color: grey;
+}
+
+#status-group {
+    padding:5px;
+}
+
+#worklist-group {
+    padding:5px;
+}
+
+.vsol-button {
+    height: 40px;
+}
+
+#thumbnails-group ul li {
+    display: inline;
+    list-style: none;
+}
+
+.thumbnail {
+    width: 100px;
+    height: 100px;
+    padding: 3px;
+}
+
+.thumbnail-selected {
+    border-width: 1px;
+    border-color: red;
+    border-style: solid;
+}
+
+#template-thumbnail-li {
+    display: none !important;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewer/Wasm/tsconfig-simple-viewer.json	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,9 @@
+{
+    "extends" : "../../Web/tsconfig-samples",
+    "compilerOptions": {
+    },
+    "include" : [
+        "simple-viewer.ts",
+        "../../build-wasm/ApplicationCommands_generated.ts"
+    ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SimpleViewerApplicationSingleFile.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,461 @@
+/**
+ * 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 "SampleApplicationBase.h"
+
+#include "../../../Framework/Deprecated/Layers/CircleMeasureTracker.h"
+#include "../../../Framework/Deprecated/Layers/LineMeasureTracker.h"
+#include "../../../Framework/Deprecated/SmartLoader.h"
+#include "../../../Framework/Deprecated/Widgets/LayoutWidget.h"
+#include "../../../Framework/Deprecated/Widgets/SliceViewerWidget.h"
+#include "../../../Framework/Messages/IObserver.h"
+
+#if ORTHANC_ENABLE_WASM==1
+#include "../../../Platforms/Wasm/WasmPlatformApplicationAdapter.h"
+#include "../../../Platforms/Wasm/Defaults.h"
+#endif
+
+#include <Core/Images/Font.h>
+#include <Core/Logging.h>
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    class SimpleViewerApplication :
+      public SampleSingleCanvasWithButtonsApplicationBase,
+      public ObserverBase<SimpleViewerApplication>
+    {
+    private:
+      class ThumbnailInteractor : public Deprecated::IWorldSceneInteractor
+      {
+      private:
+        SimpleViewerApplication&  application_;
+
+      public:
+        ThumbnailInteractor(SimpleViewerApplication&  application) :
+          application_(application)
+        {
+        }
+
+        virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
+                                                            const Deprecated::ViewportGeometry& view,
+                                                            MouseButton button,
+                                                            KeyboardModifiers modifiers,
+                                                            int viewportX,
+                                                            int viewportY,
+                                                            double x,
+                                                            double y,
+                                                            Deprecated::IStatusBar* statusBar,
+                                                            const std::vector<Deprecated::Touch>& displayTouches)
+        {
+          if (button == MouseButton_Left)
+          {
+            statusBar->SetMessage("selected thumbnail " + widget.GetName());
+            std::string seriesId = widget.GetName().substr(strlen("thumbnail-series-"));
+            application_.SelectSeriesInMainViewport(seriesId);
+          }
+          return NULL;
+        }
+
+        virtual void MouseOver(CairoContext& context,
+                               Deprecated::WorldSceneWidget& widget,
+                               const Deprecated::ViewportGeometry& view,
+                               double x,
+                               double y,
+                               Deprecated::IStatusBar* statusBar)
+        {
+        }
+
+        virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
+                                MouseWheelDirection direction,
+                                KeyboardModifiers modifiers,
+                                Deprecated::IStatusBar* statusBar)
+        {
+        }
+
+        virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
+                                KeyboardKeys key,
+                                char keyChar,
+                                KeyboardModifiers modifiers,
+                                Deprecated::IStatusBar* statusBar)
+        {
+        }
+      };
+
+      class MainWidgetInteractor : public Deprecated::IWorldSceneInteractor
+      {
+      private:
+        SimpleViewerApplication&  application_;
+        
+      public:
+        MainWidgetInteractor(SimpleViewerApplication&  application) :
+          application_(application)
+        {
+        }
+        
+        virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
+                                                            const Deprecated::ViewportGeometry& view,
+                                                            MouseButton button,
+                                                            KeyboardModifiers modifiers,
+                                                            int viewportX,
+                                                            int viewportY,
+                                                            double x,
+                                                            double y,
+                                                            Deprecated::IStatusBar* statusBar,
+                                                            const std::vector<Deprecated::Touch>& displayTouches)
+        {
+          if (button == MouseButton_Left)
+          {
+            if (application_.currentTool_ == Tool_LineMeasure)
+            {
+              return new Deprecated::LineMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(),
+                                            x, y, 255, 0, 0, application_.GetFont());
+            }
+            else if (application_.currentTool_ == Tool_CircleMeasure)
+            {
+              return new Deprecated::CircleMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(),
+                                              x, y, 255, 0, 0, application_.GetFont());
+            }
+          }
+          return NULL;
+        }
+
+        virtual void MouseOver(CairoContext& context,
+                               Deprecated::WorldSceneWidget& widget,
+                               const Deprecated::ViewportGeometry& view,
+                               double x,
+                               double y,
+                               Deprecated::IStatusBar* statusBar)
+        {
+          if (statusBar != NULL)
+          {
+            Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
+            
+            char buf[64];
+            sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)",
+                    p[0] / 10.0, p[1] / 10.0, p[2] / 10.0);
+            statusBar->SetMessage(buf);
+          }
+        }
+
+        virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
+                                MouseWheelDirection direction,
+                                KeyboardModifiers modifiers,
+                                Deprecated::IStatusBar* statusBar)
+        {
+        }
+
+        virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
+                                KeyboardKeys key,
+                                char keyChar,
+                                KeyboardModifiers modifiers,
+                                Deprecated::IStatusBar* statusBar)
+        {
+          switch (keyChar)
+          {
+            case 's':
+              widget.FitContent();
+              break;
+
+            case 'l':
+              application_.currentTool_ = Tool_LineMeasure;
+              break;
+
+            case 'c':
+              application_.currentTool_ = Tool_CircleMeasure;
+              break;
+
+            default:
+              break;
+          }
+        }
+      };
+
+
+#if ORTHANC_ENABLE_WASM==1
+      class SimpleViewerApplicationAdapter : public WasmPlatformApplicationAdapter
+      {
+        SimpleViewerApplication&  viewerApplication_;
+
+      public:
+        SimpleViewerApplicationAdapter(SimpleViewerApplication& application)
+          : WasmPlatformApplicationAdapter(application),
+            viewerApplication_(application)
+        {
+        }
+
+        virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input) 
+        {
+          if (input == "select-tool:line-measure")
+          {
+            viewerApplication_.currentTool_ = Tool_LineMeasure;
+            NotifyStatusUpdateFromCppToWebWithString("currentTool=line-measure");
+          }
+          else if (input == "select-tool:circle-measure")
+          {
+            viewerApplication_.currentTool_ = Tool_CircleMeasure;
+            NotifyStatusUpdateFromCppToWebWithString("currentTool=circle-measure");
+          }
+
+          output = "ok";
+        }
+
+        virtual void NotifySerializedMessageFromCppToWeb(const std::string& statusUpdateMessage) 
+        {
+          UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str());
+        }
+
+        virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage) 
+        {
+          UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str());
+        }
+
+      };
+#endif
+      enum Tool {
+        Tool_LineMeasure,
+        Tool_CircleMeasure
+      };
+
+      Tool                                 currentTool_;
+      std::unique_ptr<MainWidgetInteractor>  mainWidgetInteractor_;
+      std::unique_ptr<ThumbnailInteractor>   thumbnailInteractor_;
+      Deprecated::LayoutWidget*                        mainLayout_;
+      Deprecated::LayoutWidget*                        thumbnailsLayout_;
+      std::vector<boost::shared_ptr<Deprecated::SliceViewerWidget> >      thumbnails_;
+
+      std::map<std::string, std::vector<std::string> > instancesIdsPerSeriesId_;
+      std::map<std::string, Json::Value> seriesTags_;
+
+      unsigned int                         currentInstanceIndex_;
+      Deprecated::WidgetViewport*        wasmViewport1_;
+      Deprecated::WidgetViewport*        wasmViewport2_;
+
+      Deprecated::IStatusBar*                          statusBar_;
+      std::unique_ptr<Deprecated::SmartLoader>           smartLoader_;
+
+      Orthanc::Font                        font_;
+
+    public:
+      SimpleViewerApplication() :
+        currentTool_(Tool_LineMeasure),
+        mainLayout_(NULL),
+        currentInstanceIndex_(0),
+        wasmViewport1_(NULL),
+        wasmViewport2_(NULL)
+      {
+        font_.LoadFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
+//        DeclareIgnoredMessage(MessageType_Widget_ContentChanged);
+      }
+
+      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
+      {
+        boost::program_options::options_description generic("Sample options");
+        generic.add_options()
+          ("studyId", boost::program_options::value<std::string>(),
+           "Orthanc ID of the study")
+          ;
+
+        options.add(generic);
+      }
+
+      virtual void Initialize(StoneApplicationContext* context,
+                              Deprecated::IStatusBar& statusBar,
+                              const boost::program_options::variables_map& parameters)
+      {
+        using namespace OrthancStone;
+
+        context_ = context;
+        statusBar_ = &statusBar;
+
+        {// initialize viewports and layout
+          mainLayout_ = new Deprecated::LayoutWidget("main-layout");
+          mainLayout_->SetPadding(10);
+          mainLayout_->SetBackgroundCleared(true);
+          mainLayout_->SetBackgroundColor(0, 0, 0);
+          mainLayout_->SetHorizontal();
+
+          boost::shared_ptr<Deprecated::LayoutWidget> thumbnailsLayout_(new Deprecated::LayoutWidget("thumbnail-layout"));
+          thumbnailsLayout_->SetPadding(10);
+          thumbnailsLayout_->SetBackgroundCleared(true);
+          thumbnailsLayout_->SetBackgroundColor(50, 50, 50);
+          thumbnailsLayout_->SetVertical();
+
+          boost::shared_ptr<Deprecated::SliceViewerWidget> widget
+            (new Deprecated::SliceViewerWidget("main-viewport"));
+          SetCentralWidget(widget);
+          //mainWidget_->RegisterObserver(*this);
+
+          // hierarchy
+          mainLayout_->AddWidget(thumbnailsLayout_);
+          mainLayout_->AddWidget(widget);
+
+          // sources
+          smartLoader_.reset(new Deprecated::SmartLoader(context->GetOrthancApiClient()));
+          smartLoader_->SetImageQuality(Deprecated::SliceImageQuality_FullPam);
+
+          mainLayout_->SetTransmitMouseOver(true);
+          mainWidgetInteractor_.reset(new MainWidgetInteractor(*this));
+          widget->SetInteractor(*mainWidgetInteractor_);
+          thumbnailInteractor_.reset(new ThumbnailInteractor(*this));
+        }
+
+        statusBar.SetMessage("Use the key \"s\" to reinitialize the layout");
+        statusBar.SetMessage("Use the key \"n\" to go to next image in the main viewport");
+
+
+        if (parameters.count("studyId") < 1)
+        {
+          LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc";
+          context->GetOrthancApiClient()->GetJsonAsync(
+            "/studies",
+            new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
+            (GetSharedObserver(), &SimpleViewerApplication::OnStudyListReceived));
+        }
+        else
+        {
+          SelectStudy(parameters["studyId"].as<std::string>());
+        }
+      }
+
+      void OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
+      {
+        const Json::Value& response = message.GetJson();
+
+        if (response.isArray() &&
+            response.size() >= 1)
+        {
+          SelectStudy(response[0].asString());
+        }
+      }
+      
+      void OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
+      {
+        const Json::Value& response = message.GetJson();
+
+        if (response.isObject() && response["Series"].isArray())
+        {
+          for (size_t i=0; i < response["Series"].size(); i++)
+          {
+            context_->GetOrthancApiClient()->GetJsonAsync(
+              "/series/" + response["Series"][(int)i].asString(),
+              new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
+              (GetSharedObserver(), &SimpleViewerApplication::OnSeriesReceived));
+          }
+        }
+      }
+
+      void OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
+      {
+        const Json::Value& response = message.GetJson();
+
+        if (response.isObject() &&
+            response["Instances"].isArray() &&
+            response["Instances"].size() > 0)
+        {
+          // keep track of all instances IDs
+          const std::string& seriesId = response["ID"].asString();
+          seriesTags_[seriesId] = response;
+          instancesIdsPerSeriesId_[seriesId] = std::vector<std::string>();
+          for (size_t i = 0; i < response["Instances"].size(); i++)
+          {
+            const std::string& instanceId = response["Instances"][static_cast<int>(i)].asString();
+            instancesIdsPerSeriesId_[seriesId].push_back(instanceId);
+          }
+
+          // load the first instance in the thumbnail
+          LoadThumbnailForSeries(seriesId, instancesIdsPerSeriesId_[seriesId][0]);
+
+          // if this is the first thumbnail loaded, load the first instance in the mainWidget
+          Deprecated::SliceViewerWidget& widget = dynamic_cast<Deprecated::SliceViewerWidget&>(*GetCentralWidget());
+          if (widget.GetLayerCount() == 0)
+          {
+            smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
+          }
+        }
+      }
+
+      void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId)
+      {
+        LOG(INFO) << "Loading thumbnail for series " << seriesId;
+        boost::shared_ptr<Deprecated::SliceViewerWidget> thumbnailWidget(new Deprecated::SliceViewerWidget("thumbnail-series-" + seriesId));
+        thumbnails_.push_back(thumbnailWidget);
+        thumbnailsLayout_->AddWidget(thumbnailWidget);
+        Register<Deprecated::SliceViewerWidget::GeometryChangedMessage>(*thumbnailWidget, &SimpleViewerApplication::OnWidgetGeometryChanged);
+        smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0);
+        thumbnailWidget->SetInteractor(*thumbnailInteractor_);
+      }
+
+      void SelectStudy(const std::string& studyId)
+      {
+        LOG(INFO) << "Selecting study: " << studyId;
+        context_->GetOrthancApiClient()->GetJsonAsync(
+          "/studies/" + studyId, new Deprecated::DeprecatedCallable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
+          (GetSharedObserver(), &SimpleViewerApplication::OnStudyReceived));
+      }
+
+      void OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message)
+      {
+        // TODO: The "const_cast" could probably be replaced by "mainWidget"
+        const_cast<Deprecated::SliceViewerWidget&>(message.GetOrigin()).FitContent();
+      }
+
+      void SelectSeriesInMainViewport(const std::string& seriesId)
+      {
+        Deprecated::SliceViewerWidget& widget = dynamic_cast<Deprecated::SliceViewerWidget&>(*GetCentralWidget());
+        smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
+      }
+
+      const Orthanc::Font& GetFont() const
+      {
+        return font_;
+      }
+      
+      virtual void OnPushButton1Clicked() {}
+      virtual void OnPushButton2Clicked() {}
+      virtual void OnTool1Clicked() { currentTool_ = Tool_LineMeasure;}
+      virtual void OnTool2Clicked() { currentTool_ = Tool_CircleMeasure;}
+
+      virtual void GetButtonNames(std::string& pushButton1,
+                                  std::string& pushButton2,
+                                  std::string& tool1,
+                                  std::string& tool2)
+      {
+        tool1 = "line";
+        tool2 = "circle";
+        pushButton1 = "action1";
+        pushButton2 = "action2";
+      }
+
+#if ORTHANC_ENABLE_WASM==1
+      virtual void InitializeWasm()
+      {
+        AttachWidgetToWasmViewport("canvas", thumbnailsLayout_);
+        AttachWidgetToWasmViewport("canvas2", widget);
+      }
+#endif
+
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SingleFrameApplication.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,268 @@
+/**
+ * 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 "SampleApplicationBase.h"
+
+#include "../../../Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.h"
+#include "../../../Framework/Deprecated/Widgets/SliceViewerWidget.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+#include <boost/math/constants/constants.hpp>
+
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    class SingleFrameApplication :
+      public SampleSingleCanvasApplicationBase,
+      public ObserverBase<SingleFrameApplication>
+    {
+    private:
+      class Interactor : public Deprecated::IWorldSceneInteractor
+      {
+      private:
+        SingleFrameApplication&  application_;
+        
+      public:
+        Interactor(SingleFrameApplication&  application) :
+          application_(application)
+        {
+        }
+        
+        virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
+                                                                        const Deprecated::ViewportGeometry& view,
+                                                                        MouseButton button,
+                                                                        KeyboardModifiers modifiers,
+                                                                        int viewportX,
+                                                                        int viewportY,
+                                                                        double x,
+                                                                        double y,
+                                                                        Deprecated::IStatusBar* statusBar,
+                                                                        const std::vector<Deprecated::Touch>& displayTouches)
+        {
+          return NULL;
+        }
+
+        virtual void MouseOver(CairoContext& context,
+                               Deprecated::WorldSceneWidget& widget,
+                               const Deprecated::ViewportGeometry& view,
+                               double x,
+                               double y,
+                               Deprecated::IStatusBar* statusBar)
+        {
+          if (statusBar != NULL)
+          {
+            Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
+            
+            char buf[64];
+            sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", 
+                    p[0] / 10.0, p[1] / 10.0, p[2] / 10.0);
+            statusBar->SetMessage(buf);
+          }
+        }
+
+        virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
+                                MouseWheelDirection direction,
+                                KeyboardModifiers modifiers,
+                                Deprecated::IStatusBar* statusBar)
+        {
+          int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1);
+          
+          switch (direction)
+          {
+            case MouseWheelDirection_Up:
+              application_.OffsetSlice(-scale);
+              break;
+
+            case MouseWheelDirection_Down:
+              application_.OffsetSlice(scale);
+              break;
+
+            default:
+              break;
+          }
+        }
+
+        virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
+                                KeyboardKeys key,
+                                char keyChar,
+                                KeyboardModifiers modifiers,
+                                Deprecated::IStatusBar* statusBar)
+        {
+          switch (keyChar)
+          {
+            case 's':
+              widget.FitContent();
+              break;
+
+            default:
+              break;
+          }
+        }
+      };
+
+
+      void OffsetSlice(int offset)
+      {
+        if (source_)
+        {
+          int slice = static_cast<int>(slice_) + offset;
+
+          if (slice < 0)
+          {
+            slice = 0;
+          }
+
+          if (slice >= static_cast<int>(source_->GetSlicesCount()))
+          {
+            slice = static_cast<int>(source_->GetSlicesCount()) - 1;
+          }
+
+          if (slice != static_cast<int>(slice_)) 
+          {
+            SetSlice(slice);
+          }   
+        }
+      }
+
+
+      void SetSlice(size_t index)
+      {
+        if (source_ &&
+            index < source_->GetSlicesCount())
+        {
+          slice_ = static_cast<unsigned int>(index);
+          
+#if 1
+          widget_->SetSlice(source_->GetSlice(slice_).GetGeometry());
+#else
+          // TEST for scene extents - Rotate the axes
+          double a = 15.0 / 180.0 * boost::math::constants::pi<double>();
+
+#if 1
+          Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0);
+          Vector y; GeometryToolbox::AssignVector(y, -sin(a), cos(a), 0);
+#else
+          // Flip the normal
+          Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0);
+          Vector y; GeometryToolbox::AssignVector(y, sin(a), -cos(a), 0);
+#endif
+          
+          SliceGeometry s(source_->GetSlice(slice_).GetGeometry().GetOrigin(), x, y);
+          widget_->SetSlice(s);
+#endif
+        }
+      }
+        
+      
+      void OnMainWidgetGeometryReady(const Deprecated::IVolumeSlicer::GeometryReadyMessage& message)
+      {
+        // Once the geometry of the series is downloaded from Orthanc,
+        // display its middle slice, and adapt the viewport to fit this
+        // slice
+        if (source_ &&
+            source_.get() == &message.GetOrigin())
+        {
+          SetSlice(source_->GetSlicesCount() / 2);
+        }
+
+        widget_->FitContent();
+      }
+
+      boost::shared_ptr<Deprecated::SliceViewerWidget>  widget_;
+      std::unique_ptr<Interactor>         mainWidgetInteractor_;
+      boost::shared_ptr<Deprecated::DicomSeriesVolumeSlicer> source_;
+      unsigned int                      slice_;
+
+    public:
+      SingleFrameApplication() :
+        slice_(0)
+      {
+      }
+      
+      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
+      {
+        boost::program_options::options_description generic("Sample options");
+        generic.add_options()
+          ("instance", boost::program_options::value<std::string>(), 
+           "Orthanc ID of the instance")
+          ("frame", boost::program_options::value<unsigned int>()->default_value(0),
+           "Number of the frame, for multi-frame DICOM instances")
+          ("smooth", boost::program_options::value<bool>()->default_value(true), 
+           "Enable bilinear interpolation to smooth the image")
+          ;
+
+        options.add(generic);    
+      }
+
+      virtual void Initialize(StoneApplicationContext* context,
+                              Deprecated::IStatusBar& statusBar,
+                              const boost::program_options::variables_map& parameters)
+      {
+        using namespace OrthancStone;
+
+        context_ = context;
+
+        statusBar.SetMessage("Use the key \"s\" to reinitialize the layout");
+
+        if (parameters.count("instance") != 1)
+        {
+          LOG(ERROR) << "The instance ID is missing";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+
+        std::string instance = parameters["instance"].as<std::string>();
+        int frame = parameters["frame"].as<unsigned int>();
+
+        widget_.reset(new Deprecated::SliceViewerWidget("main-widget"));
+        SetCentralWidget(widget_);
+
+        boost::shared_ptr<Deprecated::DicomSeriesVolumeSlicer> layer(new Deprecated::DicomSeriesVolumeSlicer);
+        layer->Connect(context->GetOrthancApiClient());
+        source_ = layer;
+
+        layer->LoadFrame(instance, frame);
+        Register<Deprecated::IVolumeSlicer::GeometryReadyMessage>(*layer, &SingleFrameApplication::OnMainWidgetGeometryReady);
+        widget_->AddLayer(layer);
+
+        Deprecated::RenderStyle s;
+
+        if (parameters["smooth"].as<bool>())
+        {
+          s.interpolation_ = ImageInterpolation_Bilinear;
+        }
+
+        widget_->SetLayerStyle(0, s);
+        widget_->SetTransmitMouseOver(true);
+
+        mainWidgetInteractor_.reset(new Interactor(*this));
+        widget_->SetInteractor(*mainWidgetInteractor_);
+      }
+    };
+
+
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SingleFrameEditorApplication.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,531 @@
+/**
+ * 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 "SampleApplicationBase.h"
+
+#include "../../../Framework/Radiography/RadiographyLayerCropTracker.h"
+#include "../../../Framework/Radiography/RadiographyLayerMaskTracker.h"
+#include "../../../Framework/Radiography/RadiographyLayerMoveTracker.h"
+#include "../../../Framework/Radiography/RadiographyLayerResizeTracker.h"
+#include "../../../Framework/Radiography/RadiographyLayerRotateTracker.h"
+#include "../../../Framework/Radiography/RadiographyMaskLayer.h"
+#include "../../../Framework/Radiography/RadiographyScene.h"
+#include "../../../Framework/Radiography/RadiographySceneCommand.h"
+#include "../../../Framework/Radiography/RadiographySceneReader.h"
+#include "../../../Framework/Radiography/RadiographySceneWriter.h"
+#include "../../../Framework/Radiography/RadiographyWidget.h"
+#include "../../../Framework/Radiography/RadiographyWindowingTracker.h"
+#include "../../../Framework/Toolbox/TextRenderer.h"
+
+#include <Core/HttpClient.h>
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <Core/Images/PngWriter.h>
+#include <Core/Images/PngReader.h>
+
+
+// Export using PAM is faster than using PNG, but requires Orthanc
+// core >= 1.4.3
+#define EXPORT_USING_PAM  1
+
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    class RadiographyEditorInteractor :
+        public Deprecated::IWorldSceneInteractor,
+        public ObserverBase<RadiographyEditorInteractor>
+    {
+    private:
+      enum Tool
+      {
+        Tool_Move,
+        Tool_Rotate,
+        Tool_Crop,
+        Tool_Resize,
+        Tool_Mask,
+        Tool_Windowing
+      };
+
+
+      StoneApplicationContext*  context_;
+      UndoRedoStack             undoRedoStack_;
+      Tool                      tool_;
+      RadiographyMaskLayer*     maskLayer_;
+
+
+      static double GetHandleSize()
+      {
+        return 10.0;
+      }
+
+
+    public:
+      RadiographyEditorInteractor() :
+        context_(NULL),
+        tool_(Tool_Move),
+        maskLayer_(NULL)
+      {
+      }
+
+      void SetContext(StoneApplicationContext& context)
+      {
+        context_ = &context;
+      }
+
+      void SetMaskLayer(RadiographyMaskLayer* maskLayer)
+      {
+        maskLayer_ = maskLayer;
+      }
+      virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& worldWidget,
+                                                                      const Deprecated::ViewportGeometry& view,
+                                                                      MouseButton button,
+                                                                      KeyboardModifiers modifiers,
+                                                                      int viewportX,
+                                                                      int viewportY,
+                                                                      double x,
+                                                                      double y,
+                                                                      Deprecated::IStatusBar* statusBar,
+                                                                      const std::vector<Deprecated::Touch>& displayTouches)
+      {
+        RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
+
+        if (button == MouseButton_Left)
+        {
+          size_t selected;
+
+          if (tool_ == Tool_Windowing)
+          {
+            return new RadiographyWindowingTracker(
+                  undoRedoStack_,
+                  widget.GetScene(),
+                  widget,
+                  OrthancStone::ImageInterpolation_Nearest,
+                  viewportX, viewportY,
+                  RadiographyWindowingTracker::Action_DecreaseWidth,
+                  RadiographyWindowingTracker::Action_IncreaseWidth,
+                  RadiographyWindowingTracker::Action_DecreaseCenter,
+                  RadiographyWindowingTracker::Action_IncreaseCenter);
+          }
+          else if (!widget.LookupSelectedLayer(selected))
+          {
+            // No layer is currently selected
+            size_t layer;
+            if (widget.GetScene().LookupLayer(layer, x, y))
+            {
+              widget.Select(layer);
+            }
+
+            return NULL;
+          }
+          else if (tool_ == Tool_Crop ||
+                   tool_ == Tool_Resize ||
+                   tool_ == Tool_Mask)
+          {
+            RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected);
+            
+            ControlPoint controlPoint;
+            if (accessor.GetLayer().LookupControlPoint(controlPoint, x, y, view.GetZoom(), GetHandleSize()))
+            {
+              switch (tool_)
+              {
+              case Tool_Crop:
+                return new RadiographyLayerCropTracker
+                    (undoRedoStack_, widget.GetScene(), view, selected, controlPoint);
+
+              case Tool_Mask:
+                return new RadiographyLayerMaskTracker
+                    (undoRedoStack_, widget.GetScene(), view, selected, controlPoint);
+
+              case Tool_Resize:
+                return new RadiographyLayerResizeTracker
+                    (undoRedoStack_, widget.GetScene(), selected, controlPoint,
+                     (modifiers & KeyboardModifiers_Shift));
+
+              default:
+                throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+              }
+            }
+            else
+            {
+              size_t layer;
+
+              if (widget.GetScene().LookupLayer(layer, x, y))
+              {
+                widget.Select(layer);
+              }
+              else
+              {
+                widget.Unselect();
+              }
+
+              return NULL;
+            }
+          }
+          else
+          {
+            size_t layer;
+
+            if (widget.GetScene().LookupLayer(layer, x, y))
+            {
+              if (layer == selected)
+              {
+                switch (tool_)
+                {
+                case Tool_Move:
+                  return new RadiographyLayerMoveTracker
+                      (undoRedoStack_, widget.GetScene(), layer, x, y,
+                       (modifiers & KeyboardModifiers_Shift));
+
+                case Tool_Rotate:
+                  return new RadiographyLayerRotateTracker
+                      (undoRedoStack_, widget.GetScene(), view, layer, x, y,
+                       (modifiers & KeyboardModifiers_Shift));
+
+                default:
+                  break;
+                }
+
+                return NULL;
+              }
+              else
+              {
+                widget.Select(layer);
+                return NULL;
+              }
+            }
+            else
+            {
+              widget.Unselect();
+              return NULL;
+            }
+          }
+        }
+        else
+        {
+          return NULL;
+        }
+        return NULL;
+      }
+
+      virtual void MouseOver(CairoContext& context,
+                             Deprecated::WorldSceneWidget& worldWidget,
+                             const Deprecated::ViewportGeometry& view,
+                             double x,
+                             double y,
+                             Deprecated::IStatusBar* statusBar)
+      {
+        RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
+
+#if 0
+        if (statusBar != NULL)
+        {
+          char buf[64];
+          sprintf(buf, "X = %.02f Y = %.02f (in cm)", x / 10.0, y / 10.0);
+          statusBar->SetMessage(buf);
+        }
+#endif
+
+        size_t selected;
+
+        if (widget.LookupSelectedLayer(selected) &&
+            (tool_ == Tool_Crop ||
+             tool_ == Tool_Resize ||
+             tool_ == Tool_Mask))
+        {
+          RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected);
+
+          ControlPoint controlPoint;
+          if (accessor.GetLayer().LookupControlPoint(controlPoint, x, y, view.GetZoom(), GetHandleSize()))
+          {
+            double z = 1.0 / view.GetZoom();
+
+            context.SetSourceColor(255, 0, 0);
+            cairo_t* cr = context.GetObject();
+            cairo_set_line_width(cr, 2.0 * z);
+            cairo_move_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y - GetHandleSize() * z);
+            cairo_line_to(cr, controlPoint.x + GetHandleSize() * z, controlPoint.y - GetHandleSize() * z);
+            cairo_line_to(cr, controlPoint.x + GetHandleSize() * z, controlPoint.y + GetHandleSize() * z);
+            cairo_line_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y + GetHandleSize() * z);
+            cairo_line_to(cr, controlPoint.x - GetHandleSize() * z, controlPoint.y - GetHandleSize() * z);
+            cairo_stroke(cr);
+          }
+        }
+      }
+
+      virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
+                              MouseWheelDirection direction,
+                              KeyboardModifiers modifiers,
+                              Deprecated::IStatusBar* statusBar)
+      {
+      }
+
+      virtual void KeyPressed(Deprecated::WorldSceneWidget& worldWidget,
+                              KeyboardKeys key,
+                              char keyChar,
+                              KeyboardModifiers modifiers,
+                              Deprecated::IStatusBar* statusBar)
+      {
+        RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
+
+        switch (keyChar)
+        {
+        case 'a':
+          widget.FitContent();
+          break;
+
+        case 'c':
+          tool_ = Tool_Crop;
+          break;
+
+        case 'm':
+          tool_ = Tool_Mask;
+          widget.Select(1);
+          break;
+
+        case 'd':
+        {
+          // dump to json and reload
+          Json::Value snapshot;
+          RadiographySceneWriter writer;
+          writer.Write(snapshot, widget.GetScene());
+
+          LOG(INFO) << "JSON export was successful: "
+                    << snapshot.toStyledString();
+
+          boost::shared_ptr<RadiographyScene> scene(new RadiographyScene);
+          RadiographySceneReader reader(*scene, *context_->GetOrthancApiClient());
+          reader.Read(snapshot);
+
+          widget.SetScene(scene);
+        };break;
+
+        case 'e':
+        {
+          Orthanc::DicomMap tags;
+
+          // Minimal set of tags to generate a valid CR image
+          tags.SetValue(Orthanc::DICOM_TAG_ACCESSION_NUMBER, "NOPE", false);
+          tags.SetValue(Orthanc::DICOM_TAG_BODY_PART_EXAMINED, "PELVIS", false);
+          tags.SetValue(Orthanc::DICOM_TAG_INSTANCE_NUMBER, "1", false);
+          //tags.SetValue(Orthanc::DICOM_TAG_LATERALITY, "", false);
+          tags.SetValue(Orthanc::DICOM_TAG_MANUFACTURER, "OSIMIS", false);
+          tags.SetValue(Orthanc::DICOM_TAG_MODALITY, "CR", false);
+          tags.SetValue(Orthanc::DICOM_TAG_PATIENT_BIRTH_DATE, "20000101", false);
+          tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ID, "hello", false);
+          tags.SetValue(Orthanc::DICOM_TAG_PATIENT_NAME, "HELLO^WORLD", false);
+          tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ORIENTATION, "", false);
+          tags.SetValue(Orthanc::DICOM_TAG_PATIENT_SEX, "M", false);
+          tags.SetValue(Orthanc::DICOM_TAG_REFERRING_PHYSICIAN_NAME, "HOUSE^MD", false);
+          tags.SetValue(Orthanc::DICOM_TAG_SERIES_NUMBER, "1", false);
+          tags.SetValue(Orthanc::DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.1", false);
+          tags.SetValue(Orthanc::DICOM_TAG_STUDY_ID, "STUDY", false);
+          tags.SetValue(Orthanc::DICOM_TAG_VIEW_POSITION, "", false);
+
+          if (context_ != NULL)
+          {
+            widget.GetScene().ExportDicom(*context_->GetOrthancApiClient(),
+                                          tags, std::string(), 0.1, 0.1, widget.IsInverted(),
+                                          false /* autoCrop */, widget.GetInterpolation(), EXPORT_USING_PAM);
+          }
+
+          break;
+        }
+
+        case 'i':
+          widget.SwitchInvert();
+          break;
+
+        case 't':
+          tool_ = Tool_Move;
+          break;
+
+        case 'n':
+        {
+          switch (widget.GetInterpolation())
+          {
+          case ImageInterpolation_Nearest:
+            LOG(INFO) << "Switching to bilinear interpolation";
+            widget.SetInterpolation(ImageInterpolation_Bilinear);
+            break;
+
+          case ImageInterpolation_Bilinear:
+            LOG(INFO) << "Switching to nearest neighbor interpolation";
+            widget.SetInterpolation(ImageInterpolation_Nearest);
+            break;
+
+          default:
+            throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+          }
+          
+          break;
+        }
+
+        case 'r':
+          tool_ = Tool_Rotate;
+          break;
+
+        case 's':
+          tool_ = Tool_Resize;
+          break;
+
+        case 'w':
+          tool_ = Tool_Windowing;
+          break;
+
+        case 'y':
+          if (modifiers & KeyboardModifiers_Control)
+          {
+            undoRedoStack_.Redo();
+            widget.NotifyContentChanged();
+          }
+          break;
+
+        case 'z':
+          if (modifiers & KeyboardModifiers_Control)
+          {
+            undoRedoStack_.Undo();
+            widget.NotifyContentChanged();
+          }
+          break;
+
+        default:
+          break;
+        }
+      }
+    };
+
+
+
+    class SingleFrameEditorApplication :
+        public SampleSingleCanvasApplicationBase,
+        public IObserver
+    {
+    private:
+      boost::shared_ptr<RadiographyScene>   scene_;
+      RadiographyEditorInteractor           interactor_;
+      RadiographyMaskLayer*                 maskLayer_;
+
+    public:
+      virtual ~SingleFrameEditorApplication()
+      {
+        LOG(WARNING) << "Destroying the application";
+      }
+      
+      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
+      {
+        boost::program_options::options_description generic("Sample options");
+        generic.add_options()
+            ("instance", boost::program_options::value<std::string>(),
+             "Orthanc ID of the instance")
+            ("frame", boost::program_options::value<unsigned int>()->default_value(0),
+             "Number of the frame, for multi-frame DICOM instances")
+            ;
+
+        options.add(generic);
+      }
+
+      virtual void Initialize(StoneApplicationContext* context,
+                              Deprecated::IStatusBar& statusBar,
+                              const boost::program_options::variables_map& parameters)
+      {
+        using namespace OrthancStone;
+
+        context_ = context;
+        interactor_.SetContext(*context);
+
+        statusBar.SetMessage("Use the key \"a\" to reinitialize the layout");
+        statusBar.SetMessage("Use the key \"c\" to crop");
+        statusBar.SetMessage("Use the key \"e\" to export DICOM to the Orthanc server");
+        statusBar.SetMessage("Use the key \"f\" to switch full screen");
+        statusBar.SetMessage("Use the key \"i\" to invert contrast");
+        statusBar.SetMessage("Use the key \"m\" to modify the mask");
+        statusBar.SetMessage("Use the key \"n\" to switch between nearest neighbor and bilinear interpolation");
+        statusBar.SetMessage("Use the key \"r\" to rotate objects");
+        statusBar.SetMessage("Use the key \"s\" to resize objects (not applicable to DICOM layers)");
+        statusBar.SetMessage("Use the key \"t\" to move (translate) objects");
+        statusBar.SetMessage("Use the key \"w\" to change windowing");
+        
+        statusBar.SetMessage("Use the key \"ctrl-z\" to undo action");
+        statusBar.SetMessage("Use the key \"ctrl-y\" to redo action");
+
+        if (parameters.count("instance") != 1)
+        {
+          LOG(ERROR) << "The instance ID is missing";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+
+        std::string instance = parameters["instance"].as<std::string>();
+        //int frame = parameters["frame"].as<unsigned int>();
+
+        scene_.reset(new RadiographyScene);
+        
+        RadiographyLayer& dicomLayer = scene_->LoadDicomFrame(*context->GetOrthancApiClient(), instance, 0, false, NULL);
+        //scene_->LoadDicomFrame(instance, frame, false); //.SetPan(200, 0);
+        // = scene_->LoadDicomFrame(context->GetOrthancApiClient(), "61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", 0, false, NULL);
+
+#if !defined(ORTHANC_ENABLE_WASM) || ORTHANC_ENABLE_WASM != 1
+        Orthanc::HttpClient::ConfigureSsl(true, "/etc/ssl/certs/ca-certificates.crt");
+#endif
+        
+        //scene_->LoadDicomWebFrame(context->GetWebService());
+        
+        std::vector<Orthanc::ImageProcessing::ImagePoint> mask;
+        mask.push_back(Orthanc::ImageProcessing::ImagePoint(1100, 100));
+        mask.push_back(Orthanc::ImageProcessing::ImagePoint(1100, 1000));
+        mask.push_back(Orthanc::ImageProcessing::ImagePoint(2000, 1000));
+        mask.push_back(Orthanc::ImageProcessing::ImagePoint(2200, 150));
+        mask.push_back(Orthanc::ImageProcessing::ImagePoint(1500, 550));
+        maskLayer_ = dynamic_cast<RadiographyMaskLayer*>(&(scene_->LoadMask(mask, dynamic_cast<RadiographyDicomLayer&>(dicomLayer), 128.0f, NULL)));
+        interactor_.SetMaskLayer(maskLayer_);
+
+        {
+          std::unique_ptr<Orthanc::ImageAccessor> renderedTextAlpha(TextRenderer::Render(Orthanc::EmbeddedResources::UBUNTU_FONT, 100,
+                                                                                    "%öÇaA&#"));
+          RadiographyLayer& layer = scene_->LoadAlphaBitmap(renderedTextAlpha.release(), NULL);
+          dynamic_cast<RadiographyAlphaLayer&>(layer).SetForegroundValue(200.0f * 256.0f);
+        }
+
+        {
+          RadiographyTextLayer::RegisterFont("ubuntu", Orthanc::EmbeddedResources::UBUNTU_FONT);
+          RadiographyLayer& layer = scene_->LoadText("Hello\nworld", "ubuntu", 20, 128, NULL, false);
+          layer.SetResizeable(true);
+        }
+        
+        {
+          RadiographyLayer& layer = scene_->LoadTestBlock(100, 50, NULL);
+          layer.SetResizeable(true);
+          layer.SetPan(0, 200);
+        }
+        
+        boost::shared_ptr<RadiographyWidget> widget(new RadiographyWidget(scene_, "main-widget"));
+        widget->SetTransmitMouseOver(true);
+        widget->SetInteractor(interactor_);
+        SetCentralWidget(widget);
+
+        //scene_->SetWindowing(128, 256);
+      }
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SingleVolumeApplication.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,277 @@
+/**
+ * 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 "SampleApplicationBase.h"
+#include "../../../Framework/dev.h"
+#include "../../../Framework/Layers/LineMeasureTracker.h"
+#include "../../../Framework/Layers/CircleMeasureTracker.h"
+
+#include <Core/Toolbox.h>
+#include <Core/Logging.h>
+
+#include <Plugins/Samples/Common/OrthancHttpConnection.h>   // TODO REMOVE
+#include "../../../Framework/Layers/DicomStructureSetSlicer.h"   // TODO REMOVE
+#include "../../../Framework/Toolbox/MessagingToolbox.h"   // TODO REMOVE
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    class SingleVolumeApplication : public SampleApplicationBase
+    {
+    private:
+      class Interactor : public VolumeImageInteractor
+      {
+      private:
+        SliceViewerWidget&  widget_;
+        size_t        layer_;
+        
+      protected:
+        virtual void NotifySliceContentChange(const ISlicedVolume& volume,
+                                       const size_t& sliceIndex,
+                                       const Slice& slice)
+        {
+          const OrthancVolumeImage& image = dynamic_cast<const OrthancVolumeImage&>(volume);
+
+          RenderStyle s = widget_.GetLayerStyle(layer_);
+
+          if (image.FitWindowingToRange(s, slice.GetConverter()))
+          {
+            //printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_);
+            widget_.SetLayerStyle(layer_, s);
+          }
+        }
+
+        virtual void MouseOver(CairoContext& context,
+                               WorldSceneWidget& widget,
+                               const ViewportGeometry& view,
+                               double x,
+                               double y,
+                               IStatusBar* statusBar)
+        {
+          const SliceViewerWidget& w = dynamic_cast<const SliceViewerWidget&>(widget);
+          Vector p = w.GetSlice().MapSliceToWorldCoordinates(x, y);
+          printf("%f %f %f\n", p[0], p[1], p[2]);
+        }
+      
+      public:
+        Interactor(OrthancVolumeImage& volume,
+                   SliceViewerWidget& widget,
+                   VolumeProjection projection,
+                   size_t layer) :
+          VolumeImageInteractor(volume, widget, projection),
+          widget_(widget),
+          layer_(layer)
+        {
+        }
+      };
+
+
+    public:
+      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
+      {
+        boost::program_options::options_description generic("Sample options");
+        generic.add_options()
+          ("series", boost::program_options::value<std::string>(), 
+           "Orthanc ID of the series")
+          ("instance", boost::program_options::value<std::string>(), 
+           "Orthanc ID of a multi-frame instance that describes a 3D volume")
+          ("threads", boost::program_options::value<unsigned int>()->default_value(3), 
+           "Number of download threads")
+          ("projection", boost::program_options::value<std::string>()->default_value("axial"), 
+           "Projection of interest (can be axial, sagittal or coronal)")
+          ("reverse", boost::program_options::value<bool>()->default_value(false), 
+           "Reverse the normal direction of the volume")
+          ;
+
+        options.add(generic);    
+      }
+
+      virtual void Initialize(IStatusBar& statusBar,
+                              const boost::program_options::variables_map& parameters)
+      {
+        using namespace OrthancStone;
+
+        if (parameters.count("series") > 1 ||
+            parameters.count("instance") > 1)
+        {
+          LOG(ERROR) << "Only one series or instance is allowed";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+
+        if (parameters.count("series") == 1 &&
+            parameters.count("instance") == 1)
+        {
+          LOG(ERROR) << "Cannot specify both a series and an instance";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+
+        std::string series;
+        if (parameters.count("series") == 1)
+        {
+          series = parameters["series"].as<std::string>();
+        }
+        
+        std::string instance;
+        if (parameters.count("instance") == 1)
+        {
+          instance = parameters["instance"].as<std::string>();
+        }
+        
+        if (series.empty() &&
+            instance.empty())
+        {
+          LOG(ERROR) << "The series ID or instance ID is missing";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+
+        //unsigned int threads = parameters["threads"].as<unsigned int>();
+        //bool reverse = parameters["reverse"].as<bool>();
+
+        std::string tmp = parameters["projection"].as<std::string>();
+        Orthanc::Toolbox::ToLowerCase(tmp);
+
+        VolumeProjection projection;
+        if (tmp == "axial")
+        {
+          projection = VolumeProjection_Axial;
+        }
+        else if (tmp == "sagittal")
+        {
+          projection = VolumeProjection_Sagittal;
+        }
+        else if (tmp == "coronal")
+        {
+          projection = VolumeProjection_Coronal;
+        }
+        else
+        {
+          LOG(ERROR) << "Unknown projection: " << tmp;
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+
+        std::unique_ptr<SliceViewerWidget> widget(new SliceViewerWidget);
+
+#if 1
+        std::unique_ptr<OrthancVolumeImage> volume(new OrthancVolumeImage(context.GetWebService(), true));
+        if (series.empty())
+        {
+          volume->ScheduleLoadInstance(instance);
+        }
+        else
+        {
+          volume->ScheduleLoadSeries(series);
+        }
+
+        widget->AddLayer(new VolumeImageMPRSlicer(*volume));
+
+        context_->AddInteractor(new Interactor(*volume, *widget, projection, 0));
+        context_->AddSlicedVolume(volume.release());
+
+        if (1)
+        {
+          RenderStyle s;
+          //s.drawGrid_ = true;
+          s.alpha_ = 1;
+          s.windowing_ = ImageWindowing_Bone;
+          widget->SetLayerStyle(0, s);
+        }
+        else
+        {
+          RenderStyle s;
+          s.alpha_ = 1;
+          s.applyLut_ = true;
+          s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET;
+          s.interpolation_ = ImageInterpolation_Bilinear;
+          widget->SetLayerStyle(0, s);
+        }
+#else
+        std::unique_ptr<OrthancVolumeImage> ct(new OrthancVolumeImage(context_->GetWebService(), false));
+        //ct->ScheduleLoadSeries("15a6f44a-ac7b88fe-19c462d9-dddd918e-b01550d8");  // 0178023P
+        //ct->ScheduleLoadSeries("dd069910-4f090474-7d2bba07-e5c10783-f9e4fb1d");
+        //ct->ScheduleLoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // IBA
+        //ct->ScheduleLoadSeries("03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0");  // 0522c0001 TCIA
+        ct->ScheduleLoadSeries("295e8a13-dfed1320-ba6aebb2-9a13e20f-1b3eb953");  // Captain
+        
+        std::unique_ptr<OrthancVolumeImage> pet(new OrthancVolumeImage(context_->GetWebService(), true));
+        //pet->ScheduleLoadSeries("48d2997f-8e25cd81-dd715b64-bd79cdcc-e8fcee53");  // 0178023P
+        //pet->ScheduleLoadSeries("aabad2e7-80702b5d-e599d26c-4f13398e-38d58a9e");
+        //pet->ScheduleLoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // IBA 1
+        //pet->ScheduleLoadInstance("337876a1-a68a9718-f15abccd-38faafa1-b99b496a"); // IBA 2
+        //pet->ScheduleLoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb");  // IBA 3
+        //pet->ScheduleLoadInstance("269f26f4-0c83eeeb-2e67abbd-5467a40f-f1bec90c");  // 0522c0001 TCIA
+        pet->ScheduleLoadInstance("f080888c-0ab7528a-f7d9c28c-84980eb1-ff3b0ae6");  // Captain 1
+        //pet->ScheduleLoadInstance("4f78055b-6499a2c5-1e089290-394acc05-3ec781c1");  // Captain 2
+
+        std::unique_ptr<StructureSetLoader> rtStruct(new StructureSetLoader(context_->GetWebService()));
+        //rtStruct->ScheduleLoadInstance("c2ebc17b-6b3548db-5e5da170-b8ecab71-ea03add3");  // 0178023P
+        //rtStruct->ScheduleLoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // IBA
+        //rtStruct->ScheduleLoadInstance("17cd032b-ad92a438-ca05f06a-f9e96668-7e3e9e20");  // 0522c0001 TCIA
+        rtStruct->ScheduleLoadInstance("96c889ab-29fe5c54-dda6e66c-3949e4da-58f90d75");  // Captain
+        
+        widget->AddLayer(new VolumeImageMPRSlicer(*ct));
+        widget->AddLayer(new VolumeImageMPRSlicer(*pet));
+        widget->AddLayer(new DicomStructureSetSlicer(*rtStruct));
+        
+        context_->AddInteractor(new Interactor(*pet, *widget, projection, 1));
+        //context_->AddInteractor(new VolumeImageInteractor(*ct, *widget, projection));
+
+        context_->AddSlicedVolume(ct.release());
+        context_->AddSlicedVolume(pet.release());
+        context_->AddVolumeLoader(rtStruct.release());
+
+        {
+          RenderStyle s;
+          //s.drawGrid_ = true;
+          s.alpha_ = 1;
+          s.windowing_ = ImageWindowing_Bone;
+          widget->SetLayerStyle(0, s);
+        }
+
+        {
+          RenderStyle s;
+          //s.drawGrid_ = true;
+          s.SetColor(255, 0, 0);  // Draw missing PET layer in red
+          s.alpha_ = 0.5;
+          s.applyLut_ = true;
+          s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET;
+          s.interpolation_ = ImageInterpolation_Bilinear;
+          s.windowing_ = ImageWindowing_Custom;
+          s.customWindowCenter_ = 0;
+          s.customWindowWidth_ = 128;
+          widget->SetLayerStyle(1, s);
+        }
+#endif
+
+
+        statusBar.SetMessage("Use the keys \"b\", \"l\" and \"d\" to change Hounsfield windowing");
+        statusBar.SetMessage("Use the keys \"t\" to track the (X,Y,Z) mouse coordinates");
+        statusBar.SetMessage("Use the keys \"m\" to measure distances");
+        statusBar.SetMessage("Use the keys \"c\" to draw circles");
+
+        widget->SetTransmitMouseOver(true);
+        context_->SetCentralWidget(widget.release());
+      }
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/StoneSampleCommands.yml	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,35 @@
+#
+#        1         2         3         4         5         6         7         8
+# 345678901234567890123456789012345678901234567890123456789012345678901234567890
+#
+rootName: StoneSampleCommands
+
+# +---------------------------------+
+# | Messages from TypeScript to C++ |
+# +---------------------------------+
+
+enum Tool:
+  - LineMeasure
+  - CircleMeasure
+  - Crop
+  - Windowing
+  - Zoom
+  - Pan
+  - Move
+  - Rotate
+  - Resize
+  - Mask
+
+struct SelectTool:
+  __handler: cpp
+  tool: Tool
+
+enum ActionType:
+  - UndoCrop
+  - Rotate
+  - Invert
+
+struct Action:
+  __handler: cpp
+  type: ActionType
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/StoneSampleCommands_generate.py	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,16 @@
+import sys
+import os
+
+# add the generation script location to the search paths
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'Resources', 'CodeGeneration'))
+
+# import the code generation tooling script
+import stonegentool
+
+schemaFile = os.path.join(os.path.dirname(__file__), 'StoneSampleCommands.yml')
+outDir = os.path.dirname(__file__)
+
+# ignition!
+stonegentool.Process(schemaFile, outDir)
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/StoneSampleCommands_generated.hpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,703 @@
+/*
+         1         2         3         4         5         6         7
+12345678901234567890123456789012345678901234567890123456789012345678901234567890
+
+Generated on 2019-03-18 12:07:42.696093 by stonegentool
+
+*/
+#pragma once
+
+#include <exception>
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <assert.h>
+#include <memory>
+#include <json/json.h>
+
+//#define STONEGEN_NO_CPP11 1
+
+#ifdef STONEGEN_NO_CPP11
+#define StoneSmartPtr std::unique_ptr
+#else 
+#define StoneSmartPtr std::unique_ptr
+#endif 
+
+namespace StoneSampleCommands
+{
+  /** Throws in case of problem */
+  inline void _StoneDeserializeValue(int32_t& destValue, const Json::Value& jsonValue)
+  {
+    destValue = jsonValue.asInt();
+  }
+
+  inline Json::Value _StoneSerializeValue(int32_t value)
+  {
+    Json::Value result(value);
+    return result;
+  }
+
+  inline void _StoneDeserializeValue(Json::Value& destValue, const Json::Value& jsonValue)
+  {
+    destValue = jsonValue;
+  }
+
+  inline Json::Value _StoneSerializeValue(Json::Value value)
+  {
+    return value;
+  }
+
+  /** Throws in case of problem */
+  inline void _StoneDeserializeValue(double& destValue, const Json::Value& jsonValue)
+  {
+    destValue = jsonValue.asDouble();
+  }
+
+  inline Json::Value _StoneSerializeValue(double value)
+  {
+    Json::Value result(value);
+    return result;
+  }
+
+  /** Throws in case of problem */
+  inline void _StoneDeserializeValue(bool& destValue, const Json::Value& jsonValue)
+  {
+    destValue = jsonValue.asBool();
+  }
+
+  inline Json::Value _StoneSerializeValue(bool value)
+  {
+    Json::Value result(value);
+    return result;
+  }
+
+  /** Throws in case of problem */
+  inline void _StoneDeserializeValue(
+       std::string& destValue
+     , const Json::Value& jsonValue)
+  {
+    destValue = jsonValue.asString();
+  }
+
+  inline Json::Value _StoneSerializeValue(const std::string& value)
+  {
+    // the following is better than 
+    Json::Value result(value.data(),value.data()+value.size());
+    return result;
+  }
+
+  inline std::string MakeIndent(size_t indent)
+  {
+    char* txt = reinterpret_cast<char*>(malloc(indent+1)); // NO EXCEPTION BELOW!!!!!!!!!!!!
+    for(size_t i = 0; i < indent; ++i)
+      txt[i] = ' ';
+    txt[indent] = 0;
+    std::string retVal(txt);
+    free(txt); // NO EXCEPTION ABOVE !!!!!!!!!!
+    return retVal;
+  }
+
+  // generic dumper
+  template<typename T>
+  std::ostream& StoneDumpValue(std::ostream& out, const T& value, size_t indent)
+  {
+    out << MakeIndent(indent) << value;
+    return out;
+  }
+
+  // string dumper
+  inline std::ostream& StoneDumpValue(std::ostream& out, const std::string& value, size_t indent)
+  {
+    out << MakeIndent(indent) << "\"" << value  << "\"";
+    return out;
+  }
+
+  /** Throws in case of problem */
+  template<typename T>
+  void _StoneDeserializeValue(
+    std::map<std::string, T>& destValue, const Json::Value& jsonValue)
+  {
+    destValue.clear();
+    for (
+      Json::Value::const_iterator itr = jsonValue.begin();
+      itr != jsonValue.end();
+      itr++)
+    {
+      std::string key;
+      _StoneDeserializeValue(key, itr.key());
+
+      T innerDestValue;
+      _StoneDeserializeValue(innerDestValue, *itr);
+
+      destValue[key] = innerDestValue;
+    }
+  }
+
+  template<typename T>
+  Json::Value _StoneSerializeValue(const std::map<std::string,T>& value)
+  {
+    Json::Value result(Json::objectValue);
+
+    for (typename std::map<std::string, T>::const_iterator it = value.cbegin();
+      it != value.cend(); ++it)
+    {
+      // it->first it->second
+      result[it->first] = _StoneSerializeValue(it->second);
+    }
+    return result;
+  }
+
+  template<typename T>
+  std::ostream& StoneDumpValue(std::ostream& out, const std::map<std::string,T>& value, size_t indent)
+  {
+    out << MakeIndent(indent) << "{\n";
+    for (typename std::map<std::string, T>::const_iterator it = value.cbegin();
+      it != value.cend(); ++it)
+    {
+      out << MakeIndent(indent+2) << "\"" << it->first << "\" : ";
+      StoneDumpValue(out, it->second, indent+2);
+    }
+    out << MakeIndent(indent) << "}\n";
+    return out;
+  }
+
+  /** Throws in case of problem */
+  template<typename T>
+  void _StoneDeserializeValue(
+    std::vector<T>& destValue, const Json::Value& jsonValue)
+  {
+    destValue.clear();
+    destValue.reserve(jsonValue.size());
+    for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++)
+    {
+      T innerDestValue;
+      _StoneDeserializeValue(innerDestValue, jsonValue[i]);
+      destValue.push_back(innerDestValue);
+    }
+  }
+
+  template<typename T>
+  Json::Value _StoneSerializeValue(const std::vector<T>& value)
+  {
+    Json::Value result(Json::arrayValue);
+    for (size_t i = 0; i < value.size(); ++i)
+    {
+      result.append(_StoneSerializeValue(value[i]));
+    }
+    return result;
+  }
+
+  template<typename T>
+  std::ostream& StoneDumpValue(std::ostream& out, const std::vector<T>& value, size_t indent)
+  {
+    out << MakeIndent(indent) << "[\n";
+    for (size_t i = 0; i < value.size(); ++i)
+    {
+      StoneDumpValue(out, value[i], indent+2);
+    }
+    out << MakeIndent(indent) << "]\n";
+    return out;
+  }
+
+  inline void StoneCheckSerializedValueTypeGeneric(const Json::Value& value)
+  {
+    if ((!value.isMember("type")) || (!value["type"].isString()))
+    {
+      std::stringstream ss;
+      ss << "Cannot deserialize value ('type' key invalid)";
+      throw std::runtime_error(ss.str());
+    }
+  }
+
+  inline void StoneCheckSerializedValueType(
+    const Json::Value& value, std::string typeStr)
+  {
+    StoneCheckSerializedValueTypeGeneric(value);
+
+    std::string actTypeStr = value["type"].asString();
+    if (actTypeStr != typeStr)
+    {
+      std::stringstream ss;
+      ss << "Cannot deserialize type" << actTypeStr
+        << "into " << typeStr;
+      throw std::runtime_error(ss.str());
+    }
+  }
+
+  // end of generic methods
+
+// end of generic methods
+
+  enum Tool {
+    Tool_LineMeasure,
+    Tool_CircleMeasure,
+    Tool_Crop,
+    Tool_Windowing,
+    Tool_Zoom,
+    Tool_Pan,
+    Tool_Move,
+    Tool_Rotate,
+    Tool_Resize,
+    Tool_Mask,
+  };
+
+  inline std::string ToString(const Tool& value)
+  {
+    if( value == Tool_LineMeasure)
+    {
+      return std::string("LineMeasure");
+    }
+    if( value == Tool_CircleMeasure)
+    {
+      return std::string("CircleMeasure");
+    }
+    if( value == Tool_Crop)
+    {
+      return std::string("Crop");
+    }
+    if( value == Tool_Windowing)
+    {
+      return std::string("Windowing");
+    }
+    if( value == Tool_Zoom)
+    {
+      return std::string("Zoom");
+    }
+    if( value == Tool_Pan)
+    {
+      return std::string("Pan");
+    }
+    if( value == Tool_Move)
+    {
+      return std::string("Move");
+    }
+    if( value == Tool_Rotate)
+    {
+      return std::string("Rotate");
+    }
+    if( value == Tool_Resize)
+    {
+      return std::string("Resize");
+    }
+    if( value == Tool_Mask)
+    {
+      return std::string("Mask");
+    }
+    std::stringstream ss;
+    ss << "Value \"" << value << "\" cannot be converted to Tool. Possible values are: "
+        << " LineMeasure = " << static_cast<int64_t>(Tool_LineMeasure)  << ", " 
+        << " CircleMeasure = " << static_cast<int64_t>(Tool_CircleMeasure)  << ", " 
+        << " Crop = " << static_cast<int64_t>(Tool_Crop)  << ", " 
+        << " Windowing = " << static_cast<int64_t>(Tool_Windowing)  << ", " 
+        << " Zoom = " << static_cast<int64_t>(Tool_Zoom)  << ", " 
+        << " Pan = " << static_cast<int64_t>(Tool_Pan)  << ", " 
+        << " Move = " << static_cast<int64_t>(Tool_Move)  << ", " 
+        << " Rotate = " << static_cast<int64_t>(Tool_Rotate)  << ", " 
+        << " Resize = " << static_cast<int64_t>(Tool_Resize)  << ", " 
+        << " Mask = " << static_cast<int64_t>(Tool_Mask)  << ", " 
+        << std::endl;
+    std::string msg = ss.str();
+    throw std::runtime_error(msg);
+  }
+
+  inline void FromString(Tool& value, std::string strValue)
+  {
+    if( strValue == std::string("LineMeasure") )
+    {
+      value = Tool_LineMeasure;
+      return;
+    }
+    if( strValue == std::string("CircleMeasure") )
+    {
+      value = Tool_CircleMeasure;
+      return;
+    }
+    if( strValue == std::string("Crop") )
+    {
+      value = Tool_Crop;
+      return;
+    }
+    if( strValue == std::string("Windowing") )
+    {
+      value = Tool_Windowing;
+      return;
+    }
+    if( strValue == std::string("Zoom") )
+    {
+      value = Tool_Zoom;
+      return;
+    }
+    if( strValue == std::string("Pan") )
+    {
+      value = Tool_Pan;
+      return;
+    }
+    if( strValue == std::string("Move") )
+    {
+      value = Tool_Move;
+      return;
+    }
+    if( strValue == std::string("Rotate") )
+    {
+      value = Tool_Rotate;
+      return;
+    }
+    if( strValue == std::string("Resize") )
+    {
+      value = Tool_Resize;
+      return;
+    }
+    if( strValue == std::string("Mask") )
+    {
+      value = Tool_Mask;
+      return;
+    }
+
+    std::stringstream ss;
+    ss << "String \"" << strValue << "\" cannot be converted to Tool. Possible values are: LineMeasure CircleMeasure Crop Windowing Zoom Pan Move Rotate Resize Mask ";
+    std::string msg = ss.str();
+    throw std::runtime_error(msg);
+  }
+
+
+  inline void _StoneDeserializeValue(
+    Tool& destValue, const Json::Value& jsonValue)
+  {
+    FromString(destValue, jsonValue.asString());
+  }
+
+  inline Json::Value _StoneSerializeValue(const Tool& value)
+  {
+    std::string strValue = ToString(value);
+    return Json::Value(strValue);
+  }
+
+  inline std::ostream& StoneDumpValue(std::ostream& out, const Tool& value, size_t indent = 0)
+  {
+    if( value == Tool_LineMeasure)
+    {
+      out << MakeIndent(indent) << "LineMeasure" << std::endl;
+    }
+    if( value == Tool_CircleMeasure)
+    {
+      out << MakeIndent(indent) << "CircleMeasure" << std::endl;
+    }
+    if( value == Tool_Crop)
+    {
+      out << MakeIndent(indent) << "Crop" << std::endl;
+    }
+    if( value == Tool_Windowing)
+    {
+      out << MakeIndent(indent) << "Windowing" << std::endl;
+    }
+    if( value == Tool_Zoom)
+    {
+      out << MakeIndent(indent) << "Zoom" << std::endl;
+    }
+    if( value == Tool_Pan)
+    {
+      out << MakeIndent(indent) << "Pan" << std::endl;
+    }
+    if( value == Tool_Move)
+    {
+      out << MakeIndent(indent) << "Move" << std::endl;
+    }
+    if( value == Tool_Rotate)
+    {
+      out << MakeIndent(indent) << "Rotate" << std::endl;
+    }
+    if( value == Tool_Resize)
+    {
+      out << MakeIndent(indent) << "Resize" << std::endl;
+    }
+    if( value == Tool_Mask)
+    {
+      out << MakeIndent(indent) << "Mask" << std::endl;
+    }
+    return out;
+  }
+
+
+  enum ActionType {
+    ActionType_UndoCrop,
+    ActionType_Rotate,
+    ActionType_Invert,
+  };
+
+  inline std::string ToString(const ActionType& value)
+  {
+    if( value == ActionType_UndoCrop)
+    {
+      return std::string("UndoCrop");
+    }
+    if( value == ActionType_Rotate)
+    {
+      return std::string("Rotate");
+    }
+    if( value == ActionType_Invert)
+    {
+      return std::string("Invert");
+    }
+    std::stringstream ss;
+    ss << "Value \"" << value << "\" cannot be converted to ActionType. Possible values are: "
+        << " UndoCrop = " << static_cast<int64_t>(ActionType_UndoCrop)  << ", " 
+        << " Rotate = " << static_cast<int64_t>(ActionType_Rotate)  << ", " 
+        << " Invert = " << static_cast<int64_t>(ActionType_Invert)  << ", " 
+        << std::endl;
+    std::string msg = ss.str();
+    throw std::runtime_error(msg);
+  }
+
+  inline void FromString(ActionType& value, std::string strValue)
+  {
+    if( strValue == std::string("UndoCrop") )
+    {
+      value = ActionType_UndoCrop;
+      return;
+    }
+    if( strValue == std::string("Rotate") )
+    {
+      value = ActionType_Rotate;
+      return;
+    }
+    if( strValue == std::string("Invert") )
+    {
+      value = ActionType_Invert;
+      return;
+    }
+
+    std::stringstream ss;
+    ss << "String \"" << strValue << "\" cannot be converted to ActionType. Possible values are: UndoCrop Rotate Invert ";
+    std::string msg = ss.str();
+    throw std::runtime_error(msg);
+  }
+
+
+  inline void _StoneDeserializeValue(
+    ActionType& destValue, const Json::Value& jsonValue)
+  {
+    FromString(destValue, jsonValue.asString());
+  }
+
+  inline Json::Value _StoneSerializeValue(const ActionType& value)
+  {
+    std::string strValue = ToString(value);
+    return Json::Value(strValue);
+  }
+
+  inline std::ostream& StoneDumpValue(std::ostream& out, const ActionType& value, size_t indent = 0)
+  {
+    if( value == ActionType_UndoCrop)
+    {
+      out << MakeIndent(indent) << "UndoCrop" << std::endl;
+    }
+    if( value == ActionType_Rotate)
+    {
+      out << MakeIndent(indent) << "Rotate" << std::endl;
+    }
+    if( value == ActionType_Invert)
+    {
+      out << MakeIndent(indent) << "Invert" << std::endl;
+    }
+    return out;
+  }
+
+
+
+#ifdef _MSC_VER
+#pragma region SelectTool
+#endif //_MSC_VER
+
+  struct SelectTool
+  {
+    Tool tool;
+
+    SelectTool(Tool tool = Tool())
+    {
+      this->tool = tool;
+    }
+  };
+
+  inline void _StoneDeserializeValue(SelectTool& destValue, const Json::Value& value)
+  {
+    _StoneDeserializeValue(destValue.tool, value["tool"]);
+    }
+
+  inline Json::Value _StoneSerializeValue(const SelectTool& value)
+  {
+    Json::Value result(Json::objectValue);
+    result["tool"] = _StoneSerializeValue(value.tool);
+
+    return result;
+  }
+
+  inline std::ostream& StoneDumpValue(std::ostream& out, const SelectTool& value, size_t indent = 0)
+  {
+    out << MakeIndent(indent) << "{\n";
+    out << MakeIndent(indent) << "tool:\n";
+    StoneDumpValue(out, value.tool,indent+2);
+    out << "\n";
+
+    out << MakeIndent(indent) << "}\n";
+    return out;
+  }
+
+  inline void StoneDeserialize(SelectTool& destValue, const Json::Value& value)
+  {
+    StoneCheckSerializedValueType(value, "StoneSampleCommands.SelectTool");
+    _StoneDeserializeValue(destValue, value["value"]);
+  }
+
+  inline Json::Value StoneSerializeToJson(const SelectTool& value)
+  {
+    Json::Value result(Json::objectValue);
+    result["type"] = "StoneSampleCommands.SelectTool";
+    result["value"] = _StoneSerializeValue(value);
+    return result;
+  }
+
+  inline std::string StoneSerialize(const SelectTool& value)
+  {
+    Json::Value resultJson = StoneSerializeToJson(value);
+    std::string resultStr = resultJson.toStyledString();
+    return resultStr;
+  }
+
+#ifdef _MSC_VER
+#pragma endregion SelectTool
+#endif //_MSC_VER
+
+#ifdef _MSC_VER
+#pragma region Action
+#endif //_MSC_VER
+
+  struct Action
+  {
+    ActionType type;
+
+    Action(ActionType type = ActionType())
+    {
+      this->type = type;
+    }
+  };
+
+  inline void _StoneDeserializeValue(Action& destValue, const Json::Value& value)
+  {
+    _StoneDeserializeValue(destValue.type, value["type"]);
+    }
+
+  inline Json::Value _StoneSerializeValue(const Action& value)
+  {
+    Json::Value result(Json::objectValue);
+    result["type"] = _StoneSerializeValue(value.type);
+
+    return result;
+  }
+
+  inline std::ostream& StoneDumpValue(std::ostream& out, const Action& value, size_t indent = 0)
+  {
+    out << MakeIndent(indent) << "{\n";
+    out << MakeIndent(indent) << "type:\n";
+    StoneDumpValue(out, value.type,indent+2);
+    out << "\n";
+
+    out << MakeIndent(indent) << "}\n";
+    return out;
+  }
+
+  inline void StoneDeserialize(Action& destValue, const Json::Value& value)
+  {
+    StoneCheckSerializedValueType(value, "StoneSampleCommands.Action");
+    _StoneDeserializeValue(destValue, value["value"]);
+  }
+
+  inline Json::Value StoneSerializeToJson(const Action& value)
+  {
+    Json::Value result(Json::objectValue);
+    result["type"] = "StoneSampleCommands.Action";
+    result["value"] = _StoneSerializeValue(value);
+    return result;
+  }
+
+  inline std::string StoneSerialize(const Action& value)
+  {
+    Json::Value resultJson = StoneSerializeToJson(value);
+    std::string resultStr = resultJson.toStyledString();
+    return resultStr;
+  }
+
+#ifdef _MSC_VER
+#pragma endregion Action
+#endif //_MSC_VER
+
+#ifdef _MSC_VER
+#pragma region Dispatching code
+#endif //_MSC_VER
+
+  class IHandler
+  {
+  public:
+    virtual bool Handle(const SelectTool& value) = 0;
+    virtual bool Handle(const Action& value) = 0;
+  };
+
+  /** Service function for StoneDispatchToHandler */
+  inline bool StoneDispatchJsonToHandler(
+    const Json::Value& jsonValue, IHandler* handler)
+  {
+    StoneCheckSerializedValueTypeGeneric(jsonValue);
+    std::string type = jsonValue["type"].asString();
+    if (type == "")
+    {
+      // this should never ever happen
+      throw std::runtime_error("Caught empty type while dispatching");
+    }
+    else if (type == "StoneSampleCommands.SelectTool")
+    {
+      SelectTool value;
+      _StoneDeserializeValue(value, jsonValue["value"]);
+      return handler->Handle(value);
+    }
+    else if (type == "StoneSampleCommands.Action")
+    {
+      Action value;
+      _StoneDeserializeValue(value, jsonValue["value"]);
+      return handler->Handle(value);
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  /** Takes a serialized type and passes this to the handler */
+  inline bool StoneDispatchToHandler(std::string strValue, IHandler* handler)
+  {
+    Json::Value readValue;
+
+    Json::CharReaderBuilder builder;
+    Json::CharReader* reader = builder.newCharReader();
+
+    StoneSmartPtr<Json::CharReader> ptr(reader);
+
+    std::string errors;
+
+    bool ok = reader->parse(
+      strValue.c_str(),
+      strValue.c_str() + strValue.size(),
+      &readValue,
+      &errors
+    );
+    if (!ok)
+    {
+      std::stringstream ss;
+      ss << "Jsoncpp parsing error: " << errors;
+      throw std::runtime_error(ss.str());
+    }
+    return StoneDispatchJsonToHandler(readValue, handler);
+  }
+
+#ifdef _MSC_VER
+#pragma endregion Dispatching code
+#endif //_MSC_VER
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/StoneSampleCommands_generated.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,333 @@
+/*
+         1         2         3         4         5         6         7
+12345678901234567890123456789012345678901234567890123456789012345678901234567890
+
+Generated on 2019-03-18 12:07:42.696093 by stonegentool
+
+*/
+
+function StoneCheckSerializedValueType(value: any, typeStr: string)
+{
+  StoneCheckSerializedValueTypeGeneric(value);
+
+  if (value['type'] != typeStr)
+  {
+    throw new Error(
+      `Cannot deserialize type ${value['type']} into ${typeStr}`);
+  }
+}
+
+function isString(val: any) :boolean
+{
+  return ((typeof val === 'string') || (val instanceof String));
+}
+
+function StoneCheckSerializedValueTypeGeneric(value: any)
+{
+  // console.//log("+-------------------------------------------------+");
+  // console.//log("|            StoneCheckSerializedValueTypeGeneric |");
+  // console.//log("+-------------------------------------------------+");
+  // console.//log("value = ");
+  // console.//log(value);
+  if ( (!('type' in value)) || (!isString(value.type)) )
+  {
+    throw new Error(
+      "Cannot deserialize value ('type' key invalid)");
+  }
+}
+
+// end of generic methods
+
+export enum Tool {
+  LineMeasure = "LineMeasure",
+  CircleMeasure = "CircleMeasure",
+  Crop = "Crop",
+  Windowing = "Windowing",
+  Zoom = "Zoom",
+  Pan = "Pan",
+  Move = "Move",
+  Rotate = "Rotate",
+  Resize = "Resize",
+  Mask = "Mask"
+};
+
+export function Tool_FromString(strValue:string) : Tool
+{
+  if( strValue == "LineMeasure" )
+  {
+    return Tool.LineMeasure;
+  }
+  if( strValue == "CircleMeasure" )
+  {
+    return Tool.CircleMeasure;
+  }
+  if( strValue == "Crop" )
+  {
+    return Tool.Crop;
+  }
+  if( strValue == "Windowing" )
+  {
+    return Tool.Windowing;
+  }
+  if( strValue == "Zoom" )
+  {
+    return Tool.Zoom;
+  }
+  if( strValue == "Pan" )
+  {
+    return Tool.Pan;
+  }
+  if( strValue == "Move" )
+  {
+    return Tool.Move;
+  }
+  if( strValue == "Rotate" )
+  {
+    return Tool.Rotate;
+  }
+  if( strValue == "Resize" )
+  {
+    return Tool.Resize;
+  }
+  if( strValue == "Mask" )
+  {
+    return Tool.Mask;
+  }
+
+  let msg : string =  `String ${strValue} cannot be converted to Tool. Possible values are: LineMeasure, CircleMeasure, Crop, Windowing, Zoom, Pan, Move, Rotate, Resize, Mask`;
+  throw new Error(msg);
+}
+
+export function Tool_ToString(value:Tool) : string
+{
+  if( value == Tool.LineMeasure )
+  {
+    return "LineMeasure";
+  }
+  if( value == Tool.CircleMeasure )
+  {
+    return "CircleMeasure";
+  }
+  if( value == Tool.Crop )
+  {
+    return "Crop";
+  }
+  if( value == Tool.Windowing )
+  {
+    return "Windowing";
+  }
+  if( value == Tool.Zoom )
+  {
+    return "Zoom";
+  }
+  if( value == Tool.Pan )
+  {
+    return "Pan";
+  }
+  if( value == Tool.Move )
+  {
+    return "Move";
+  }
+  if( value == Tool.Rotate )
+  {
+    return "Rotate";
+  }
+  if( value == Tool.Resize )
+  {
+    return "Resize";
+  }
+  if( value == Tool.Mask )
+  {
+    return "Mask";
+  }
+
+  let msg : string = `Value ${value} cannot be converted to Tool. Possible values are: `;
+  {
+    let _LineMeasure_enumValue : string = Tool.LineMeasure; // enums are strings in stonecodegen, so this will work.
+    let msg_LineMeasure : string = `LineMeasure (${_LineMeasure_enumValue}), `;
+    msg = msg + msg_LineMeasure;
+  }
+  {
+    let _CircleMeasure_enumValue : string = Tool.CircleMeasure; // enums are strings in stonecodegen, so this will work.
+    let msg_CircleMeasure : string = `CircleMeasure (${_CircleMeasure_enumValue}), `;
+    msg = msg + msg_CircleMeasure;
+  }
+  {
+    let _Crop_enumValue : string = Tool.Crop; // enums are strings in stonecodegen, so this will work.
+    let msg_Crop : string = `Crop (${_Crop_enumValue}), `;
+    msg = msg + msg_Crop;
+  }
+  {
+    let _Windowing_enumValue : string = Tool.Windowing; // enums are strings in stonecodegen, so this will work.
+    let msg_Windowing : string = `Windowing (${_Windowing_enumValue}), `;
+    msg = msg + msg_Windowing;
+  }
+  {
+    let _Zoom_enumValue : string = Tool.Zoom; // enums are strings in stonecodegen, so this will work.
+    let msg_Zoom : string = `Zoom (${_Zoom_enumValue}), `;
+    msg = msg + msg_Zoom;
+  }
+  {
+    let _Pan_enumValue : string = Tool.Pan; // enums are strings in stonecodegen, so this will work.
+    let msg_Pan : string = `Pan (${_Pan_enumValue}), `;
+    msg = msg + msg_Pan;
+  }
+  {
+    let _Move_enumValue : string = Tool.Move; // enums are strings in stonecodegen, so this will work.
+    let msg_Move : string = `Move (${_Move_enumValue}), `;
+    msg = msg + msg_Move;
+  }
+  {
+    let _Rotate_enumValue : string = Tool.Rotate; // enums are strings in stonecodegen, so this will work.
+    let msg_Rotate : string = `Rotate (${_Rotate_enumValue}), `;
+    msg = msg + msg_Rotate;
+  }
+  {
+    let _Resize_enumValue : string = Tool.Resize; // enums are strings in stonecodegen, so this will work.
+    let msg_Resize : string = `Resize (${_Resize_enumValue}), `;
+    msg = msg + msg_Resize;
+  }
+  {
+    let _Mask_enumValue : string = Tool.Mask; // enums are strings in stonecodegen, so this will work.
+    let msg_Mask : string = `Mask (${_Mask_enumValue})`;
+    msg = msg + msg_Mask;
+  }
+  throw new Error(msg);
+}
+
+export enum ActionType {
+  UndoCrop = "UndoCrop",
+  Rotate = "Rotate",
+  Invert = "Invert"
+};
+
+export function ActionType_FromString(strValue:string) : ActionType
+{
+  if( strValue == "UndoCrop" )
+  {
+    return ActionType.UndoCrop;
+  }
+  if( strValue == "Rotate" )
+  {
+    return ActionType.Rotate;
+  }
+  if( strValue == "Invert" )
+  {
+    return ActionType.Invert;
+  }
+
+  let msg : string =  `String ${strValue} cannot be converted to ActionType. Possible values are: UndoCrop, Rotate, Invert`;
+  throw new Error(msg);
+}
+
+export function ActionType_ToString(value:ActionType) : string
+{
+  if( value == ActionType.UndoCrop )
+  {
+    return "UndoCrop";
+  }
+  if( value == ActionType.Rotate )
+  {
+    return "Rotate";
+  }
+  if( value == ActionType.Invert )
+  {
+    return "Invert";
+  }
+
+  let msg : string = `Value ${value} cannot be converted to ActionType. Possible values are: `;
+  {
+    let _UndoCrop_enumValue : string = ActionType.UndoCrop; // enums are strings in stonecodegen, so this will work.
+    let msg_UndoCrop : string = `UndoCrop (${_UndoCrop_enumValue}), `;
+    msg = msg + msg_UndoCrop;
+  }
+  {
+    let _Rotate_enumValue : string = ActionType.Rotate; // enums are strings in stonecodegen, so this will work.
+    let msg_Rotate : string = `Rotate (${_Rotate_enumValue}), `;
+    msg = msg + msg_Rotate;
+  }
+  {
+    let _Invert_enumValue : string = ActionType.Invert; // enums are strings in stonecodegen, so this will work.
+    let msg_Invert : string = `Invert (${_Invert_enumValue})`;
+    msg = msg + msg_Invert;
+  }
+  throw new Error(msg);
+}
+
+
+
+export class SelectTool {
+  tool:Tool;
+
+  constructor() {
+  }
+
+  public StoneSerialize(): string {
+    let container: object = {};
+    container['type'] = 'StoneSampleCommands.SelectTool';
+    container['value'] = this;
+    return JSON.stringify(container);
+  }
+
+  public static StoneDeserialize(valueStr: string) : SelectTool
+  {
+    let value: any = JSON.parse(valueStr);
+    StoneCheckSerializedValueType(value, 'StoneSampleCommands.SelectTool');
+    let result: SelectTool = value['value'] as SelectTool;
+    return result;
+  }
+}
+export class Action {
+  type:ActionType;
+
+  constructor() {
+  }
+
+  public StoneSerialize(): string {
+    let container: object = {};
+    container['type'] = 'StoneSampleCommands.Action';
+    container['value'] = this;
+    return JSON.stringify(container);
+  }
+
+  public static StoneDeserialize(valueStr: string) : Action
+  {
+    let value: any = JSON.parse(valueStr);
+    StoneCheckSerializedValueType(value, 'StoneSampleCommands.Action');
+    let result: Action = value['value'] as Action;
+    return result;
+  }
+}
+
+export interface IHandler {
+};
+
+/** Service function for StoneDispatchToHandler */
+export function StoneDispatchJsonToHandler(
+  jsonValue: any, handler: IHandler): boolean
+{
+  StoneCheckSerializedValueTypeGeneric(jsonValue);
+  let type: string = jsonValue["type"];
+  if (type == "")
+  {
+    // this should never ever happen
+    throw new Error("Caught empty type while dispatching");
+  }
+  else
+  {
+    return false;
+  }
+}
+
+/** Takes a serialized type and passes this to the handler */
+export function StoneDispatchToHandler(
+  strValue: string, handler: IHandler): boolean
+{
+  // console.//log("+------------------------------------------------+");
+  // console.//log("|            StoneDispatchToHandler              |");
+  // console.//log("+------------------------------------------------+");
+  // console.//log("strValue = ");
+  // console.//log(strValue);
+  let jsonValue: any = JSON.parse(strValue)
+  return StoneDispatchJsonToHandler(jsonValue, handler);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/SynchronizedSeriesApplication.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,109 @@
+/**
+ * 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 "SampleInteractor.h"
+
+#include "../../../Framework/Toolbox/OrthancSeriesLoader.h"
+#include "../../../Framework/Layers/SeriesFrameRendererFactory.h"
+#include "../../../Framework/Layers/ReferenceLineFactory.h"
+#include "../../../Framework/Widgets/LayoutWidget.h"
+
+#include <Core/Logging.h>
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    class SynchronizedSeriesApplication : public SampleApplicationBase
+    {
+    private:   
+      LayeredSceneWidget* CreateSeriesWidget(BasicApplicationContext& context,
+                                             const std::string& series)
+      {
+        std::unique_ptr<ISeriesLoader> loader
+          (new OrthancSeriesLoader(context.GetWebService().GetConnection(), series));
+
+        std::unique_ptr<SampleInteractor> interactor(new SampleInteractor(*loader, false));
+
+        std::unique_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget);
+        widget->AddLayer(new SeriesFrameRendererFactory(loader.release(), false));
+        widget->SetSlice(interactor->GetCursor().GetCurrentSlice());
+        widget->SetInteractor(*interactor);
+
+        context.AddInteractor(interactor.release());
+
+        return widget.release();
+      }
+
+    public:
+      virtual void DeclareCommandLineOptions(boost::program_options::options_description& options)
+      {
+        boost::program_options::options_description generic("Sample options");
+        generic.add_options()
+          ("a", boost::program_options::value<std::string>(), 
+           "Orthanc ID of the 1st series")
+          ("b", boost::program_options::value<std::string>(), 
+           "Orthanc ID of the 2nd series")
+          ("c", boost::program_options::value<std::string>(), 
+           "Orthanc ID of the 3rd series")
+          ;
+
+        options.add(generic);    
+      }
+
+      virtual void Initialize(BasicApplicationContext& context,
+                              IStatusBar& statusBar,
+                              const boost::program_options::variables_map& parameters)
+      {
+        if (parameters.count("a") != 1 ||
+            parameters.count("b") != 1 ||
+            parameters.count("c") != 1)
+        {
+          LOG(ERROR) << "At least one of the three series IDs is missing";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+
+        std::unique_ptr<LayeredSceneWidget> a(CreateSeriesWidget(context, parameters["a"].as<std::string>()));
+        std::unique_ptr<LayeredSceneWidget> b(CreateSeriesWidget(context, parameters["b"].as<std::string>()));
+        std::unique_ptr<LayeredSceneWidget> c(CreateSeriesWidget(context, parameters["c"].as<std::string>()));
+
+        ReferenceLineFactory::Configure(*a, *b);
+        ReferenceLineFactory::Configure(*a, *c);
+        ReferenceLineFactory::Configure(*b, *c);
+
+        std::unique_ptr<LayoutWidget> layout(new LayoutWidget);
+        layout->SetPadding(5);
+        layout->AddWidget(a.release());
+
+        std::unique_ptr<LayoutWidget> layoutB(new LayoutWidget);
+        layoutB->SetVertical();
+        layoutB->SetPadding(5);
+        layoutB->AddWidget(b.release());
+        layoutB->AddWidget(c.release());
+        layout->AddWidget(layoutB.release());
+
+        context.SetCentralWidget(layout.release());        
+      }
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/TestPatternApplication.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,63 @@
+/**
+ * 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 "SampleApplicationBase.h"
+
+#include "../../../Framework/Widgets/TestCairoWidget.h"
+#include "../../../Framework/Widgets/TestWorldSceneWidget.h"
+#include "../../../Framework/Widgets/LayoutWidget.h"
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    class TestPatternApplication : public SampleApplicationBase
+    {
+    public:
+      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
+      {
+        boost::program_options::options_description generic("Sample options");
+        generic.add_options()
+          ("animate", boost::program_options::value<bool>()->default_value(true), "Animate the test pattern")
+          ;
+
+        options.add(generic);    
+      }
+
+      virtual void Initialize(IStatusBar& statusBar,
+                              const boost::program_options::variables_map& parameters)
+      {
+        using namespace OrthancStone;
+
+        std::unique_ptr<LayoutWidget> layout(new LayoutWidget);
+        layout->SetPadding(10);
+        layout->SetBackgroundCleared(true);
+        layout->AddWidget(new TestCairoWidget(parameters["animate"].as<bool>()));
+        layout->AddWidget(new TestWorldSceneWidget(parameters["animate"].as<bool>()));
+
+        context_->SetCentralWidget(layout.release());
+        context_->SetUpdateDelay(25);  // If animation, update the content each 25ms
+      }
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Web/index.html	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,23 @@
+<!doctype html>
+
+<html lang="us">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <!-- Disable pinch zoom on mobile devices -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <meta name="HandheldFriendly" content="true" />
+
+    <title>Wasm Samples</title>
+
+<body>
+    <ul>
+      <li><a href="simple-viewer/simple-viewer.html">Simple Viewer Project (you may add ?studyId=XXX in the url)</a></li>
+      <li><a href="single-frame.html?instance=XXX">Single frame application (you must replace XXX by a valid instance id in the url)</a></li>
+      <li><a href="single-frame-editor.html?instance=XXX">Single frame editor application (you must replace XXX by a valid instance id in the url)</a></li>
+      <li><a href="simple-viewer-single-file.html">Simple Viewer Single file (to be replaced by other samples)</a></li>
+    </ul>
+</body>
+
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Web/samples-styles.css	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,16 @@
+html, body {
+  width: 100%;
+  height: 100%;
+  margin: 0px;
+  border: 0;
+  overflow: hidden; /*  Disable scrollbars */
+  display: block;  /* No floating content on sides */
+  background-color: black;
+  color: white;
+  font-family: Arial, Helvetica, sans-serif;
+}
+
+canvas {
+  left:0px;
+  top:0px;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Web/simple-viewer-single-file.html	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,39 @@
+<!doctype html>
+
+<html lang="us">
+
+<head>
+  <meta charset="utf-8" />
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+  <!-- Disable pinch zoom on mobile devices -->
+  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+  <meta name="HandheldFriendly" content="true" />
+
+  <title>Simple Viewer</title>
+  <link href="samples-styles.css" rel="stylesheet" />
+
+<body>
+  <div id="breadcrumb">
+    <span id="patient-id"></span>
+    <span id="study-description"></span>
+    <span id="series-description"></span>
+  </div>
+  <div style="height: calc(100% - 50px)">
+    <div style="width: 20%; height: 100%; display: inline-block">
+      <canvas id="canvas"></canvas>
+    </div>
+    <div style="width: 70%; height: 100%; display: inline-block">
+      <canvas id="canvas2"></canvas>
+    </div>
+  </div>
+  <div id="toolbox" style="height: 50px">
+    <input tool-selector="line-measure" type="radio" name="radio-tool-selector" class="tool-selector">line
+    <input tool-selector="circle-measure" type="radio" name="radio-tool-selector" class="tool-selector">circle
+    <button action-trigger="action1" class="action-trigger">action1</button>
+    <button action-trigger="action2" class="action-trigger">action2</button>
+  </div>
+  <script type="text/javascript" src="app-simple-viewer-single-file.js"></script>
+</body>
+
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Web/simple-viewer-single-file.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,61 @@
+import wasmApplicationRunner = require('../../../Platforms/Wasm/wasm-application-runner');
+
+wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSimpleViewerSingleFile", "/orthanc");
+
+function SelectTool(toolName: string) {
+    var command = {
+        command: "selectTool",
+        args: {
+            toolName: toolName
+        }
+    };
+    wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command));
+
+}
+
+function PerformAction(commandName: string) {
+    var command = {
+        command: commandName,
+        commandType: "simple",
+        args: {}
+    };
+    wasmApplicationRunner.SendSerializedMessageToStoneApplication(JSON.stringify(command));
+}
+
+//initializes the buttons
+//-----------------------
+// install "SelectTool" handlers
+document.querySelectorAll("[tool-selector]").forEach((e) => {
+    console.log(e);
+    (e as HTMLInputElement).addEventListener("click", () => {
+        console.log(e);
+        SelectTool(e.attributes["tool-selector"].value);
+    });
+});
+
+// install "PerformAction" handlers
+document.querySelectorAll("[action-trigger]").forEach((e) => {
+    (e as HTMLInputElement).addEventListener("click", () => {
+        PerformAction(e.attributes["action-trigger"].value);
+    });
+});
+
+// this method is called "from the C++ code" when the StoneApplication is updated.
+// it can be used to update the UI of the application
+function UpdateWebApplicationWithString(statusUpdateMessage: string) {
+  console.log(statusUpdateMessage);
+  
+  if (statusUpdateMessage.startsWith("series-description=")) {
+      document.getElementById("series-description").innerText = statusUpdateMessage.split("=")[1];
+  }
+}
+
+function UpdateWebApplicationWithSerializedMessage(statusUpdateMessageString: string) {
+  console.log("updating web application with serialized message: ", statusUpdateMessageString);
+  console.log("<not supported in the simple viewer (single file)!>");
+}
+
+// make it available to other js scripts in the application
+(<any> window).UpdateWebApplicationWithString = UpdateWebApplicationWithString;
+
+(<any> window).UpdateWebApplicationWithSerializedMessage = UpdateWebApplicationWithSerializedMessage;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Web/simple-viewer-single-file.tsconfig.json	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,9 @@
+{
+    "extends" : "./tsconfig-samples",
+    "compilerOptions": {
+        // "outFile": "../build-web/app-simple-viewer-single-file.js"
+    },
+    "include" : [
+        "simple-viewer-single-file.ts"
+    ]
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Web/single-frame-editor.html	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,22 @@
+<!doctype html>
+
+<html lang="us">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <!-- Disable pinch zoom on mobile devices -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <meta name="HandheldFriendly" content="true" />
+
+    <title>Simple Viewer</title>
+    <link href="samples-styles.css" rel="stylesheet" />
+
+<body>
+  <div style="width: 100%; height: 100%">
+    <canvas id="canvas"></canvas>
+  </div>
+  <script type="text/javascript" src="app-single-frame-editor.js"></script>
+</body>
+
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Web/single-frame-editor.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,3 @@
+import wasmApplicationRunner = require('../../../Platforms/Wasm/wasm-application-runner');
+
+wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSingleFrameEditor", "/orthanc");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Web/single-frame-editor.tsconfig.json	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,8 @@
+{
+    "extends" : "./tsconfig-samples",
+    "compilerOptions": {
+    },
+    "include" : [
+        "single-frame-editor.ts"
+    ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Web/single-frame.html	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,22 @@
+<!doctype html>
+
+<html lang="us">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <!-- Disable pinch zoom on mobile devices -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <meta name="HandheldFriendly" content="true" />
+
+    <title>Simple Viewer</title>
+    <link href="samples-styles.css" rel="stylesheet" />
+
+<body>
+  <div style="width: 100%; height: 100%">
+    <canvas id="canvas"></canvas>
+  </div>
+  <script type="text/javascript" src="app-single-frame.js"></script>
+</body>
+
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Web/single-frame.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,4 @@
+import wasmApplicationRunner = require('../../../Platforms/Wasm/wasm-application-runner');
+
+wasmApplicationRunner.InitializeWasmApplication("OrthancStoneSingleFrame", "/orthanc");
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Web/single-frame.tsconfig.json	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,8 @@
+{
+    "extends" : "./tsconfig-samples",
+    "compilerOptions": {
+    },
+    "include" : [
+        "single-frame.ts"
+    ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/Web/tsconfig-samples.json	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,11 @@
+{
+    "extends" : "../../../Platforms/Wasm/tsconfig-stone",
+    "compilerOptions": {
+        "sourceMap": false,
+        "lib" : [
+            "es2017",
+            "dom",
+            "dom.iterable"
+        ]
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/build-wasm.sh	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# usage:
+# to build all targets in Debug:
+# ./build-wasm.sh
+#
+# to build a single target in release:
+# ./build-wasm.sh OrthancStoneSingleFrameEditor Release
+
+set -e
+
+target=${1:-all}
+buildType=${2:-Debug}
+
+currentDir=$(pwd)
+samplesRootDir=$(pwd)
+
+mkdir -p $samplesRootDir/build-wasm
+cd $samplesRootDir/build-wasm
+
+source ~/apps/emsdk/emsdk_env.sh
+cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=~/apps/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=$buildType -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc -DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON
+ninja $target
+
+echo "-- building the web application -- "
+cd $currentDir
+./build-web.sh
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/build-wasm.sh.old	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# usage:
+# to build all targets:
+# ./build-wasm.sh
+#
+# to build a single target:
+# ./build-wasm.sh OrthancStoneSingleFrameEditor
+
+set -e
+
+target=${1:-all}
+
+currentDir=$(pwd)
+samplesRootDir=$(pwd)
+
+mkdir -p $samplesRootDir/build-wasm
+cd $samplesRootDir/build-wasm
+
+source ~/apps/emsdk/emsdk_env.sh
+cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake \
+  -DCMAKE_BUILD_TYPE=Release \
+  -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone \
+  -DORTHANC_FRAMEWORK_SOURCE=path \
+  -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc \
+  -DALLOW_DOWNLOADS=ON .. \
+  -DENABLE_WASM=ON
+
+ninja $target
+
+echo "-- building the web application -- "
+cd $currentDir
+./build-web.sh
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/build-web-ext.sh	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+set -e
+
+target=${1:-all}
+# this script currently assumes that the wasm code has been built on its side and is availabie in build-wasm/
+
+currentDir=$(pwd)
+
+scriptDirRel=$(dirname $0)
+#echo $scriptDirRel
+scriptDirAbs=$(realpath $scriptDirRel)
+echo $scriptDirAbs
+
+samplesRootDir=scriptDirAbs
+
+outputDir=$samplesRootDir/build-web/
+mkdir -p $outputDir
+
+# files used by all single files samples
+cp $samplesRootDir/Web/index.html $outputDir
+cp $samplesRootDir/Web/samples-styles.css $outputDir
+
+# build simple-viewer-single-file (obsolete project)
+if [[ $target == "all" || $target == "OrthancStoneSimpleViewerSingleFile" ]]; then
+  cp $samplesRootDir/Web/simple-viewer-single-file.html $outputDir
+  tsc --allowJs --project $samplesRootDir/Web/simple-viewer-single-file.tsconfig.json
+  cp $currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.js  $outputDir
+  cp $currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.wasm  $outputDir
+fi
+
+# build single-frame
+if [[ $target == "all" || $target == "OrthancStoneSingleFrame" ]]; then
+  cp $samplesRootDir/Web/single-frame.html $outputDir
+  tsc --allowJs --project $samplesRootDir/Web/single-frame.tsconfig.json
+  cp $currentDir/build-wasm/OrthancStoneSingleFrame.js  $outputDir
+  cp $currentDir/build-wasm/OrthancStoneSingleFrame.wasm  $outputDir
+fi
+
+# build single-frame-editor
+if [[ $target == "all" || $target == "OrthancStoneSingleFrameEditor" ]]; then
+  cp $samplesRootDir/Web/single-frame-editor.html $outputDir
+  tsc --allowJs --project $samplesRootDir/Web/single-frame-editor.tsconfig.json
+  cp $currentDir/build-wasm/OrthancStoneSingleFrameEditor.js  $outputDir
+  cp $currentDir/build-wasm/OrthancStoneSingleFrameEditor.wasm  $outputDir
+fi
+
+# build simple-viewer project
+if [[ $target == "all" || $target == "OrthancStoneSimpleViewer" ]]; then
+  mkdir -p $outputDir/simple-viewer/
+  cp $samplesRootDir/SimpleViewer/Wasm/simple-viewer.html $outputDir/simple-viewer/
+  cp $samplesRootDir/SimpleViewer/Wasm/styles.css $outputDir/simple-viewer/
+  tsc --allowJs --project $samplesRootDir/SimpleViewer/Wasm/tsconfig-simple-viewer.json
+  cp $currentDir/build-wasm/OrthancStoneSimpleViewer.js  $outputDir/simple-viewer/
+  cp $currentDir/build-wasm/OrthancStoneSimpleViewer.wasm  $outputDir/simple-viewer/
+fi
+
+cd $currentDir
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/build-web.sh	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+set -e
+
+target=${1:-all}
+# this script currently assumes that the wasm code has been built on its side and is availabie in build-wasm/
+
+currentDir=$(pwd)
+samplesRootDir=$(pwd)
+
+echo "*************************************************************************"
+echo "samplesRootDir = $samplesRootDir"
+echo "*************************************************************************"
+
+outputDir=$samplesRootDir/build-web/
+mkdir -p "$outputDir"
+
+# files used by all single files samples
+cp "$samplesRootDir/Web/index.html" "$outputDir"
+cp "$samplesRootDir/Web/samples-styles.css" "$outputDir"
+
+# # build simple-viewer-single-file (obsolete project)
+# if [[ $target == "all" || $target == "OrthancStoneSimpleViewerSingleFile" ]]; then
+#   cp $samplesRootDir/Web/simple-viewer-single-file.html $outputDir
+#   tsc --project $samplesRootDir/Web/simple-viewer-single-file.tsconfig.json --outDir "$outputDir"
+#   browserify \
+#       "$outputDir/Platforms/Wasm/wasm-application-runner.js" \
+#       "$outputDir/Applications/Samples/Web/simple-viewer-single-file.js" \
+#       -o "$outputDir/app-simple-viewer-single-file.js"
+#   cp "$currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.js"  $outputDir
+#   cp "$currentDir/build-wasm/OrthancStoneSimpleViewerSingleFile.wasm"  $outputDir
+# fi
+
+# # build single-frame
+# if [[ $target == "all" || $target == "OrthancStoneSingleFrame" ]]; then
+#   cp $samplesRootDir/Web/single-frame.html $outputDir
+#   tsc --project $samplesRootDir/Web/single-frame.tsconfig.json --outDir "$outputDir"
+#   browserify \
+#       "$outputDir/Platforms/Wasm/wasm-application-runner.js" \
+#       "$outputDir/Applications/Samples/Web/single-frame.js" \
+#       -o "$outputDir/app-single-frame.js"
+#   cp "$currentDir/build-wasm/OrthancStoneSingleFrame.js"  $outputDir
+#   cp "$currentDir/build-wasm/OrthancStoneSingleFrame.wasm"  $outputDir
+# fi
+
+# build single-frame-editor
+if [[ $target == "all" || $target == "OrthancStoneSingleFrameEditor" ]]; then
+  cp $samplesRootDir/Web/single-frame-editor.html $outputDir
+  tsc --project $samplesRootDir/Web/single-frame-editor.tsconfig.json --outDir "$outputDir"
+  browserify \
+      "$outputDir/Platforms/Wasm/wasm-application-runner.js" \
+      "$outputDir/Applications/Samples/Web/single-frame-editor.js" \
+      -o "$outputDir/app-single-frame-editor.js"
+  cp "$currentDir/build-wasm/OrthancStoneSingleFrameEditor.js"  $outputDir
+  cp "$currentDir/build-wasm/OrthancStoneSingleFrameEditor.wasm"  $outputDir
+fi
+
+# build simple-viewer project
+if [[ $target == "all" || $target == "OrthancStoneSimpleViewer" ]]; then
+  mkdir -p $outputDir/simple-viewer/
+  cp $samplesRootDir/SimpleViewer/Wasm/simple-viewer.html $outputDir/simple-viewer/
+  cp $samplesRootDir/SimpleViewer/Wasm/styles.css $outputDir/simple-viewer/
+  
+  # the root dir must contain all the source files for the whole project
+  tsc --module commonjs --allowJs --project "$samplesRootDir/SimpleViewer/Wasm/tsconfig-simple-viewer.json" --rootDir "$samplesRootDir/../.." --outDir "$outputDir/simple-viewer/"
+  browserify \
+    "$outputDir/simple-viewer/Platforms/Wasm/wasm-application-runner.js" \
+    "$outputDir/simple-viewer/Applications/Samples/SimpleViewer/Wasm/simple-viewer.js" \
+    -o "$outputDir/simple-viewer/app-simple-viewer.js"
+  cp "$currentDir/build-wasm/OrthancStoneSimpleViewer.js"  "$outputDir/simple-viewer/"
+  cp "$currentDir/build-wasm/OrthancStoneSimpleViewer.wasm"  "$outputDir/simple-viewer/"
+fi
+
+cd $currentDir
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/get-requirements-windows.ps1	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,50 @@
+
+if ($true) {
+
+    Write-Error "This script is obsolete. Please work under WSL and run build-wasm.sh"
+
+} else {
+
+    param(
+        [IO.DirectoryInfo] $EmsdkRootDir = "C:\Emscripten",
+        [bool] $Overwrite = $false
+      )
+    
+    if (Test-Path -Path $EmsdkRootDir) {
+        if( $Override) {
+            Remove-Item -Path $EmsdkRootDir -Force -Recurse
+        } else {
+            throw "The `"$EmsdkRootDir`" folder may not exist! Use the Overwrite flag to bypass this check."
+        }
+    }
+    
+    # TODO: detect whether git is installed
+    # choco install -y git
+    
+    Write-Host "Will retrieve the Emscripten SDK to the `"$EmsdkRootDir`" folder"
+    
+    $EmsdkParentDir = split-path -Parent $EmsdkRootDir
+    $EmsdkRootName = split-path -Leaf $EmsdkRootDir
+    
+    Push-Location $EmsdkParentDir
+    
+    git clone https://github.com/juj/emsdk.git $EmsdkRootName
+    cd $EmsdkRootName
+    
+    git pull
+    
+    ./emsdk install latest
+    
+    ./emsdk activate latest
+    
+    echo "INFO: the ~/.emscripten file has been configured for this installation of Emscripten."
+    
+    Write-Host "emsdk is now installed in $EmsdkRootDir"
+    
+    Pop-Location
+
+}
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/nginx.local.conf	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,44 @@
+# Local config to serve the WASM samples static files and reverse proxy Orthanc.
+# Uses port 9977 instead of 80.
+
+# `events` section is mandatory
+events {
+  worker_connections 1024; # Default: 1024
+}
+
+http {
+
+  # prevent nginx sync issues on OSX
+  proxy_buffering off;
+
+  server {
+    listen 9977 default_server;
+    client_max_body_size 4G;
+
+    # location may have to be adjusted depending on your OS and nginx install
+    include /etc/nginx/mime.types;
+    # if not in your system mime.types, add this line to support WASM:
+    # types {
+    #    application/wasm                      wasm; 
+    # }
+
+    # serve WASM static files
+    root build-web/;
+    location / {
+	}
+
+    # reverse proxy orthanc
+	location /orthanc/ {
+		rewrite /orthanc(.*) $1 break;
+		proxy_pass http://127.0.0.1:8042;
+		proxy_set_header Host $http_host;
+		proxy_set_header my-auth-header good-token;
+		proxy_request_buffering off;
+		proxy_max_temp_file_size 0;
+		client_max_body_size 0;
+	}
+
+
+  }
+  
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/package-lock.json	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,11 @@
+{
+  "requires": true,
+  "lockfileVersion": 1,
+  "dependencies": {
+    "typescript": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz",
+      "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg=="
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/CMakeLists.txt	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,142 @@
+cmake_minimum_required(VERSION 2.8.3)
+project(RtViewerDemo)
+
+if(MSVC)
+  add_definitions(/MP)
+  if (CMAKE_BUILD_TYPE MATCHES DEBUG)
+    add_definitions(/JMC)
+  endif()
+endif()
+
+message("-------------------------------------------------------------------------------------------------------------------")
+message("ORTHANC_FRAMEWORK_ROOT is set to ${ORTHANC_FRAMEWORK_ROOT}")
+message("-------------------------------------------------------------------------------------------------------------------")
+
+if(NOT DEFINED ORTHANC_FRAMEWORK_ROOT)
+  message(FATAL_ERROR "The location of the Orthanc source repository must be set in the ORTHANC_FRAMEWORK_ROOT CMake variable")
+endif()
+
+message("-------------------------------------------------------------------------------------------------------------------")
+message("STONE_SOURCES_DIR is set to ${STONE_SOURCES_DIR}")
+message("-------------------------------------------------------------------------------------------------------------------")
+
+if(NOT DEFINED STONE_SOURCES_DIR)
+  message(FATAL_ERROR "The location of the Stone of Orthanc source repository must be set in the STONE_SOURCES_DIR CMake variable")
+endif()
+
+include(${STONE_SOURCES_DIR}/Resources/CMake/OrthancStoneParameters.cmake)
+
+if (OPENSSL_NO_CAPIENG)
+add_definitions(-DOPENSSL_NO_CAPIENG=1)
+endif()
+
+set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application")
+set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application")
+set(ENABLE_WASM OFF CACHE BOOL "Target WASM application")
+
+if (ENABLE_WASM)
+  #####################################################################
+  ## Configuration of the Emscripten compiler for WebAssembly target
+  #####################################################################
+
+  set(WASM_FLAGS "-s WASM=1")
+  set(WASM_FLAGS "${WASM_FLAGS} -s STRICT=1") # drops support for all deprecated build options
+  set(WASM_FLAGS "${WASM_FLAGS} -s FILESYSTEM=1") # if we don't include it, gen_uuid.c fails to build because srand, getpid(), ... are not defined
+  set(WASM_FLAGS "${WASM_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") # actually enable exception catching 
+  set(WASM_FLAGS "${WASM_FLAGS} -s ERROR_ON_MISSING_LIBRARIES=1")
+
+  if (CMAKE_BUILD_TYPE MATCHES DEBUG)
+    set(WASM_FLAGS "${WASM_FLAGS} -g4") # generate debug information
+    set(WASM_FLAGS "${WASM_FLAGS} -s ASSERTIONS=2") # more runtime checks
+  else()
+    set(WASM_FLAGS "${WASM_FLAGS} -Os") # optimize for web (speed and size)
+  endif()
+
+  set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module")
+
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}")
+
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${WASM_FLAGS}")  # not always clear which flags are for the compiler and which one are for the linker -> pass them all to the linker too
+  # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Applications/Samples/samples-library.js")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"'")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_STACK=128000000")
+
+  add_definitions(-DORTHANC_ENABLE_WASM=1)
+  set(ORTHANC_SANDBOXED ON)
+
+elseif (ENABLE_QT OR ENABLE_SDL)
+
+  set(ENABLE_NATIVE ON)
+  set(ORTHANC_SANDBOXED OFF)
+  set(ENABLE_CRYPTO_OPTIONS ON)
+  set(ENABLE_GOOGLE_TEST ON)
+  set(ENABLE_WEB_CLIENT ON)
+
+endif()
+
+
+#####################################################################
+## Configuration for Orthanc
+#####################################################################
+
+if (ORTHANC_STONE_VERSION STREQUAL "mainline")
+  set(ORTHANC_FRAMEWORK_VERSION "mainline")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
+else()
+  set(ORTHANC_FRAMEWORK_VERSION "1.4.1")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
+endif()
+
+set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")")
+set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"")
+set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
+
+add_definitions(
+  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
+  )
+
+
+#####################################################################
+## Build a static library containing the Orthanc Stone framework
+#####################################################################
+
+LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
+
+include(${STONE_SOURCES_DIR}/Resources/CMake/OrthancStoneConfiguration.cmake)
+
+add_library(OrthancStone STATIC
+  ${ORTHANC_STONE_SOURCES}
+  )
+
+#####################################################################
+## Build all the sample applications
+#####################################################################
+
+include_directories(${ORTHANC_STONE_ROOT})
+
+list(APPEND RTVIEWERDEMO_APPLICATION_SOURCES
+  ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleInteractor.h
+  ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleApplicationBase.h
+  )
+
+if (ENABLE_WASM)
+  list(APPEND RTVIEWERDEMO_APPLICATION_SOURCES
+    ${STONE_WASM_SOURCES}
+    )
+endif()
+
+add_executable(RtViewerDemo
+  main.cpp
+  ${RTVIEWERDEMO_APPLICATION_SOURCES}
+)
+set_target_properties(RtViewerDemo PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=3)
+target_include_directories(RtViewerDemo PRIVATE ${ORTHANC_STONE_ROOT})
+target_link_libraries(RtViewerDemo OrthancStone)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/build-sdl-msvc15.ps1	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,24 @@
+if (-not (Test-Path "build-sdl-msvc15")) {
+  mkdir -p "build-sdl-msvc15"
+}
+
+cd build-sdl-msvc15
+
+cmake -G "Visual Studio 15 2017 Win64" -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DSTONE_SOURCES_DIR="$($pwd)\..\..\..\.." -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\..\..\..\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON .. 
+
+if (!$?) {
+	Write-Error 'cmake configuration failed' -ErrorAction Stop
+}
+
+cmake --build . --target RtViewerDemo --config Debug
+
+if (!$?) {
+	Write-Error 'cmake build failed' -ErrorAction Stop
+}
+
+cd Debug
+
+.\RtViewerDemo.exe --ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa --dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb --struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/build-wasm.sh	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# usage:
+# build-wasm BUILD_TYPE
+# where BUILD_TYPE is Debug, RelWithDebInfo or Release
+
+set -e
+
+buildType=${1:-Debug}
+
+currentDir=$(pwd)
+currentDirAbs=$(realpath $currentDir)
+
+mkdir -p build-wasm
+cd build-wasm
+
+source ~/apps/emsdk/emsdk_env.sh
+cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake \
+-DCMAKE_BUILD_TYPE=$buildType -DSTONE_SOURCES_DIR=$currentDirAbs/../../../../orthanc-stone \
+-DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDirAbs/../../../../orthanc \
+-DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON
+
+ninja $target
+
+echo "-- building the web application -- "
+cd $currentDir
+./build-web.sh
+
+echo "Launch start-serving-files.sh to access the web sample application locally"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/build-web.sh	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+set -e
+
+target=${1:-all}
+# this script currently assumes that the wasm code has been built on its side and is availabie in build-wasm/
+
+currentDir=$(pwd)
+samplesRootDir=$(pwd)
+
+tscOutput=$samplesRootDir/build-tsc-output/
+outputDir=$samplesRootDir/build-web/
+mkdir -p "$outputDir"
+
+# files used by all single files samples
+cp "$samplesRootDir/index.html" "$outputDir"
+cp "$samplesRootDir/samples-styles.css" "$outputDir"
+
+# build rt-viewer-demo
+cp $samplesRootDir/rt-viewer-demo.html $outputDir
+tsc --project $samplesRootDir/rt-viewer-demo.tsconfig.json --outDir "$tscOutput"
+browserify \
+    "$tscOutput/orthanc-stone/Platforms/Wasm/logger.js" \
+    "$tscOutput/orthanc-stone/Platforms/Wasm/stone-framework-loader.js" \
+    "$tscOutput/orthanc-stone/Platforms/Wasm/wasm-application-runner.js" \
+    "$tscOutput/orthanc-stone/Platforms/Wasm/wasm-viewport.js" \
+    "$tscOutput/rt-viewer-sample/rt-viewer-demo.js" \
+    -o "$outputDir/app-rt-viewer-demo.js"
+cp "$currentDir/build-wasm/RtViewerDemo.js"  $outputDir
+cp "$currentDir/build-wasm/RtViewerDemo.wasm"  $outputDir
+
+cd $currentDir
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/index.html	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,20 @@
+<!doctype html>
+
+<html lang="us">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <!-- Disable pinch zoom on mobile devices -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <meta name="HandheldFriendly" content="true" />
+
+    <title>Wasm Samples</title>
+
+<body>
+    <ul>
+      <li><a href="rt-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa&dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb&struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9">RTSTRUCT + CT + RTDOSE viewer demo. Pplease replace the url arguments with suitable IDs (you can find those in the Orthanc Explorer, for instance)</a></li>
+    </ul>
+</body>
+
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/main.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,893 @@
+/**
+ * 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/>.
+ **/
+
+#include "Applications/IStoneApplication.h"
+#include "Framework/Widgets/WorldSceneWidget.h"
+#include "Framework/Widgets/LayoutWidget.h"
+
+#if ORTHANC_ENABLE_WASM==1
+  #include "Platforms/Wasm/WasmPlatformApplicationAdapter.h"
+  #include "Platforms/Wasm/Defaults.h"
+  #include "Platforms/Wasm/WasmViewport.h"
+#endif
+
+#if ORTHANC_ENABLE_QT==1
+  #include "Qt/SampleMainWindow.h"
+  #include "Qt/SampleMainWindowWithButtons.h"
+#endif
+
+#include "Framework/Layers/DicomSeriesVolumeSlicer.h"
+#include "Framework/Widgets/SliceViewerWidget.h"
+#include "Framework/Volumes/StructureSetLoader.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <Core/Images/ImageTraits.h>
+
+#include <boost/math/constants/constants.hpp>
+#include "Framework/dev.h"
+#include "Framework/Widgets/LayoutWidget.h"
+#include "Framework/Layers/DicomStructureSetSlicer.h"
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    class RtViewerDemoBaseApplication : public IStoneApplication
+    {
+    protected:
+      // ownership is transferred to the application context
+#ifndef RESTORE_NON_RTVIEWERDEMO_BEHAVIOR
+      LayoutWidget*          mainWidget_;
+#else
+      WorldSceneWidget*  mainWidget_;
+#endif
+
+    public:
+      virtual void Initialize(StoneApplicationContext* context,
+                              IStatusBar& statusBar,
+                              const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE
+      {
+      }
+
+      virtual std::string GetTitle() const ORTHANC_OVERRIDE
+      {
+        return "Stone of Orthanc - Sample";
+      }
+
+      /**
+       * In the basic samples, the commands are handled by the platform adapter and NOT
+       * by the application handler
+      */
+      virtual void HandleSerializedMessage(const char* data) ORTHANC_OVERRIDE {};
+
+
+      virtual void Finalize() ORTHANC_OVERRIDE {}
+      virtual IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainWidget_;}
+
+#if ORTHANC_ENABLE_WASM==1
+      // default implementations for a single canvas named "canvas" in the HTML and an empty WasmApplicationAdapter
+
+      virtual void InitializeWasm() ORTHANC_OVERRIDE
+      {
+        AttachWidgetToWasmViewport("canvas", mainWidget_);
+      }
+
+      virtual WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(MessageBroker& broker)
+      {
+        return new WasmPlatformApplicationAdapter(broker, *this);
+      }
+#endif
+
+    };
+
+    // this application actually works in Qt and WASM
+    class RtViewerDemoBaseSingleCanvasWithButtonsApplication : public RtViewerDemoBaseApplication
+    {
+public:
+      virtual void OnPushButton1Clicked() {}
+      virtual void OnPushButton2Clicked() {}
+      virtual void OnTool1Clicked() {}
+      virtual void OnTool2Clicked() {}
+
+      virtual void GetButtonNames(std::string& pushButton1,
+                                  std::string& pushButton2,
+                                  std::string& tool1,
+                                  std::string& tool2
+                                  ) {
+        pushButton1 = "action1";
+        pushButton2 = "action2";
+        tool1 = "tool1";
+        tool2 = "tool2";
+      }
+
+#if ORTHANC_ENABLE_QT==1
+      virtual QStoneMainWindow* CreateQtMainWindow() {
+        return new SampleMainWindowWithButtons(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this);
+      }
+#endif
+
+    };
+
+    // this application actually works in SDL and WASM
+    class RtViewerDemoBaseApplicationSingleCanvas : public RtViewerDemoBaseApplication
+    {
+public:
+
+#if ORTHANC_ENABLE_QT==1
+      virtual QStoneMainWindow* CreateQtMainWindow() {
+        return new SampleMainWindow(dynamic_cast<OrthancStone::NativeStoneApplicationContext&>(*context_), *this);
+      }
+#endif
+    };
+  }
+}
+
+
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    template <Orthanc::PixelFormat T>
+    void ReadDistributionInternal(std::vector<float>& distribution,
+                                  const Orthanc::ImageAccessor& image)
+    {
+      const unsigned int width = image.GetWidth();
+      const unsigned int height = image.GetHeight();
+      
+      distribution.resize(width * height);
+      size_t pos = 0;
+
+      for (unsigned int y = 0; y < height; y++)
+      {
+        for (unsigned int x = 0; x < width; x++, pos++)
+        {
+          distribution[pos] = Orthanc::ImageTraits<T>::GetFloatPixel(image, x, y);
+        }
+      }
+    }
+
+    void ReadDistribution(std::vector<float>& distribution,
+                          const Orthanc::ImageAccessor& image)
+    {
+      switch (image.GetFormat())
+      {
+        case Orthanc::PixelFormat_Grayscale8:
+          ReadDistributionInternal<Orthanc::PixelFormat_Grayscale8>(distribution, image);
+          break;
+
+        case Orthanc::PixelFormat_Grayscale16:
+          ReadDistributionInternal<Orthanc::PixelFormat_Grayscale16>(distribution, image);
+          break;
+
+        case Orthanc::PixelFormat_SignedGrayscale16:
+          ReadDistributionInternal<Orthanc::PixelFormat_SignedGrayscale16>(distribution, image);
+          break;
+
+        case Orthanc::PixelFormat_Grayscale32:
+          ReadDistributionInternal<Orthanc::PixelFormat_Grayscale32>(distribution, image);
+          break;
+
+        case Orthanc::PixelFormat_Grayscale64:
+          ReadDistributionInternal<Orthanc::PixelFormat_Grayscale64>(distribution, image);
+          break;
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+    }
+
+
+    class DoseInteractor : public VolumeImageInteractor
+    {
+    private:
+      SliceViewerWidget&         widget_;
+      size_t               layer_;
+      DicomFrameConverter  converter_;
+
+
+      
+    protected:
+      virtual void NotifySliceChange(const ISlicedVolume& slicedVolume,
+                                    const size_t& sliceIndex,
+                                    const Slice& slice)
+      {
+        converter_ = slice.GetConverter();
+        
+  #if 0
+        const OrthancVolumeImage& volume = dynamic_cast<const OrthancVolumeImage&>(slicedVolume);
+
+        RenderStyle s = widget_.GetLayerStyle(layer_);
+
+        if (volume.FitWindowingToRange(s, slice.GetConverter()))
+        {
+          printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_);
+          widget_.SetLayerStyle(layer_, s);
+        }
+  #endif
+      }
+
+      virtual void NotifyVolumeReady(const ISlicedVolume& slicedVolume)
+      {
+        const float percentile = 0.01f;
+        const OrthancVolumeImage& volume = dynamic_cast<const OrthancVolumeImage&>(slicedVolume);
+
+        std::vector<float> distribution;
+        ReadDistribution(distribution, volume.GetImage().GetInternalImage());
+        std::sort(distribution.begin(), distribution.end());
+
+        int start = static_cast<int>(std::ceil(distribution.size() * percentile));
+        int end = static_cast<int>(std::floor(distribution.size() * (1.0f - percentile)));
+
+        float a = 0;
+        float b = 0;
+        
+        if (start < end &&
+            start >= 0 &&
+            end < static_cast<int>(distribution.size()))
+        {
+          a = distribution[start];
+          b = distribution[end];
+        }
+        else if (!distribution.empty())
+        {
+          // Too small distribution: Use full range
+          a = distribution.front();
+          b = distribution.back();
+        }
+
+        //printf("%f %f\n", a, b);
+
+        RenderStyle s = widget_.GetLayerStyle(layer_);
+        s.windowing_ = ImageWindowing_Custom;
+        s.customWindowCenter_ = static_cast<float>(converter_.Apply((a + b) / 2.0f));
+        s.customWindowWidth_ = static_cast<float>(converter_.Apply(b - a));
+        
+        // 96.210556 => 192.421112
+        widget_.SetLayerStyle(layer_, s);
+        printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_);      
+      }
+
+    public:
+      DoseInteractor(MessageBroker& broker, OrthancVolumeImage& volume,
+                    SliceViewerWidget& widget,
+                    VolumeProjection projection,
+                    size_t layer) :
+        VolumeImageInteractor(broker, volume, widget, projection),
+        widget_(widget),
+        layer_(layer)
+      {
+      }
+    };
+
+    class RtViewerDemoApplication :
+      public RtViewerDemoBaseApplicationSingleCanvas,
+      public IObserver
+    {
+    public:
+      std::vector<std::pair<SliceViewerWidget*, size_t> > doseCtWidgetLayerPairs_;
+      std::list<OrthancStone::IWorldSceneInteractor*>    interactors_;
+
+      class Interactor : public IWorldSceneInteractor
+      {
+      private:
+        RtViewerDemoApplication&  application_;
+
+      public:
+        Interactor(RtViewerDemoApplication&  application) :
+          application_(application)
+        {
+        }
+
+        virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
+          const ViewportGeometry& view,
+          MouseButton button,
+          KeyboardModifiers modifiers,
+          int viewportX,
+          int viewportY,
+          double x,
+          double y,
+          IStatusBar* statusBar,
+          const std::vector<Touch>& displayTouches)
+        {
+          return NULL;
+        }
+
+        virtual void MouseOver(CairoContext& context,
+          WorldSceneWidget& widget,
+          const ViewportGeometry& view,
+          double x,
+          double y,
+          IStatusBar* statusBar)
+        {
+          if (statusBar != NULL)
+          {
+            Vector p = dynamic_cast<SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
+
+            char buf[64];
+            sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)",
+              p[0] / 10.0, p[1] / 10.0, p[2] / 10.0);
+            statusBar->SetMessage(buf);
+          }
+        }
+
+        virtual void MouseWheel(WorldSceneWidget& widget,
+          MouseWheelDirection direction,
+          KeyboardModifiers modifiers,
+          IStatusBar* statusBar)
+        {
+          int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1);
+
+          switch (direction)
+          {
+          case MouseWheelDirection_Up:
+            application_.OffsetSlice(-scale);
+            break;
+
+          case MouseWheelDirection_Down:
+            application_.OffsetSlice(scale);
+            break;
+
+          default:
+            break;
+          }
+        }
+
+        virtual void KeyPressed(WorldSceneWidget& widget,
+          KeyboardKeys key,
+          char keyChar,
+          KeyboardModifiers modifiers,
+          IStatusBar* statusBar)
+        {
+          switch (keyChar)
+          {
+          case 's':
+            // TODO: recursively traverse children
+            widget.FitContent();
+            break;
+
+          default:
+            break;
+          }
+        }
+      };
+
+      void OffsetSlice(int offset)
+      {
+        if (source_ != NULL)
+        {
+          int slice = static_cast<int>(slice_) + offset;
+
+          if (slice < 0)
+          {
+            slice = 0;
+          }
+
+          if (slice >= static_cast<int>(source_->GetSliceCount()))
+          {
+            slice = static_cast<int>(source_->GetSliceCount()) - 1;
+          }
+
+          if (slice != static_cast<int>(slice_))
+          {
+            SetSlice(slice);
+          }
+        }
+      }
+
+
+      SliceViewerWidget& GetMainWidget()
+      {
+        return *dynamic_cast<SliceViewerWidget*>(mainWidget_);
+      }
+
+
+      void SetSlice(size_t index)
+      {
+        if (source_ != NULL &&
+          index < source_->GetSliceCount())
+        {
+          slice_ = static_cast<unsigned int>(index);
+
+#if 1
+          GetMainWidget().SetSlice(source_->GetSlice(slice_).GetGeometry());
+#else
+          // TEST for scene extents - Rotate the axes
+          double a = 15.0 / 180.0 * boost::math::constants::pi<double>();
+
+#if 1
+          Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0);
+          Vector y; GeometryToolbox::AssignVector(y, -sin(a), cos(a), 0);
+#else
+          // Flip the normal
+          Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0);
+          Vector y; GeometryToolbox::AssignVector(y, sin(a), -cos(a), 0);
+#endif
+
+          SliceGeometry s(source_->GetSlice(slice_).GetGeometry().GetOrigin(), x, y);
+          widget_->SetSlice(s);
+#endif
+        }
+      }
+
+
+      void OnMainWidgetGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message)
+      {
+        // Once the geometry of the series is downloaded from Orthanc,
+        // display its middle slice, and adapt the viewport to fit this
+        // slice
+        if (source_ == &message.GetOrigin())
+        {
+          SetSlice(source_->GetSliceCount() / 2);
+        }
+
+        GetMainWidget().FitContent();
+      }
+
+    DicomFrameConverter                                converter_;
+
+    void OnSliceContentChangedMessage(const ISlicedVolume::SliceContentChangedMessage&   message)
+    {
+      converter_ = message.GetSlice().GetConverter();
+    }
+
+    void OnVolumeReadyMessage(const ISlicedVolume::VolumeReadyMessage& message)
+    {
+      const float percentile = 0.01f;
+
+      auto& slicedVolume = message.GetOrigin();
+      const OrthancVolumeImage& volume = dynamic_cast<const OrthancVolumeImage&>(slicedVolume);
+
+      std::vector<float> distribution;
+      ReadDistribution(distribution, volume.GetImage().GetInternalImage());
+      std::sort(distribution.begin(), distribution.end());
+
+      int start = static_cast<int>(std::ceil(distribution.size() * percentile));
+      int end = static_cast<int>(std::floor(distribution.size() * (1.0f - percentile)));
+
+      float a = 0;
+      float b = 0;
+
+      if (start < end &&
+        start >= 0 &&
+        end < static_cast<int>(distribution.size()))
+      {
+        a = distribution[start];
+        b = distribution[end];
+      }
+      else if (!distribution.empty())
+      {
+        // Too small distribution: Use full range
+        a = distribution.front();
+        b = distribution.back();
+      }
+
+      //printf("WINDOWING %f %f\n", a, b);
+
+      for (const auto& pair : doseCtWidgetLayerPairs_)
+      {
+        auto widget = pair.first;
+        auto layer = pair.second;
+        RenderStyle s = widget->GetLayerStyle(layer);
+        s.windowing_ = ImageWindowing_Custom;
+        s.customWindowCenter_ = static_cast<float>(converter_.Apply((a + b) / 2.0f));
+        s.customWindowWidth_ = static_cast<float>(converter_.Apply(b - a));
+
+        // 96.210556 => 192.421112
+        widget->SetLayerStyle(layer, s);
+        printf("Windowing: %f => %f\n", s.customWindowCenter_, s.customWindowWidth_);
+      }
+    }
+      
+
+
+      size_t AddDoseLayer(SliceViewerWidget& widget,
+        OrthancVolumeImage& volume, VolumeProjection projection);
+
+      void AddStructLayer(
+        SliceViewerWidget& widget, StructureSetLoader& loader);
+
+      SliceViewerWidget* CreateDoseCtWidget(
+        std::unique_ptr<OrthancVolumeImage>& ct,
+        std::unique_ptr<OrthancVolumeImage>& dose,
+        std::unique_ptr<StructureSetLoader>& structLoader,
+        VolumeProjection projection);
+
+      void AddCtLayer(SliceViewerWidget& widget, OrthancVolumeImage& volume);
+
+      std::unique_ptr<Interactor>         mainWidgetInteractor_;
+      const DicomSeriesVolumeSlicer*    source_;
+      unsigned int                      slice_;
+
+      std::string                                        ctSeries_;
+      std::string                                        doseInstance_;
+      std::string                                        doseSeries_;
+      std::string                                        structInstance_;
+      std::unique_ptr<OrthancStone::OrthancVolumeImage>    dose_;
+      std::unique_ptr<OrthancStone::OrthancVolumeImage>    ct_;
+      std::unique_ptr<OrthancStone::StructureSetLoader>    struct_;
+
+    public:
+      RtViewerDemoApplication(MessageBroker& broker) :
+        IObserver(broker),
+        source_(NULL),
+        slice_(0)
+      {
+      }
+
+      /*
+      dev options on bgo xps15
+
+      COMMAND LINE
+      --ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa --dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb --struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9
+
+      URL PARAMETERS
+      ?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa&dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb&struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9
+
+      */
+
+      void ParseParameters(const boost::program_options::variables_map&  parameters)
+      {
+        // Generic
+        {
+          if (parameters.count("verbose"))
+          {
+            Orthanc::Logging::EnableInfoLevel(true);
+            LOG(INFO) << "Verbose logs (info) are enabled";
+          }
+        }
+
+        {
+          if (parameters.count("trace"))
+          {
+            LOG(INFO) << "parameters.count(\"trace\") != 0";
+            Orthanc::Logging::EnableTraceLevel(true);
+            VLOG(1) << "Trace logs (debug) are enabled";
+          }
+        }
+
+        // CT series
+        {
+
+          if (parameters.count("ct-series") != 1)
+          {
+            LOG(ERROR) << "There must be exactly one CT series specified";
+            throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+          }
+          ctSeries_ = parameters["ct-series"].as<std::string>();
+        }
+
+        // RTDOSE 
+        {
+          if (parameters.count("dose-instance") == 1)
+          {
+            doseInstance_ = parameters["dose-instance"].as<std::string>();
+          }
+          else
+          {
+#ifdef BGO_NOT_IMPLEMENTED_YET
+            // Dose series
+            if (parameters.count("dose-series") != 1)
+            {
+              LOG(ERROR) << "the RTDOSE series is missing";
+              throw Orthanc::OrthancException(
+                Orthanc::ErrorCode_ParameterOutOfRange);
+            }
+            doseSeries_ = parameters["ct"].as<std::string>();
+#endif
+            LOG(ERROR) << "the RTSTRUCT instance is missing";
+            throw Orthanc::OrthancException(
+              Orthanc::ErrorCode_ParameterOutOfRange);
+          }
+        }
+        
+        // RTSTRUCT 
+        {
+          if (parameters.count("struct-instance") == 1)
+          {
+            structInstance_ = parameters["struct-instance"].as<std::string>();
+          }
+          else
+          {
+#ifdef BGO_NOT_IMPLEMENTED_YET
+            // Struct series
+            if (parameters.count("struct-series") != 1)
+            {
+              LOG(ERROR) << "the RTSTRUCT series is missing";
+              throw Orthanc::OrthancException(
+                Orthanc::ErrorCode_ParameterOutOfRange);
+            }
+            structSeries_ = parameters["struct-series"].as<std::string>();
+#endif
+            LOG(ERROR) << "the RTSTRUCT instance is missing";
+            throw Orthanc::OrthancException(
+              Orthanc::ErrorCode_ParameterOutOfRange);
+          }
+        }
+      }
+
+      virtual void DeclareStartupOptions(
+        boost::program_options::options_description& options)
+      {
+        boost::program_options::options_description generic(
+          "RtViewerDemo options. Please note that some of these options "
+          "are mutually exclusive");
+        generic.add_options()
+          ("ct-series", boost::program_options::value<std::string>(),
+            "Orthanc ID of the CT series")
+          ("dose-instance", boost::program_options::value<std::string>(), 
+            "Orthanc ID of the RTDOSE instance (incompatible with dose-series)")
+          ("dose-series", boost::program_options::value<std::string>(), 
+            "NOT IMPLEMENTED YET. Orthanc ID of the RTDOSE series (incompatible"
+            " with dose-instance)")
+          ("struct-instance", boost::program_options::value<std::string>(), 
+            "Orthanc ID of the RTSTRUCT instance (incompatible with struct-"
+            "series)")
+          ("struct-series", boost::program_options::value<std::string>(), 
+            "NOT IMPLEMENTED YET. Orthanc ID of the RTSTRUCT (incompatible with"
+            " struct-instance)")
+          ("smooth", boost::program_options::value<bool>()->default_value(true),
+            "Enable bilinear image smoothing")
+          ;
+
+        options.add(generic);
+      }
+
+      virtual void Initialize(
+        StoneApplicationContext*                      context,
+        IStatusBar&                                   statusBar,
+        const boost::program_options::variables_map&  parameters)
+      {
+        using namespace OrthancStone;
+
+        ParseParameters(parameters);
+
+        context_ = context;
+
+        statusBar.SetMessage("Use the key \"s\" to reinitialize the layout");
+
+        if (!ctSeries_.empty())
+        {
+          printf("CT = [%s]\n", ctSeries_.c_str());
+
+          ct_.reset(new OrthancStone::OrthancVolumeImage(
+            IObserver::GetBroker(), context->GetOrthancApiClient(), false));
+          ct_->ScheduleLoadSeries(ctSeries_);
+          //ct_->ScheduleLoadSeries(
+          //  "a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");
+          //ct_->ScheduleLoadSeries(
+          //  "03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0");
+        }
+
+        if (!doseSeries_.empty() ||
+          !doseInstance_.empty())
+        {
+          dose_.reset(new OrthancStone::OrthancVolumeImage(
+            IObserver::GetBroker(), context->GetOrthancApiClient(), true));
+
+
+          dose_->RegisterObserverCallback(
+            new Callable<RtViewerDemoApplication, ISlicedVolume::VolumeReadyMessage>
+            (*this, &RtViewerDemoApplication::OnVolumeReadyMessage));
+
+          dose_->RegisterObserverCallback(
+            new Callable<RtViewerDemoApplication, ISlicedVolume::SliceContentChangedMessage>
+            (*this, &RtViewerDemoApplication::OnSliceContentChangedMessage));
+
+          if (doseInstance_.empty())
+          {
+            dose_->ScheduleLoadSeries(doseSeries_);
+          }
+          else
+          {
+            dose_->ScheduleLoadInstance(doseInstance_);
+          }
+
+          //dose_->ScheduleLoadInstance(
+            //"830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb");  // 1
+          //dose_->ScheduleLoadInstance(
+            //"269f26f4-0c83eeeb-2e67abbd-5467a40f-f1bec90c"); //0522c0001 TCIA
+        }
+
+        if (!structInstance_.empty())
+        {
+          struct_.reset(new OrthancStone::StructureSetLoader(
+            IObserver::GetBroker(), context->GetOrthancApiClient()));
+
+          struct_->ScheduleLoadInstance(structInstance_);
+
+          //struct_->ScheduleLoadInstance(
+            //"54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");
+          //struct_->ScheduleLoadInstance(
+            //"17cd032b-ad92a438-ca05f06a-f9e96668-7e3e9e20"); // 0522c0001 TCIA
+        }
+
+        mainWidget_ = new LayoutWidget("main-layout");
+        mainWidget_->SetBackgroundColor(0, 0, 0);
+        mainWidget_->SetBackgroundCleared(true);
+        mainWidget_->SetPadding(0);
+
+        auto axialWidget = CreateDoseCtWidget
+        (ct_, dose_, struct_, OrthancStone::VolumeProjection_Axial);
+        mainWidget_->AddWidget(axialWidget);
+               
+        std::unique_ptr<OrthancStone::LayoutWidget> subLayout(
+          new OrthancStone::LayoutWidget("main-layout"));
+        subLayout->SetVertical();
+        subLayout->SetPadding(5);
+
+        auto coronalWidget = CreateDoseCtWidget
+        (ct_, dose_, struct_, OrthancStone::VolumeProjection_Coronal);
+        subLayout->AddWidget(coronalWidget);
+
+        auto sagittalWidget = CreateDoseCtWidget
+        (ct_, dose_, struct_, OrthancStone::VolumeProjection_Sagittal);
+        subLayout->AddWidget(sagittalWidget);
+        
+        mainWidget_->AddWidget(subLayout.release());
+      }
+    };
+
+
+    size_t RtViewerDemoApplication::AddDoseLayer(
+      SliceViewerWidget& widget,
+      OrthancVolumeImage& volume, VolumeProjection projection)
+    {
+      size_t layer = widget.AddLayer(
+        new VolumeImageMPRSlicer(IObserver::GetBroker(), volume));
+
+      RenderStyle s;
+      //s.drawGrid_ = true;
+      s.SetColor(255, 0, 0);  // Draw missing PET layer in red
+      s.alpha_ = 0.3f;
+      s.applyLut_ = true;
+      s.lut_ = Orthanc::EmbeddedResources::COLORMAP_JET;
+      s.interpolation_ = ImageInterpolation_Bilinear;
+      widget.SetLayerStyle(layer, s);
+
+      return layer;
+    }
+
+    void RtViewerDemoApplication::AddStructLayer(
+      SliceViewerWidget& widget, StructureSetLoader& loader)
+    {
+      widget.AddLayer(new DicomStructureSetSlicer(
+        IObserver::GetBroker(), loader));
+    }
+
+    SliceViewerWidget* RtViewerDemoApplication::CreateDoseCtWidget(
+      std::unique_ptr<OrthancVolumeImage>& ct,
+      std::unique_ptr<OrthancVolumeImage>& dose,
+      std::unique_ptr<StructureSetLoader>& structLoader,
+      VolumeProjection projection)
+    {
+      std::unique_ptr<OrthancStone::SliceViewerWidget> widget(
+        new OrthancStone::SliceViewerWidget(IObserver::GetBroker(),
+          "ct-dose-widget"));
+
+      if (ct.get() != NULL)
+      {
+        AddCtLayer(*widget, *ct);
+      }
+
+      if (dose.get() != NULL)
+      {
+        size_t layer = AddDoseLayer(*widget, *dose, projection);
+
+        // we need to store the dose rendering widget because we'll update them
+        // according to various asynchronous events
+        doseCtWidgetLayerPairs_.push_back(std::make_pair(widget.get(), layer));
+#if 0
+        interactors_.push_back(new VolumeImageInteractor(
+          IObserver::GetBroker(), *dose, *widget, projection));
+#else
+        interactors_.push_back(new DoseInteractor(
+          IObserver::GetBroker(), *dose, *widget, projection, layer));
+#endif
+      }
+      else if (ct.get() != NULL)
+      {
+        interactors_.push_back(
+          new VolumeImageInteractor(
+            IObserver::GetBroker(), *ct, *widget, projection));
+      }
+
+      if (structLoader.get() != NULL)
+      {
+        AddStructLayer(*widget, *structLoader);
+      }
+
+      return widget.release();
+    }
+
+    void RtViewerDemoApplication::AddCtLayer(
+      SliceViewerWidget& widget,
+      OrthancVolumeImage& volume)
+    {
+      size_t layer = widget.AddLayer(
+        new VolumeImageMPRSlicer(IObserver::GetBroker(), volume));
+
+      RenderStyle s;
+      //s.drawGrid_ = true;
+      s.alpha_ = 1;
+      s.windowing_ = ImageWindowing_Bone;
+      widget.SetLayerStyle(layer, s);
+    }
+  }
+}
+
+
+
+#if ORTHANC_ENABLE_WASM==1
+
+#include "Platforms/Wasm/WasmWebService.h"
+#include "Platforms/Wasm/WasmViewport.h"
+
+#include <emscripten/emscripten.h>
+
+//#include "SampleList.h"
+
+
+OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker)
+{
+  return new OrthancStone::Samples::RtViewerDemoApplication(broker);
+}
+
+OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application)
+{
+  return dynamic_cast<OrthancStone::Samples::RtViewerDemoApplication*>(application)->CreateWasmApplicationAdapter(broker);
+}
+
+#else
+
+//#include "SampleList.h"
+#if ORTHANC_ENABLE_SDL==1
+#include "Applications/Sdl/SdlStoneApplicationRunner.h"
+#endif
+#if ORTHANC_ENABLE_QT==1
+#include "Applications/Qt/SampleQtApplicationRunner.h"
+#endif
+#include "Framework/Messages/MessageBroker.h"
+
+int main(int argc, char* argv[])
+{
+  OrthancStone::MessageBroker broker;
+  OrthancStone::Samples::RtViewerDemoApplication sampleStoneApplication(broker);
+
+#if ORTHANC_ENABLE_SDL==1
+  OrthancStone::SdlStoneApplicationRunner sdlApplicationRunner(broker, sampleStoneApplication);
+  return sdlApplicationRunner.Execute(argc, argv);
+#endif
+#if ORTHANC_ENABLE_QT==1
+  OrthancStone::Samples::SampleQtApplicationRunner qtAppRunner(broker, sampleStoneApplication);
+  return qtAppRunner.Execute(argc, argv);
+#endif
+}
+
+
+#endif
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/nginx.local.conf	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,44 @@
+# Local config to serve the WASM samples static files and reverse proxy Orthanc.
+# Uses port 9977 instead of 80.
+
+# `events` section is mandatory
+events {
+  worker_connections 1024; # Default: 1024
+}
+
+http {
+
+  # prevent nginx sync issues on OSX
+  proxy_buffering off;
+
+  server {
+    listen 9977 default_server;
+    client_max_body_size 4G;
+
+    # location may have to be adjusted depending on your OS and nginx install
+    include /etc/nginx/mime.types;
+    # if not in your system mime.types, add this line to support WASM:
+    # types {
+    #    application/wasm                      wasm; 
+    # }
+
+    # serve WASM static files
+    root build-web/;
+    location / {
+	}
+
+    # reverse proxy orthanc
+	location /orthanc/ {
+		rewrite /orthanc(.*) $1 break;
+		proxy_pass http://127.0.0.1:8042;
+		proxy_set_header Host $http_host;
+		proxy_set_header my-auth-header good-token;
+		proxy_request_buffering off;
+		proxy_max_temp_file_size 0;
+		client_max_body_size 0;
+	}
+
+
+  }
+  
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.html	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,25 @@
+<!doctype html>
+
+<html lang="us">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <!-- Disable pinch zoom on mobile devices -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <meta name="HandheldFriendly" content="true" />
+
+    <title>Simple Viewer</title>
+    <link href="samples-styles.css" rel="stylesheet" />
+
+<body>
+  <div style="width: 100%; height: 5%">
+    <p>RTSTRUCT viewer demonstration</p>
+  </div>
+  <div style="width: 100%; height: 95%">
+    <canvas id="canvas"></canvas>
+  </div>
+  <script type="text/javascript" src="app-rt-viewer-demo.js"></script>
+</body>
+
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,5 @@
+import { InitializeWasmApplication } from '../../../Platforms/Wasm/wasm-application-runner';
+
+
+InitializeWasmApplication("RtViewerDemo", "/orthanc");
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.tsconfig.json	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,8 @@
+{
+    "extends" : "./tsconfig-samples",
+    "compilerOptions": {
+    },
+    "include" : [
+        "rt-viewer-demo.ts"
+    ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/samples-styles.css	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,16 @@
+html, body {
+  width: 100%;
+  height: 100%;
+  margin: 0px;
+  border: 0;
+  overflow: hidden; /*  Disable scrollbars */
+  display: block;  /* No floating content on sides */
+  background-color: black;
+  color: white;
+  font-family: Arial, Helvetica, sans-serif;
+}
+
+canvas {
+  left:0px;
+  top:0px;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/start-serving-files.sh	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+sudo nginx -p $(pwd) -c nginx.local.conf
+
+echo "Please browse to :"
+
+echo "http://localhost:9977/rt-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa&dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb&struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"
+
+echo "(This requires you have uploaded the correct files to your local Orthanc instance)"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/stop-serving-files.sh	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+sudo nginx -s stop
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/rt-viewer-demo/tsconfig-samples.json	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,11 @@
+{
+    "extends" : "../../../Platforms/Wasm/tsconfig-stone.json",
+    "compilerOptions": {
+        "sourceMap": false,
+        "lib" : [
+            "es2017",
+            "dom",
+            "dom.iterable"
+        ]
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Samples/Deprecated/tsconfig-stone.json	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,7 @@
+{
+    "include" : [
+        "../../Platforms/Wasm/stone-framework-loader.ts",
+        "../../Platforms/Wasm/wasm-application-runner.ts",
+        "../../Platforms/Wasm/wasm-viewport.ts"
+    ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Sdl/SdlCairoSurface.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,94 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "SdlCairoSurface.h"
+
+#if ORTHANC_ENABLE_SDL == 1
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+namespace OrthancStone
+{
+  SdlCairoSurface::SdlCairoSurface(SdlWindow& window) :
+    window_(window),
+    sdlSurface_(NULL)
+  {
+  }
+
+
+  SdlCairoSurface::~SdlCairoSurface()
+  {
+    if (sdlSurface_)
+    {
+      SDL_FreeSurface(sdlSurface_);
+    }
+  }
+
+
+  void SdlCairoSurface::SetSize(unsigned int width,
+                                unsigned int height)
+  {
+    if (cairoSurface_.get() == NULL ||
+        cairoSurface_->GetWidth() != width ||
+        cairoSurface_->GetHeight() != height)
+    {
+      cairoSurface_.reset(new CairoSurface(width, height, false /* no alpha */));
+
+      // TODO Big endian?
+      static const uint32_t rmask = 0x00ff0000;
+      static const uint32_t gmask = 0x0000ff00;
+      static const uint32_t bmask = 0x000000ff;
+
+      if (sdlSurface_)
+      {
+        SDL_FreeSurface(sdlSurface_);
+      }
+
+      sdlSurface_ = SDL_CreateRGBSurfaceFrom(cairoSurface_->GetBuffer(), width, height, 32,
+                                             cairoSurface_->GetPitch(), rmask, gmask, bmask, 0);
+      if (!sdlSurface_)
+      {
+        LOG(ERROR) << "Cannot create a SDL surface from a Cairo surface";
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+    }
+  }
+
+
+  void SdlCairoSurface::Render(Deprecated::IViewport& viewport)
+  {
+    if (cairoSurface_.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+
+    Orthanc::ImageAccessor target;
+    cairoSurface_->GetWriteableAccessor(target);
+
+    if (viewport.Render(target))
+    {
+      window_.Render(sdlSurface_);
+    }
+  }
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Sdl/SdlCairoSurface.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,56 @@
+/**
+ * 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
+
+#if ORTHANC_ENABLE_SDL == 1
+
+#include "../../Framework/Viewport/SdlWindow.h"
+#include "../../Framework/Wrappers/CairoSurface.h"
+#include "../../Framework/Deprecated/Viewport/IViewport.h"
+
+#include <Core/Compatibility.h>
+
+#include <SDL_render.h>
+#include <boost/thread/mutex.hpp>
+
+namespace OrthancStone
+{
+  class SdlCairoSurface : public boost::noncopyable
+  {
+  private:
+    std::unique_ptr<CairoSurface>  cairoSurface_;
+    SdlWindow&                   window_;
+    SDL_Surface*                 sdlSurface_;
+
+  public:
+    SdlCairoSurface(SdlWindow& window);
+
+    ~SdlCairoSurface();
+
+    void SetSize(unsigned int width,
+                 unsigned int height);
+
+    void Render(Deprecated::IViewport& viewport);
+  };
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Sdl/SdlEngine.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,282 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "SdlEngine.h"
+
+#if ORTHANC_ENABLE_SDL == 1
+
+#include <Core/Logging.h>
+
+#include <SDL.h>
+
+namespace OrthancStone
+{
+  void SdlEngine::SetSize(unsigned int width,
+                          unsigned int height)
+  {
+    NativeStoneApplicationContext::GlobalMutexLocker locker(context_);
+    locker.GetCentralViewport().SetSize(width, height);
+    surface_.SetSize(width, height);
+  }
+
+
+  void SdlEngine::RenderFrame()
+  {
+    if (viewportChanged_)
+    {
+      NativeStoneApplicationContext::GlobalMutexLocker locker(context_);
+      surface_.Render(locker.GetCentralViewport());
+
+      viewportChanged_ = false;
+    }
+  }
+
+
+  KeyboardModifiers SdlEngine::GetKeyboardModifiers(const uint8_t* keyboardState,
+                                                    const int scancodeCount)
+  {
+    int result = KeyboardModifiers_None;
+
+    if (keyboardState != NULL)
+    {
+      if (SDL_SCANCODE_LSHIFT < scancodeCount &&
+          keyboardState[SDL_SCANCODE_LSHIFT])
+      {
+        result |= KeyboardModifiers_Shift;
+      }
+
+      if (SDL_SCANCODE_RSHIFT < scancodeCount &&
+          keyboardState[SDL_SCANCODE_RSHIFT])
+      {
+        result |= KeyboardModifiers_Shift;
+      }
+
+      if (SDL_SCANCODE_LCTRL < scancodeCount &&
+          keyboardState[SDL_SCANCODE_LCTRL])
+      {
+        result |= KeyboardModifiers_Control;
+      }
+
+      if (SDL_SCANCODE_RCTRL < scancodeCount &&
+          keyboardState[SDL_SCANCODE_RCTRL])
+      {
+        result |= KeyboardModifiers_Control;
+      }
+
+      if (SDL_SCANCODE_LALT < scancodeCount &&
+          keyboardState[SDL_SCANCODE_LALT])
+      {
+        result |= KeyboardModifiers_Alt;
+      }
+
+      if (SDL_SCANCODE_RALT < scancodeCount &&
+          keyboardState[SDL_SCANCODE_RALT])
+      {
+        result |= KeyboardModifiers_Alt;
+      }
+    }
+
+    return static_cast<KeyboardModifiers>(result);
+  }
+
+
+  SdlEngine::SdlEngine(SdlWindow& window,
+                       NativeStoneApplicationContext& context) :
+    window_(window),
+    context_(context),
+    surface_(window),
+    viewportChanged_(true)
+  {
+  }
+  
+
+  void SdlEngine::Run()
+  {
+    int scancodeCount = 0;
+    const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
+
+    SetSize(window_.GetWidth(), window_.GetHeight());
+    
+    {
+      NativeStoneApplicationContext::GlobalMutexLocker locker(context_);
+      locker.GetCentralViewport().FitContent();
+    }
+    
+    bool stop = false;
+    while (!stop)
+    {
+      RenderFrame();
+
+      SDL_Event event;
+
+      while (!stop &&
+             SDL_PollEvent(&event))
+      {
+        NativeStoneApplicationContext::GlobalMutexLocker locker(context_);
+
+        if (event.type == SDL_QUIT)
+        {
+          stop = true;
+          break;
+        }
+        else if (event.type == SDL_MOUSEBUTTONDOWN)
+        {
+          KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount);
+
+          switch (event.button.button)
+          {
+          case SDL_BUTTON_LEFT:
+            locker.GetCentralViewport().MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>());
+            break;
+            
+          case SDL_BUTTON_RIGHT:
+            locker.GetCentralViewport().MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>());
+            break;
+            
+          case SDL_BUTTON_MIDDLE:
+            locker.GetCentralViewport().MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>());
+            break;
+
+          default:
+            break;
+          }
+        }
+        else if (event.type == SDL_MOUSEMOTION)
+        {
+          locker.GetCentralViewport().MouseMove(event.button.x, event.button.y, std::vector<Deprecated::Touch>());
+        }
+        else if (event.type == SDL_MOUSEBUTTONUP)
+        {
+          locker.GetCentralViewport().MouseUp();
+        }
+        else if (event.type == SDL_WINDOWEVENT)
+        {
+          switch (event.window.event)
+          {
+          case SDL_WINDOWEVENT_LEAVE:
+            locker.GetCentralViewport().MouseLeave();
+            break;
+
+          case SDL_WINDOWEVENT_ENTER:
+            locker.GetCentralViewport().MouseEnter();
+            break;
+
+          case SDL_WINDOWEVENT_SIZE_CHANGED:
+            SetSize(event.window.data1, event.window.data2);
+            break;
+
+          default:
+            break;
+          }
+        }
+        else if (event.type == SDL_MOUSEWHEEL)
+        {
+          KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount);
+
+          int x, y;
+          SDL_GetMouseState(&x, &y);
+
+          if (event.wheel.y > 0)
+          {
+            locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers);
+          }
+          else if (event.wheel.y < 0)
+          {
+            locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers);
+          }
+        }
+        else if (event.type == SDL_KEYDOWN &&
+                 event.key.repeat == 0 /* Ignore key bounce */)
+        {
+          KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount);
+
+          switch (event.key.keysym.sym)
+          {
+          case SDLK_a:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'a', modifiers);  break;
+          case SDLK_b:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'b', modifiers);  break;
+          case SDLK_c:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'c', modifiers);  break;
+          case SDLK_d:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'd', modifiers);  break;
+          case SDLK_e:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'e', modifiers);  break;
+          case SDLK_f:    window_.ToggleMaximize();                         break;
+          case SDLK_g:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'g', modifiers);  break;
+          case SDLK_h:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'h', modifiers);  break;
+          case SDLK_i:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'i', modifiers);  break;
+          case SDLK_j:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'j', modifiers);  break;
+          case SDLK_k:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'k', modifiers);  break;
+          case SDLK_l:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'l', modifiers);  break;
+          case SDLK_m:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'm', modifiers);  break;
+          case SDLK_n:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'n', modifiers);  break;
+          case SDLK_o:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'o', modifiers);  break;
+          case SDLK_p:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'p', modifiers);  break;
+          case SDLK_q:    stop = true;                                      break;
+          case SDLK_r:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'r', modifiers);  break;
+          case SDLK_s:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 's', modifiers);  break;
+          case SDLK_t:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 't', modifiers);  break;
+          case SDLK_u:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'u', modifiers);  break;
+          case SDLK_v:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'v', modifiers);  break;
+          case SDLK_w:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'w', modifiers);  break;
+          case SDLK_x:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'x', modifiers);  break;
+          case SDLK_y:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'y', modifiers);  break;
+          case SDLK_z:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'z', modifiers);  break;
+          case SDLK_KP_0: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '0', modifiers);  break;
+          case SDLK_KP_1: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '1', modifiers);  break;
+          case SDLK_KP_2: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '2', modifiers);  break;
+          case SDLK_KP_3: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '3', modifiers);  break;
+          case SDLK_KP_4: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '4', modifiers);  break;
+          case SDLK_KP_5: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '5', modifiers);  break;
+          case SDLK_KP_6: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '6', modifiers);  break;
+          case SDLK_KP_7: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '7', modifiers);  break;
+          case SDLK_KP_8: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '8', modifiers);  break;
+          case SDLK_KP_9: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '9', modifiers);  break;
+
+          case SDLK_PLUS:
+          case SDLK_KP_PLUS:
+            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '+', modifiers);  break;
+
+          case SDLK_MINUS:
+          case SDLK_KP_MINUS:
+            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '-', modifiers);  break;
+
+          case SDLK_DELETE:
+            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Delete, 0, modifiers);  break;
+          case SDLK_BACKSPACE:
+            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Backspace, 0, modifiers);  break;
+          case SDLK_RIGHT:
+            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Right, 0, modifiers);  break;
+          case SDLK_LEFT:
+            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Left, 0, modifiers);  break;
+          case SDLK_UP:
+            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Up, 0, modifiers);  break;
+          case SDLK_DOWN:
+            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Down, 0, modifiers);  break;
+          default:
+            break;
+          }
+        }
+      }
+
+      // Small delay to avoid using 100% of CPU
+      SDL_Delay(1);
+    }
+  }
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Sdl/SdlEngine.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,61 @@
+/**
+ * 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
+
+#if ORTHANC_ENABLE_SDL == 1
+
+#include "../../Framework/Messages/ObserverBase.h"
+#include "../Generic/NativeStoneApplicationContext.h"
+#include "SdlCairoSurface.h"
+
+namespace OrthancStone
+{
+  class SdlEngine : public ObserverBase<SdlEngine>
+  {
+  private:
+    SdlWindow&                window_;
+    NativeStoneApplicationContext&  context_;
+    SdlCairoSurface           surface_;
+    bool                      viewportChanged_;
+
+    void SetSize(unsigned int width,
+                 unsigned int height);
+    
+    void RenderFrame();
+
+    static KeyboardModifiers GetKeyboardModifiers(const uint8_t* keyboardState,
+                                                  const int scancodeCount);
+
+  public:
+    SdlEngine(SdlWindow& window,
+              NativeStoneApplicationContext& context);
+  
+    void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message)
+    {
+      viewportChanged_ = true;
+    }
+
+    void Run();
+  };
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Sdl/SdlOrthancSurface.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,108 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "SdlOrthancSurface.h"
+
+#if ORTHANC_ENABLE_SDL == 1
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <Core/Images/Image.h>
+
+#include <SDL_render.h>
+
+namespace OrthancStone
+{
+  SdlOrthancSurface::SdlOrthancSurface(SdlWindow& window) :
+    window_(window),
+    sdlSurface_(NULL)
+  {
+  }
+
+
+  SdlOrthancSurface::~SdlOrthancSurface()
+  {
+    if (sdlSurface_)
+    {
+      SDL_FreeSurface(sdlSurface_);
+    }
+  }
+
+
+  void SdlOrthancSurface::SetSize(unsigned int width,
+                                  unsigned int height)
+  {
+    if (image_.get() == NULL ||
+        image_->GetWidth() != width ||
+        image_->GetHeight() != height)
+    {
+      image_.reset(new Orthanc::Image(Orthanc::PixelFormat_BGRA32, width, height, true));  // (*)
+
+      if (image_->GetPitch() != image_->GetWidth() * 4)
+      {
+        // This should have been ensured by setting "forceMinimalPitch" to "true" (*)
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+
+      // TODO Big endian?
+      static const uint32_t rmask = 0x00ff0000;
+      static const uint32_t gmask = 0x0000ff00;
+      static const uint32_t bmask = 0x000000ff;
+
+      if (sdlSurface_)
+      {
+        SDL_FreeSurface(sdlSurface_);
+      }
+
+      sdlSurface_ = SDL_CreateRGBSurfaceFrom(image_->GetBuffer(), width, height, 32,
+                                             image_->GetPitch(), rmask, gmask, bmask, 0);
+      if (!sdlSurface_)
+      {
+        LOG(ERROR) << "Cannot create a SDL surface from a Orthanc surface";
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+    }
+  }
+
+
+  Orthanc::ImageAccessor& SdlOrthancSurface::GetImage()
+  {
+    if (image_.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+
+    return *image_;
+  }
+
+
+  void SdlOrthancSurface::Render()
+  {
+    if (image_.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+
+    window_.Render(sdlSurface_);
+  }
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Sdl/SdlOrthancSurface.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,56 @@
+/**
+ * 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
+
+#if ORTHANC_ENABLE_SDL == 1
+
+#include "../../Framework/Viewport/SdlWindow.h"
+
+#include <Core/Compatibility.h>
+#include <Core/Images/ImageAccessor.h>
+
+#include <boost/thread/mutex.hpp>
+
+namespace OrthancStone
+{
+  class SdlOrthancSurface : public boost::noncopyable
+  {
+  private:
+    std::unique_ptr<Orthanc::ImageAccessor>  image_;
+    SdlWindow&                             window_;
+    SDL_Surface*                           sdlSurface_;
+
+  public:
+    SdlOrthancSurface(SdlWindow& window);
+
+    ~SdlOrthancSurface();
+
+    void SetSize(unsigned int width,
+                 unsigned int height);
+
+    Orthanc::ImageAccessor& GetImage();
+
+    void Render();
+  };
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Sdl/SdlStoneApplicationRunner.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,138 @@
+/**
+ * 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/>.
+ **/
+
+
+#if ORTHANC_ENABLE_SDL != 1
+#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 1
+#endif
+
+#include "SdlStoneApplicationRunner.h"
+
+#include "../../Platforms/Generic/OracleWebService.h"
+#include "SdlEngine.h"
+
+#include <Core/Logging.h>
+#include <Core/HttpClient.h>
+#include <Core/Toolbox.h>
+#include <Core/OrthancException.h>
+#include <Plugins/Samples/Common/OrthancHttpConnection.h>
+
+#include <boost/program_options.hpp>
+
+namespace OrthancStone
+{
+  void SdlStoneApplicationRunner::Initialize()
+  {
+    SdlWindow::GlobalInitialize();
+  }
+
+  
+  void SdlStoneApplicationRunner::DeclareCommandLineOptions(boost::program_options::options_description& options)
+  {
+    boost::program_options::options_description sdl("SDL options");
+    sdl.add_options()
+        ("width", boost::program_options::value<int>()->default_value(1024), "Initial width of the SDL window")
+        ("height", boost::program_options::value<int>()->default_value(768), "Initial height of the SDL window")
+        ("opengl", boost::program_options::value<bool>()->default_value(true), "Enable OpenGL in SDL")
+        ;
+
+    options.add(sdl);
+  }
+
+  
+  void SdlStoneApplicationRunner::ParseCommandLineOptions(const boost::program_options::variables_map& parameters)
+  {
+    if (!parameters.count("width") ||
+        !parameters.count("height") ||
+        !parameters.count("opengl"))
+    {
+      LOG(ERROR) << "Parameter \"width\", \"height\" or \"opengl\" is missing";
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    int w = parameters["width"].as<int>();
+    int h = parameters["height"].as<int>();
+    if (w <= 0 || h <= 0)
+    {
+      LOG(ERROR) << "Parameters \"width\" and \"height\" must be positive";
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    width_ = static_cast<unsigned int>(w);
+    height_ = static_cast<unsigned int>(h);
+    LOG(WARNING) << "Initial display size: " << width_ << "x" << height_;
+
+    enableOpenGl_ = parameters["opengl"].as<bool>();
+    if (enableOpenGl_)
+    {
+      LOG(WARNING) << "OpenGL is enabled, disable it with option \"--opengl=off\" if the application crashes";
+    }
+    else
+    {
+      LOG(WARNING) << "OpenGL is disabled, enable it with option \"--opengl=on\" for best performance";
+    }
+  }
+
+  
+  void SdlStoneApplicationRunner::Run(NativeStoneApplicationContext& context,
+                                      const std::string& title,
+                                      int argc,
+                                      char* argv[])
+  {
+    /**************************************************************
+     * Run the application inside a SDL window
+     **************************************************************/
+
+    LOG(WARNING) << "Starting the application";
+
+    SdlWindow window(title.c_str(), width_, height_, enableOpenGl_);
+    boost::shared_ptr<SdlEngine> sdl(new SdlEngine(window, context));
+
+    {
+      NativeStoneApplicationContext::GlobalMutexLocker locker(context);
+
+      sdl->Register<Deprecated::IViewport::ViewportChangedMessage>
+        (locker.GetCentralViewport(), &SdlEngine::OnViewportChanged);
+
+      //context.GetCentralViewport().Register(sdl);  // (*)
+    }
+
+    context.Start();
+    sdl->Run();
+
+    LOG(WARNING) << "Stopping the application";
+
+    // Don't move the "Stop()" command below out of the block,
+    // otherwise the application might crash, because the
+    // "SdlEngine" is an observer of the viewport (*) and the
+    // update thread started by "context.Start()" would call a
+    // destructed object (the "SdlEngine" is deleted with the
+    // lexical scope).
+
+    // TODO Is this still true with message broker?
+    context.Stop();
+  }
+
+  
+  void SdlStoneApplicationRunner::Finalize()
+  {
+    SdlWindow::GlobalFinalize();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Sdl/SdlStoneApplicationRunner.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,60 @@
+/**
+ * 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 "../Generic/NativeStoneApplicationRunner.h"
+
+#if ORTHANC_ENABLE_SDL != 1
+#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 1
+#endif
+
+#include <SDL.h>   // Necessary to avoid undefined reference to `SDL_main'
+
+namespace OrthancStone
+{
+  class SdlStoneApplicationRunner : public NativeStoneApplicationRunner
+  {
+  private:
+    unsigned int  width_;
+    unsigned int  height_;
+    bool          enableOpenGl_;
+    
+  public:
+    SdlStoneApplicationRunner(boost::shared_ptr<IStoneApplication> application) :
+      NativeStoneApplicationRunner(application)
+    {
+    }
+
+    virtual void Initialize();
+    
+    virtual void DeclareCommandLineOptions(boost::program_options::options_description& options);
+    
+    virtual void Run(NativeStoneApplicationContext& context,
+                     const std::string& title,
+                     int argc,
+                     char* argv[]);
+    
+    virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters);
+    
+    virtual void Finalize();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/StoneApplicationContext.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,86 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "StoneApplicationContext.h"
+
+#include <Core/OrthancException.h>
+
+namespace OrthancStone
+{
+  void StoneApplicationContext::InitializeOrthanc()
+  {
+    if (webService_ == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+
+    orthanc_.reset(new Deprecated::OrthancApiClient(*webService_, orthancBaseUrl_));
+  }
+
+
+  boost::shared_ptr<Deprecated::IWebService> StoneApplicationContext::GetWebService()
+  {
+    if (webService_ == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    
+    return webService_;
+  }
+
+  
+  boost::shared_ptr<Deprecated::OrthancApiClient> StoneApplicationContext::GetOrthancApiClient()
+  {
+    if (orthanc_.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    
+    return orthanc_;
+  }
+
+  
+  void StoneApplicationContext::SetWebService(boost::shared_ptr<Deprecated::IWebService> webService)
+  {
+    webService_ = webService;
+    InitializeOrthanc();
+  }
+
+
+  void StoneApplicationContext::SetOrthancBaseUrl(const std::string& baseUrl)
+  {
+    // Make sure the base url ends with "/"
+    if (baseUrl.empty() ||
+        baseUrl[baseUrl.size() - 1] != '/')
+    {
+      orthancBaseUrl_ = baseUrl + "/";
+    }
+    else
+    {
+      orthancBaseUrl_ = baseUrl;
+    }
+
+    if (webService_ != NULL)
+    {
+      InitializeOrthanc();
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/StoneApplicationContext.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,97 @@
+/**
+ * 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 "../Framework/Deprecated/Toolbox/IWebService.h"
+#include "../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
+#include "../Framework/Deprecated/Toolbox/OrthancApiClient.h"
+#include "../Framework/Deprecated/Viewport/WidgetViewport.h"
+
+
+#ifdef _MSC_VER
+  #if _MSC_VER > 1910
+    #define orthanc_override override
+  #else
+    #define orthanc_override
+  #endif
+#elif defined __GNUC__
+  #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+/* Test for GCC > 3.2.0 */
+  #if GCC_VERSION > 40900
+    #define orthanc_override override
+  #else
+    #define orthanc_override
+  #endif
+#else
+    #define orthanc_override
+#endif
+
+#include <list>
+
+namespace OrthancStone
+{
+  // a StoneApplicationContext contains the services that a StoneApplication
+  // uses and that depends on the environment in which the Application executes.
+  // I.e, the StoneApplicationContext provides a WebService interface such that
+  // the StoneApplication can perform HTTP requests.  In a WASM environment,
+  // the WebService is provided by the browser while, in a native environment,
+  // the WebService is provided by the OracleWebService (a C++ Http client)
+
+  class StoneApplicationContext : public boost::noncopyable
+  {
+  private:
+    boost::shared_ptr<Deprecated::IWebService>     webService_;
+    Deprecated::IDelayedCallExecutor*  delayedCallExecutor_;   // TODO => shared_ptr ??
+    boost::shared_ptr<Deprecated::OrthancApiClient>  orthanc_;
+    std::string                      orthancBaseUrl_;
+
+    void InitializeOrthanc();
+
+  public:
+    StoneApplicationContext() :
+      delayedCallExecutor_(NULL)
+    {
+    }
+
+    virtual ~StoneApplicationContext()
+    {
+    }
+
+    boost::shared_ptr<Deprecated::IWebService> GetWebService();
+
+    boost::shared_ptr<Deprecated::OrthancApiClient> GetOrthancApiClient();
+
+    void SetWebService(boost::shared_ptr<Deprecated::IWebService> webService);
+
+    void SetOrthancBaseUrl(const std::string& baseUrl);
+
+    void SetDelayedCallExecutor(Deprecated::IDelayedCallExecutor& delayedCallExecutor)
+    {
+      delayedCallExecutor_ = &delayedCallExecutor;
+    }
+
+    Deprecated::IDelayedCallExecutor& GetDelayedCallExecutor()
+    {
+      return *delayedCallExecutor_;
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Wasm/StartupParametersBuilder.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,71 @@
+#include "StartupParametersBuilder.h"
+#include <iostream>
+#include <cstdio>
+#include "emscripten/html5.h"
+
+namespace OrthancStone
+{
+  void StartupParametersBuilder::Clear()
+  {
+    startupParameters_.clear();
+  }
+
+  void StartupParametersBuilder::SetStartupParameter(
+    const char* name,
+    const char* value)
+  {
+    startupParameters_.push_back(std::make_tuple(name, value));
+  }
+
+  void StartupParametersBuilder::GetStartupParameters(
+    boost::program_options::variables_map& parameters,
+    const boost::program_options::options_description& options) 
+  {
+    std::vector<std::string> argvStrings(startupParameters_.size() + 1);
+    // argv mirrors pointers to the internal argvStrings buffers.
+    // ******************************************************
+    // THIS IS HIGHLY DANGEROUS SO BEWARE!!!!!!!!!!!!!!
+    // ******************************************************
+    std::vector<const char*> argv(startupParameters_.size() + 1);
+    
+    int argCounter = 0;
+    argvStrings[argCounter] = "dummy.exe";
+    argv[argCounter] = argvStrings[argCounter].c_str();
+    
+    argCounter++;
+    
+    std::string cmdLine = "";
+    for ( StartupParameters::const_iterator it = startupParameters_.begin(); 
+          it != startupParameters_.end(); 
+          it++)
+    {
+      std::stringstream argSs;
+
+      argSs << "--" << std::get<0>(*it);
+      if(std::get<1>(*it).length() > 0)
+        argSs << "=" << std::get<1>(*it);
+      
+      argvStrings[argCounter] = argSs.str();
+      cmdLine = cmdLine + " " + argvStrings[argCounter];
+      std::cout << cmdLine << std::endl;
+      argv[argCounter] = argvStrings[argCounter].c_str();
+      argCounter++;
+    }
+
+
+    std::cout << "simulated cmdLine = \"" << cmdLine.c_str() << "\"\n";
+
+    try
+    {
+      boost::program_options::store(
+        boost::program_options::command_line_parser(argCounter, argv.data()).
+          options(options).allow_unregistered().run(), parameters);
+      boost::program_options::notify(parameters);
+    }
+    catch (boost::program_options::error& e)
+    {
+      std::cerr << "Error while parsing the command-line arguments: " <<
+        e.what() << std::endl;    
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Applications/Wasm/StartupParametersBuilder.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,54 @@
+/**
+ * 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 <boost/program_options.hpp>
+#include <tuple>
+
+#if ORTHANC_ENABLE_SDL == 1
+#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 0
+#endif
+
+namespace OrthancStone
+{
+  // This class is used to generate boost program options from a dico.
+  // In a Wasm context, startup options are passed as URI arguments that
+  // are then passed to this class as a dico.
+  // This class regenerates a fake command-line and parses it to produce
+  // the same output as if the app was started at command-line.
+  class StartupParametersBuilder
+  {
+    typedef std::list<std::tuple<std::string, std::string>> StartupParameters;
+    StartupParameters startupParameters_;
+
+  public:
+
+    void Clear();
+    // Please note that if a parameter is a flag-style one, the value that 
+    // is passed should be an empty string
+    void SetStartupParameter(const char* name, const char* value);
+    void GetStartupParameters(
+      boost::program_options::variables_map& parameters_, 
+      const boost::program_options::options_description& options);
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/DelayedCallCommand.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,64 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "DelayedCallCommand.h"
+#include "boost/thread/thread.hpp"
+
+#include <iostream>
+
+namespace Deprecated
+{
+  DelayedCallCommand::DelayedCallCommand(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,  // takes ownership
+                                         unsigned int timeoutInMs,
+                                         Orthanc::IDynamicObject* payload /* takes ownership */,
+                                         OrthancStone::NativeStoneApplicationContext& context
+                                         ) :
+    callback_(callback),
+    payload_(payload),
+    context_(context),
+    expirationTimePoint_(boost::posix_time::microsec_clock::local_time() + boost::posix_time::milliseconds(timeoutInMs)),
+    timeoutInMs_(timeoutInMs)
+  {
+  }
+
+
+  void DelayedCallCommand::Execute()
+  {
+    while (boost::posix_time::microsec_clock::local_time() < expirationTimePoint_)
+    {
+      boost::this_thread::sleep(boost::posix_time::milliseconds(1));
+    }
+  }
+
+  void DelayedCallCommand::Commit()
+  {
+    // We want to make sure that, i.e, the UpdateThread is not
+    // triggered while we are updating the "model" with the result of
+    // an OracleCommand
+    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_);
+
+    if (callback_.get() != NULL)
+    {
+      IDelayedCallExecutor::TimeoutMessage message; // TODO: add payload
+      callback_->Apply(message);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/DelayedCallCommand.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,56 @@
+/**
+ * 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 "IOracleCommand.h"
+
+#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
+#include "../../Framework/Messages/IObservable.h"
+#include "../../Framework/Messages/ICallable.h"
+#include "../../Applications/Generic/NativeStoneApplicationContext.h"
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+namespace Deprecated
+{
+  class DelayedCallCommand : public IOracleCommand, OrthancStone::IObservable
+  {
+  protected:
+    std::unique_ptr<MessageHandler<IDelayedCallExecutor::TimeoutMessage> >  callback_;
+    std::unique_ptr<Orthanc::IDynamicObject>  payload_;
+    OrthancStone::NativeStoneApplicationContext&          context_;
+    boost::posix_time::ptime                expirationTimePoint_;
+    unsigned int                            timeoutInMs_;
+
+  public:
+    DelayedCallCommand(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,  // takes ownership
+                       unsigned int timeoutInMs,
+                       Orthanc::IDynamicObject* payload /* takes ownership */,
+                       OrthancStone::NativeStoneApplicationContext& context
+                       );
+
+    virtual void Execute();
+
+    virtual void Commit();
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/IOracleCommand.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,42 @@
+/**
+ * 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 <Core/IDynamicObject.h>
+
+namespace Deprecated
+{
+  class IOracleCommand : public Orthanc::IDynamicObject
+  {
+  public:
+    virtual ~IOracleCommand()
+    {
+    }
+
+    // This part of the command can be invoked simultaneously, and
+    // must not modify the Stone context
+    virtual void Execute() = 0;
+
+    // This part of the command must be invoked in mutual exclusion
+    virtual void Commit() = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/Oracle.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,212 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "Oracle.h"
+
+#include <Core/Logging.h>
+#include <Core/MultiThreading/SharedMessageQueue.h>
+#include <Core/OrthancException.h>
+
+#include <vector>
+#include <stdio.h>
+#include <boost/thread/mutex.hpp>
+
+namespace Deprecated
+{
+  class Oracle::PImpl
+  {
+  private:
+    enum State
+    {
+      State_Init,
+      State_Started,
+      State_Stopped
+    };
+
+    boost::mutex                   oracleMutex_;
+    State                          state_;
+    std::vector<boost::thread*>    threads_;
+    Orthanc::SharedMessageQueue    queue_;
+
+    static void Worker(PImpl* that)
+    {
+      for (;;)
+      {
+        State state;
+        
+        {
+          boost::mutex::scoped_lock lock(that->oracleMutex_);
+          state = that->state_;
+        }
+
+        if (state == State_Stopped)
+        {
+          break;
+        }
+
+        std::unique_ptr<Orthanc::IDynamicObject> item(that->queue_.Dequeue(100));
+        if (item.get() != NULL)
+        {
+          IOracleCommand& command = dynamic_cast<IOracleCommand&>(*item);
+          try
+          {
+            command.Execute();
+          }
+          catch (Orthanc::OrthancException& /*ex*/)
+          {
+            // this is probably a curl error that has been triggered.  We may just ignore it.
+            // The command.success_ will stay at false and this will be handled in the command.Commit
+          }
+
+          // Random sleeping to test
+          //boost::this_thread::sleep(boost::posix_time::milliseconds(50 * (1 + rand() % 10)));
+
+          command.Commit();
+        }
+      }
+    }
+    
+  public:
+    PImpl(unsigned int threadCount) :
+      state_(State_Init),
+      threads_(threadCount)
+    {
+    }
+
+    ~PImpl()
+    {
+      if (state_ == State_Started)
+      {
+        LOG(ERROR) << "You should have manually called Oracle::Stop()";
+        Stop();
+      }
+    }
+
+    Orthanc::SharedMessageQueue& GetQueue()
+    {
+      return queue_;
+    }
+
+    void Submit(IOracleCommand* command)
+    {
+      std::unique_ptr<IOracleCommand> protection(command);
+
+      if (command == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+      
+      boost::mutex::scoped_lock lock(oracleMutex_);
+
+      switch (state_)
+      {
+        case State_Init:
+        case State_Started:
+          queue_.Enqueue(protection.release());
+          break;
+
+        case State_Stopped:
+          LOG(ERROR) << "Cannot schedule a request to the Oracle after having "
+                     << "called Oracle::Stop()";
+          break;
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+      
+    }
+
+    void Start()
+    {
+      boost::mutex::scoped_lock lock(oracleMutex_);
+
+      if (state_ != State_Init)
+      {
+        LOG(ERROR) << "Oracle::PImpl::Start: (state_ != State_Init)";
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+      
+      for (size_t i = 0; i < threads_.size(); i++)
+      {
+        threads_[i] = new boost::thread(Worker, this);
+      }
+
+      state_ = State_Started;
+    }
+
+    void Stop()
+    {
+      {
+        boost::mutex::scoped_lock lock(oracleMutex_);
+
+        if (state_ != State_Started)
+        {
+          LOG(ERROR) << "Oracle::PImpl::Stop(): (state_ != State_Started)";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+        }
+
+        state_ = State_Stopped;
+      }
+      
+      for (size_t i = 0; i < threads_.size(); i++)
+      {
+        if (threads_[i] != NULL)
+        {
+          if (threads_[i]->joinable())
+          {
+            threads_[i]->join();
+          }
+
+          delete threads_[i];
+        }
+      }
+    }
+  };
+  
+
+  Oracle::Oracle(unsigned int threadCount) :
+    pimpl_(new PImpl(threadCount))
+  {
+  }
+
+  void Oracle::Start()
+  {
+    pimpl_->Start();
+  }
+
+
+  void Oracle::Submit(IOracleCommand* command)
+  {
+    pimpl_->Submit(command);
+  }
+     
+   
+  void Oracle::Stop()
+  {
+    pimpl_->Stop();
+  }
+
+
+  void Oracle::WaitEmpty()
+  {
+    pimpl_->GetQueue().WaitEmpty(50);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/Oracle.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,48 @@
+/**
+ * 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 "IOracleCommand.h"
+
+#include <boost/shared_ptr.hpp>
+
+namespace Deprecated
+{
+  class Oracle : public boost::noncopyable
+  {
+  private:
+    class PImpl;
+  
+    boost::shared_ptr<PImpl>  pimpl_;
+
+  public:
+    Oracle(unsigned int threadCount);
+
+    void Start();
+
+    void Submit(IOracleCommand* command);
+        
+    void WaitEmpty();  // For unit tests
+
+    void Stop();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/OracleDelayedCallExecutor.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,52 @@
+/**
+ * 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 "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
+#include "Oracle.h"
+#include "../../Applications/Generic/NativeStoneApplicationContext.h"
+#include "DelayedCallCommand.h"
+
+namespace Deprecated
+{
+  // The OracleTimeout executes callbacks after a delay.
+  class OracleDelayedCallExecutor : public IDelayedCallExecutor
+  {
+  private:
+    Oracle&                        oracle_;
+    OrthancStone::NativeStoneApplicationContext& context_;
+
+  public:
+    OracleDelayedCallExecutor(Oracle& oracle,
+                              OrthancStone::NativeStoneApplicationContext& context) :
+      oracle_(oracle),
+      context_(context)
+    {
+    }
+
+    virtual void Schedule(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
+                          unsigned int timeoutInMs = 1000)
+    {
+      oracle_.Submit(new DelayedCallCommand(callback, timeoutInMs, NULL, context_));
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/OracleWebService.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,80 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "OracleWebService.h"
+#include "../../Framework/Deprecated/Toolbox/IWebService.h"
+
+namespace Deprecated
+{
+
+
+  class OracleWebService::WebServiceCachedGetCommand : public IOracleCommand, OrthancStone::IObservable
+  {
+  protected:
+    std::unique_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> >  successCallback_;
+    std::unique_ptr<Orthanc::IDynamicObject>                                  payload_;
+    boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage>      cachedMessage_;
+    OrthancStone::NativeStoneApplicationContext&                                          context_;
+
+  public:
+    WebServiceCachedGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                               boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
+                               Orthanc::IDynamicObject* payload /* takes ownership */,
+                               OrthancStone::NativeStoneApplicationContext& context
+                               ) :
+      successCallback_(successCallback),
+      payload_(payload),
+      cachedMessage_(cachedMessage),
+      context_(context)
+    {
+    }
+
+    virtual void Execute()
+    {
+      // nothing to do, everything is in the commit
+    }
+
+    virtual void Commit()
+    {
+      // We want to make sure that, i.e, the UpdateThread is not
+      // triggered while we are updating the "model" with the result of
+      // a WebServiceCommand
+      OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_);
+
+      IWebService::HttpRequestSuccessMessage successMessage(cachedMessage_->GetUri(),
+                                                            cachedMessage_->GetAnswer(),
+                                                            cachedMessage_->GetAnswerSize(),
+                                                            cachedMessage_->GetAnswerHttpHeaders(),
+                                                            payload_.get());
+
+      successCallback_->Apply(successMessage);
+    }
+  };
+
+  void OracleWebService::NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
+                                                Orthanc::IDynamicObject* payload, // takes ownership
+                                                MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback)
+  {
+    oracle_.Submit(new WebServiceCachedGetCommand(successCallback, cachedMessage, payload, context_));
+  }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/OracleWebService.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,92 @@
+/**
+ * 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 "../../Framework/Deprecated/Toolbox/BaseWebService.h"
+#include "Oracle.h"
+#include "WebServiceGetCommand.h"
+#include "WebServicePostCommand.h"
+#include "WebServiceDeleteCommand.h"
+#include "../../Applications/Generic/NativeStoneApplicationContext.h"
+
+namespace Deprecated
+{
+  // The OracleWebService performs HTTP requests in a native environment.
+  // It uses a thread pool to handle multiple HTTP requests in a same time.
+  // It works asynchronously to mimick the behaviour of the WebService running in a WASM environment.
+  class OracleWebService : public BaseWebService
+  {
+  private:
+    Oracle&                        oracle_;
+    OrthancStone::NativeStoneApplicationContext& context_;
+    Orthanc::WebServiceParameters  parameters_;
+
+    class WebServiceCachedGetCommand;
+
+  public:
+    OracleWebService(Oracle& oracle,
+                     const Orthanc::WebServiceParameters& parameters,
+                     OrthancStone::NativeStoneApplicationContext& context) :
+      oracle_(oracle),
+      context_(context),
+      parameters_(parameters)
+    {
+    }
+
+    virtual void PostAsync(const std::string& uri,
+                           const HttpHeaders& headers,
+                           const std::string& body,
+                           Orthanc::IDynamicObject* payload, // takes ownership
+                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership
+                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL, // takes ownership
+                           unsigned int timeoutInSeconds = 60)
+    {
+      oracle_.Submit(new WebServicePostCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, body, payload, context_));
+    }
+
+    virtual void DeleteAsync(const std::string& uri,
+                             const HttpHeaders& headers,
+                             Orthanc::IDynamicObject* payload,
+                             MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
+                             MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                             unsigned int timeoutInSeconds = 60)
+    {
+      oracle_.Submit(new WebServiceDeleteCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_));
+    }
+
+  protected:
+    virtual void GetAsyncInternal(const std::string& uri,
+                                  const HttpHeaders& headers,
+                                  Orthanc::IDynamicObject* payload, // takes ownership
+                                  MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,   // takes ownership
+                                  MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,// takes ownership
+                                  unsigned int timeoutInSeconds = 60)
+    {
+      oracle_.Submit(new WebServiceGetCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_));
+    }
+
+    virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage,
+                                        Orthanc::IDynamicObject* payload, // takes ownership
+                                        MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback);
+
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServiceCommandBase.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,69 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "WebServiceCommandBase.h"
+
+#include <Core/HttpClient.h>
+
+namespace Deprecated
+{
+  WebServiceCommandBase::WebServiceCommandBase(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
+                                               MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
+                                               const Orthanc::WebServiceParameters& parameters,
+                                               const std::string& url,
+                                               const IWebService::HttpHeaders& headers,
+                                               unsigned int timeoutInSeconds,
+                                               Orthanc::IDynamicObject* payload /* takes ownership */,
+                                               OrthancStone::NativeStoneApplicationContext& context) :
+    successCallback_(successCallback),
+    failureCallback_(failureCallback),
+    parameters_(parameters),
+    url_(url),
+    headers_(headers),
+    payload_(payload),
+    success_(false),
+    httpStatus_(Orthanc::HttpStatus_None),
+    context_(context),
+    timeoutInSeconds_(timeoutInSeconds)
+  {
+  }
+
+
+  void WebServiceCommandBase::Commit()
+  {
+    // We want to make sure that, i.e, the UpdateThread is not
+    // triggered while we are updating the "model" with the result of
+    // a WebServiceCommand
+    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); 
+
+    if (success_ && successCallback_.get() != NULL)
+    {
+      IWebService::HttpRequestSuccessMessage message
+        (url_, answer_.c_str(), answer_.size(), answerHeaders_, payload_.get());
+      successCallback_->Apply(message);
+    }
+    else if (!success_ && failureCallback_.get() != NULL)
+    {
+      IWebService::HttpRequestErrorMessage message(url_, httpStatus_, payload_.get());
+      failureCallback_->Apply(message);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServiceCommandBase.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,69 @@
+/**
+ * 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 "IOracleCommand.h"
+
+#include "../../Framework/Deprecated/Toolbox/IWebService.h"
+#include "../../Framework/Messages/IObservable.h"
+#include "../../Framework/Messages/ICallable.h"
+#include "../../Applications/Generic/NativeStoneApplicationContext.h"
+
+#include <Core/WebServiceParameters.h>
+
+#include <memory>
+
+namespace Deprecated
+{
+  class WebServiceCommandBase : public IOracleCommand, OrthancStone::IObservable
+  {
+  protected:
+    std::unique_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> >  successCallback_;
+    std::unique_ptr<MessageHandler<IWebService::HttpRequestErrorMessage> >    failureCallback_;
+    Orthanc::WebServiceParameters           parameters_;
+    std::string                             url_;
+    IWebService::HttpHeaders                headers_;
+    std::unique_ptr<Orthanc::IDynamicObject>  payload_;
+    bool                                    success_;
+    Orthanc::HttpStatus                     httpStatus_;
+    std::string                             answer_;
+    IWebService::HttpHeaders                answerHeaders_;
+    OrthancStone::NativeStoneApplicationContext&          context_;
+    unsigned int                            timeoutInSeconds_;
+
+  public:
+    WebServiceCommandBase(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+                          const Orthanc::WebServiceParameters& parameters,
+                          const std::string& url,
+                          const IWebService::HttpHeaders& headers,
+                          unsigned int timeoutInSeconds,
+                          Orthanc::IDynamicObject* payload /* takes ownership */,
+                          OrthancStone::NativeStoneApplicationContext& context
+                          );
+
+    virtual void Execute() = 0;
+
+    virtual void Commit();
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServiceDeleteCommand.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,56 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "WebServiceDeleteCommand.h"
+
+#include <Core/HttpClient.h>
+
+namespace Deprecated
+{
+  WebServiceDeleteCommand::WebServiceDeleteCommand(MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                                                   MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+                                                   const Orthanc::WebServiceParameters& parameters,
+                                                   const std::string& url,
+                                                   const Deprecated::IWebService::HttpHeaders& headers,
+                                                   unsigned int timeoutInSeconds,
+                                                   Orthanc::IDynamicObject* payload /* takes ownership */,
+                                                   OrthancStone::NativeStoneApplicationContext& context) :
+    WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context)
+  {
+  }
+
+  void WebServiceDeleteCommand::Execute()
+  {
+    Orthanc::HttpClient client(parameters_, "/");
+    client.SetUrl(url_);
+    client.SetTimeout(timeoutInSeconds_);
+    client.SetMethod(Orthanc::HttpMethod_Delete);
+
+    for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
+    {
+      client.AddHeader(it->first, it->second);
+    }
+
+    success_ = client.Apply(answer_, answerHeaders_);
+    httpStatus_ = client.GetLastStatus();
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServiceDeleteCommand.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,42 @@
+/**
+ * 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 "WebServiceCommandBase.h"
+
+namespace Deprecated
+{
+  class WebServiceDeleteCommand : public WebServiceCommandBase
+  {
+  public:
+    WebServiceDeleteCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                            MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+                            const Orthanc::WebServiceParameters& parameters,
+                            const std::string& url,
+                            const IWebService::HttpHeaders& headers,
+                            unsigned int timeoutInSeconds,
+                            Orthanc::IDynamicObject* payload /* takes ownership */,
+                            OrthancStone::NativeStoneApplicationContext& context);
+
+    virtual void Execute();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServiceGetCommand.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,57 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "WebServiceGetCommand.h"
+
+#include <Core/HttpClient.h>
+
+namespace Deprecated
+{
+  WebServiceGetCommand::WebServiceGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                                             MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+                                             const Orthanc::WebServiceParameters& parameters,
+                                             const std::string& url,
+                                             const IWebService::HttpHeaders& headers,
+                                             unsigned int timeoutInSeconds,
+                                             Orthanc::IDynamicObject* payload /* takes ownership */,
+                                             OrthancStone::NativeStoneApplicationContext& context) :
+    WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context)
+  {
+  }
+
+
+  void WebServiceGetCommand::Execute()
+  {
+    Orthanc::HttpClient client(parameters_, "/");
+    client.SetUrl(url_);
+    client.SetTimeout(timeoutInSeconds_);
+    client.SetMethod(Orthanc::HttpMethod_Get);
+
+    for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
+    {
+      client.AddHeader(it->first, it->second);
+    }
+
+    success_ = client.Apply(answer_, answerHeaders_);
+    httpStatus_ = client.GetLastStatus();
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServiceGetCommand.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,43 @@
+/**
+ * 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 "WebServiceCommandBase.h"
+
+namespace Deprecated
+{
+  class WebServiceGetCommand : public WebServiceCommandBase
+  {
+  public:
+    WebServiceGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                         MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+                         const Orthanc::WebServiceParameters& parameters,
+                         const std::string& url,
+                         const IWebService::HttpHeaders& headers,
+                         unsigned int timeoutInSeconds,
+                         Orthanc::IDynamicObject* payload /* takes ownership */,
+                         OrthancStone::NativeStoneApplicationContext& context);
+
+    virtual void Execute();
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServicePostCommand.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,58 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "WebServicePostCommand.h"
+
+#include <Core/HttpClient.h>
+
+namespace Deprecated
+{
+  WebServicePostCommand::WebServicePostCommand(MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                                               MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+                                               const Orthanc::WebServiceParameters& parameters,
+                                               const std::string& url,
+                                               const Deprecated::IWebService::HttpHeaders& headers,
+                                               unsigned int timeoutInSeconds,
+                                               const std::string& body,
+                                               Orthanc::IDynamicObject* payload /* takes ownership */,
+                                               OrthancStone::NativeStoneApplicationContext& context) :
+    WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context),
+    body_(body)
+  {
+  }
+
+  void WebServicePostCommand::Execute()
+  {
+    Orthanc::HttpClient client(parameters_, "/");
+    client.SetUrl(url_);
+    client.SetTimeout(timeoutInSeconds_);
+    client.SetMethod(Orthanc::HttpMethod_Post);
+    client.GetBody().swap(body_);
+
+    for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
+    {
+      client.AddHeader(it->first, it->second);
+    }
+
+    success_ = client.Apply(answer_, answerHeaders_);
+    httpStatus_ = client.GetLastStatus();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Generic/WebServicePostCommand.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,46 @@
+/**
+ * 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 "WebServiceCommandBase.h"
+
+namespace Deprecated
+{
+  class WebServicePostCommand : public WebServiceCommandBase
+  {
+  protected:
+    std::string  body_;
+
+  public:
+    WebServicePostCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+                          const Orthanc::WebServiceParameters& parameters,
+                          const std::string& url,
+                          const IWebService::HttpHeaders& headers,
+                          unsigned int timeoutInSeconds,
+                          const std::string& body,
+                          Orthanc::IDynamicObject* payload /* takes ownership */,
+                          OrthancStone::NativeStoneApplicationContext& context);
+
+    virtual void Execute();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/Defaults.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,416 @@
+#include "Defaults.h"
+
+#include "WasmWebService.h"
+#include "WasmDelayedCallExecutor.h"
+#include "../../Framework/Deprecated/Widgets/TestCairoWidget.h"
+#include <Framework/Deprecated/Viewport/WidgetViewport.h>
+#include <Applications/Wasm/StartupParametersBuilder.h>
+#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h>
+#include <Framework/StoneInitialization.h>
+#include <Core/Logging.h>
+#include <sstream>
+
+#include <algorithm>
+
+
+static unsigned int width_ = 0;
+static unsigned int height_ = 0;
+
+/**********************************/
+
+static std::unique_ptr<OrthancStone::IStoneApplication> application;
+static std::unique_ptr<OrthancStone::WasmPlatformApplicationAdapter> applicationWasmAdapter = NULL;
+static std::unique_ptr<OrthancStone::StoneApplicationContext> context;
+static OrthancStone::StartupParametersBuilder startupParametersBuilder;
+static OrthancStone::MessageBroker broker;
+
+static OrthancStone::ViewportContentChangedObserver viewportContentChangedObserver_(broker);
+static OrthancStone::StatusBar statusBar_;
+
+static std::list<std::shared_ptr<Deprecated::WidgetViewport>> viewports_;
+
+std::shared_ptr<Deprecated::WidgetViewport> FindViewportSharedPtr(ViewportHandle viewport) {
+  for (const auto& v : viewports_) {
+    if (v.get() == viewport) {
+      return v;
+    }
+  }
+  assert(false);
+  return std::shared_ptr<Deprecated::WidgetViewport>();
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if 0
+  // rewrite malloc/free in order to monitor allocations.  We actually only monitor large allocations (like images ...)
+
+  size_t bigChunksTotalSize = 0;
+  std::map<void*, size_t> allocatedBigChunks;
+
+  extern void* emscripten_builtin_malloc(size_t bytes);
+  extern void emscripten_builtin_free(void* mem);
+
+  void * __attribute__((noinline)) malloc(size_t size)
+  {
+    void *ptr = emscripten_builtin_malloc(size);
+    if (size > 100000)
+    {
+      bigChunksTotalSize += size;
+      printf("++ Allocated %zu bytes, got %p. (%zu MB consumed by big chunks)\n", size, ptr, bigChunksTotalSize/(1024*1024));
+      allocatedBigChunks[ptr] = size;
+    }
+    return ptr;
+  }
+
+  void __attribute__((noinline)) free(void *ptr)
+  {
+    emscripten_builtin_free(ptr);
+
+    std::map<void*, size_t>::iterator it = allocatedBigChunks.find(ptr);
+    if (it != allocatedBigChunks.end())
+    {
+      bigChunksTotalSize -= it->second;
+      printf("--     Freed %zu bytes at %p.   (%zu MB consumed by big chunks)\n", it->second, ptr, bigChunksTotalSize/(1024*1024));
+      allocatedBigChunks.erase(it);
+    }
+  }
+#endif // 0
+
+  using namespace OrthancStone;
+
+  // when WASM needs a C++ viewport
+  ViewportHandle EMSCRIPTEN_KEEPALIVE CreateCppViewport() {
+    
+    std::shared_ptr<Deprecated::WidgetViewport> viewport(new Deprecated::WidgetViewport(broker));
+    printf("viewport %x\n", (int)viewport.get());
+
+    viewports_.push_back(viewport);
+
+    printf("There are now %lu viewports in C++\n", viewports_.size());
+
+    viewport->SetStatusBar(statusBar_);
+
+    viewport->RegisterObserverCallback(
+      new Callable<ViewportContentChangedObserver, Deprecated::IViewport::ViewportChangedMessage>
+      (viewportContentChangedObserver_, &ViewportContentChangedObserver::OnViewportChanged));
+
+    return viewport.get();
+  }
+
+  // when WASM does not need a viewport anymore, it should release it 
+  void EMSCRIPTEN_KEEPALIVE ReleaseCppViewport(ViewportHandle viewport) {
+    viewports_.remove_if([viewport](const std::shared_ptr<Deprecated::WidgetViewport>& v) { return v.get() == viewport;});
+
+    printf("There are now %lu viewports in C++\n", viewports_.size());
+  }
+
+  void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle viewport) {
+    printf("Initializing Stone\n");
+    OrthancStone::StoneInitialize();
+    printf("CreateWasmApplication\n");
+
+    application.reset(CreateUserApplication(broker));
+    applicationWasmAdapter.reset(CreateWasmApplicationAdapter(broker, application.get())); 
+    Deprecated::WasmWebService::SetBroker(broker);
+    Deprecated::WasmDelayedCallExecutor::SetBroker(broker);
+
+    startupParametersBuilder.Clear();
+  }
+
+  void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc,
+                                                  const char* value) {
+    startupParametersBuilder.SetStartupParameter(keyc, value);
+  }
+
+  void EMSCRIPTEN_KEEPALIVE StartWasmApplication(const char* baseUri) {
+
+    printf("StartWasmApplication\n");
+
+    Orthanc::Logging::SetErrorWarnInfoTraceLoggingFunctions(
+      stone_console_error, stone_console_warning,
+      stone_console_info, stone_console_trace);
+
+    // recreate a command line from uri arguments and parse it
+    boost::program_options::variables_map parameters;
+    boost::program_options::options_description options;
+    application->DeclareStartupOptions(options);
+    startupParametersBuilder.GetStartupParameters(parameters, options);
+
+    context.reset(new OrthancStone::StoneApplicationContext(broker));
+    context->SetOrthancBaseUrl(baseUri);
+    printf("Base URL to Orthanc API: [%s]\n", baseUri);
+    context->SetWebService(Deprecated::WasmWebService::GetInstance());
+    context->SetDelayedCallExecutor(Deprecated::WasmDelayedCallExecutor::GetInstance());
+    application->Initialize(context.get(), statusBar_, parameters);
+    application->InitializeWasm();
+
+//    viewport->SetSize(width_, height_);
+    printf("StartWasmApplication - completed\n");
+  }
+  
+  bool EMSCRIPTEN_KEEPALIVE WasmIsTraceLevelEnabled()
+  {
+    return Orthanc::Logging::IsTraceLevelEnabled();
+  }
+
+  bool EMSCRIPTEN_KEEPALIVE WasmIsInfoLevelEnabled()
+  {
+    return Orthanc::Logging::IsInfoLevelEnabled();
+  }
+  
+  void EMSCRIPTEN_KEEPALIVE WasmDoAnimation()
+  {
+    for (auto viewport : viewports_) {
+      // TODO Only launch the JavaScript timer if "HasAnimation()"
+      if (viewport->HasAnimation())
+      {
+        viewport->DoAnimation();
+      }
+
+    }
+
+  }
+  
+
+  void EMSCRIPTEN_KEEPALIVE ViewportSetSize(ViewportHandle viewport, unsigned int width, unsigned int height)
+  {
+    width_ = width;
+    height_ = height;
+    
+    viewport->SetSize(width, height);
+  }
+
+  int EMSCRIPTEN_KEEPALIVE ViewportRender(ViewportHandle viewport,
+                                          unsigned int width,
+                                          unsigned int height,
+                                          uint8_t* data)
+  {
+    viewportContentChangedObserver_.Reset();
+
+    //printf("ViewportRender called %dx%d\n", width, height);
+    if (width == 0 ||
+        height == 0)
+    {
+      return 1;
+    }
+
+    Orthanc::ImageAccessor surface;
+    surface.AssignWritable(Orthanc::PixelFormat_BGRA32, width, height, 4 * width, data);
+
+    viewport->Render(surface);
+
+    // Convert from BGRA32 memory layout (only color mode supported by
+    // Cairo, which corresponds to CAIRO_FORMAT_ARGB32) to RGBA32 (as
+    // expected by HTML5 canvas). This simply amounts to swapping the
+    // B and R channels.
+    uint8_t* p = data;
+    for (unsigned int y = 0; y < height; y++) {
+      for (unsigned int x = 0; x < width; x++) {
+        uint8_t tmp = p[0];
+        p[0] = p[2];
+        p[2] = tmp;
+        
+        p += 4;
+      }
+    }
+
+    return 1;
+  }
+
+
+  void EMSCRIPTEN_KEEPALIVE ViewportMouseDown(ViewportHandle viewport,
+                                              unsigned int rawButton,
+                                              int x,
+                                              int y,
+                                              unsigned int rawModifiers)
+  {
+    OrthancStone::MouseButton button;
+    switch (rawButton)
+    {
+      case 0:
+        button = OrthancStone::MouseButton_Left;
+        break;
+
+      case 1:
+        button = OrthancStone::MouseButton_Middle;
+        break;
+
+      case 2:
+        button = OrthancStone::MouseButton_Right;
+        break;
+
+      default:
+        return;  // Unknown button
+    }
+
+    viewport->MouseDown(button, x, y, OrthancStone::KeyboardModifiers_None, std::vector<Deprecated::Touch>());
+  }
+  
+
+  void EMSCRIPTEN_KEEPALIVE ViewportMouseWheel(ViewportHandle viewport,
+                                               int deltaY,
+                                               int x,
+                                               int y,
+                                               int isControl)
+  {
+    if (deltaY != 0)
+    {
+      OrthancStone::MouseWheelDirection direction = (deltaY < 0 ?
+                                                     OrthancStone::MouseWheelDirection_Up :
+                                                     OrthancStone::MouseWheelDirection_Down);
+      OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None;
+
+      if (isControl != 0)
+      {
+        modifiers = OrthancStone::KeyboardModifiers_Control;
+      }
+
+      viewport->MouseWheel(direction, x, y, modifiers);
+    }
+  }
+  
+
+  void EMSCRIPTEN_KEEPALIVE ViewportMouseMove(ViewportHandle viewport,
+                                              int x,
+                                              int y)
+  {
+    viewport->MouseMove(x, y, std::vector<Deprecated::Touch>());
+  }
+
+  void GetTouchVector(std::vector<Deprecated::Touch>& output,
+                      int touchCount,
+                      float x0,
+                      float y0,
+                      float x1,
+                      float y1,
+                      float x2,
+                      float y2)
+  {
+    // TODO: it might be nice to try to pass all the x0,y0 coordinates as arrays but that's not so easy to pass array between JS and C++
+    if (touchCount > 0)
+    {
+      output.push_back(Deprecated::Touch(x0, y0));
+    }
+    if (touchCount > 1)
+    {
+      output.push_back(Deprecated::Touch(x1, y1));
+    }
+    if (touchCount > 2)
+    {
+      output.push_back(Deprecated::Touch(x2, y2));
+    }
+
+  }
+
+  void EMSCRIPTEN_KEEPALIVE ViewportTouchStart(ViewportHandle viewport,
+                                              int touchCount,
+                                              float x0,
+                                              float y0,
+                                              float x1,
+                                              float y1,
+                                              float x2,
+                                              float y2)
+  {
+    // printf("touch start with %d touches\n", touchCount);
+
+    std::vector<Deprecated::Touch> touches;
+    GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
+    viewport->TouchStart(touches);
+  }
+
+  void EMSCRIPTEN_KEEPALIVE ViewportTouchMove(ViewportHandle viewport,
+                                              int touchCount,
+                                              float x0,
+                                              float y0,
+                                              float x1,
+                                              float y1,
+                                              float x2,
+                                              float y2)
+  {
+    // printf("touch move with %d touches\n", touchCount);
+
+    std::vector<Deprecated::Touch> touches;
+    GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
+    viewport->TouchMove(touches);
+  }
+
+  void EMSCRIPTEN_KEEPALIVE ViewportTouchEnd(ViewportHandle viewport,
+                                              int touchCount,
+                                              float x0,
+                                              float y0,
+                                              float x1,
+                                              float y1,
+                                              float x2,
+                                              float y2)
+  {
+    // printf("touch end with %d touches remaining\n", touchCount);
+
+    std::vector<Deprecated::Touch> touches;
+    GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
+    viewport->TouchEnd(touches);
+  }
+
+  void EMSCRIPTEN_KEEPALIVE ViewportKeyPressed(ViewportHandle viewport,
+                                               int key,
+                                               const char* keyChar, 
+                                               bool isShiftPressed, 
+                                               bool isControlPressed,
+                                               bool isAltPressed)
+                                               
+  {
+    OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None;
+    if (isShiftPressed) {
+      modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Shift);
+    }
+    if (isControlPressed) {
+      modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Control);
+    }
+    if (isAltPressed) {
+      modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Alt);
+    }
+
+    char c = 0;
+    if (keyChar != NULL && key == OrthancStone::KeyboardKeys_Generic) {
+      c = keyChar[0];
+    }
+    viewport->KeyPressed(static_cast<OrthancStone::KeyboardKeys>(key), c, modifiers);
+  }
+  
+
+  void EMSCRIPTEN_KEEPALIVE ViewportMouseUp(ViewportHandle viewport)
+  {
+    viewport->MouseUp();
+  }
+  
+
+  void EMSCRIPTEN_KEEPALIVE ViewportMouseEnter(ViewportHandle viewport)
+  {
+    viewport->MouseEnter();
+  }
+  
+
+  void EMSCRIPTEN_KEEPALIVE ViewportMouseLeave(ViewportHandle viewport)
+  {
+    viewport->MouseLeave();
+  }
+
+  const char* EMSCRIPTEN_KEEPALIVE SendSerializedMessageToStoneApplication(const char* message) 
+  {
+    static std::string output; // we don't want the string to be deallocated when we return to JS code so we always use the same string (this is fine since JS is single-thread)
+
+    //printf("SendSerializedMessageToStoneApplication\n");
+    //printf("%s", message);
+
+    if (applicationWasmAdapter.get() != NULL) {
+      applicationWasmAdapter->HandleSerializedMessageFromWeb(output, std::string(message));
+      return output.c_str();
+    }
+    printf("This Stone application does not have a Web Adapter, unable to send messages");
+    return NULL;
+  }
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/Defaults.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,82 @@
+#pragma once
+
+#include <emscripten/emscripten.h>
+
+#include "../../Framework/Deprecated/Viewport/WidgetViewport.h"
+#include "../../Framework/Deprecated/Widgets/LayoutWidget.h"
+#include <Applications/IStoneApplication.h>
+#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h>
+
+typedef Deprecated::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+  
+  // JS methods accessible from C++
+  extern void ScheduleWebViewportRedrawFromCpp(ViewportHandle cppViewportHandle);
+  extern void UpdateStoneApplicationStatusFromCppWithString(const char* statusUpdateMessage);
+  extern void UpdateStoneApplicationStatusFromCppWithSerializedMessage(const char* statusUpdateMessage);
+  extern void stone_console_error(const char*);
+  extern void stone_console_warning(const char*);
+  extern void stone_console_info(const char*);
+  extern void stone_console_trace(const char*);
+
+  // C++ methods accessible from JS
+  extern void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle cppViewportHandle);
+  extern void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc, const char* value);
+  
+
+#ifdef __cplusplus
+}
+#endif
+
+// these methods must be implemented in the custom app "mainWasm.cpp"
+extern OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker);
+extern OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application);
+
+namespace OrthancStone {
+
+  // default Observer to trigger Viewport redraw when something changes in the Viewport
+  class ViewportContentChangedObserver : public IObserver
+  {
+  private:
+    // Flag to avoid flooding JavaScript with redundant Redraw requests
+    bool isScheduled_; 
+
+  public:
+    ViewportContentChangedObserver(MessageBroker& broker) :
+      IObserver(broker),
+      isScheduled_(false)
+    {
+    }
+
+    void Reset()
+    {
+      isScheduled_ = false;
+    }
+
+    void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message)
+    {
+      if (!isScheduled_)
+      {
+        ScheduleWebViewportRedrawFromCpp((ViewportHandle)&message.GetOrigin());  // loosing constness when transmitted to Web
+        isScheduled_ = true;
+      }
+    }
+  };
+
+  // default status bar to log messages on the console/stdout
+  class StatusBar : public Deprecated::IStatusBar
+  {
+  public:
+    virtual void ClearMessage()
+    {
+    }
+
+    virtual void SetMessage(const std::string& message)
+    {
+      printf("%s\n", message.c_str());
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,46 @@
+#include "WasmDelayedCallExecutor.h"
+#include "json/value.h"
+#include "json/writer.h"
+#include <emscripten/emscripten.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  extern void WasmDelayedCallExecutor_Schedule(void* callable,
+                                      unsigned int timeoutInMs
+                                      /*void* payload*/);
+
+  void EMSCRIPTEN_KEEPALIVE WasmDelayedCallExecutor_ExecuteCallback(void* callable
+                                                       //void* payload
+                                                       )
+  {
+    if (callable == NULL)
+    {
+      throw;
+    }
+    else
+    {
+      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IDelayedCallExecutor::TimeoutMessage>*>(callable)->
+        Apply(Deprecated::IDelayedCallExecutor::TimeoutMessage()); // uri, reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
+    }
+  }
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+namespace Deprecated
+{
+  OrthancStone::MessageBroker* WasmDelayedCallExecutor::broker_ = NULL;
+
+
+  void WasmDelayedCallExecutor::Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
+                         unsigned int timeoutInMs)
+  {
+    WasmDelayedCallExecutor_Schedule(callback, timeoutInMs);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
+#include <Core/OrthancException.h>
+
+namespace Deprecated
+{
+  class WasmDelayedCallExecutor : public IDelayedCallExecutor
+  {
+  private:
+    static OrthancStone::MessageBroker* broker_;
+
+    // Private constructor => Singleton design pattern
+    WasmDelayedCallExecutor(OrthancStone::MessageBroker& broker) :
+      IDelayedCallExecutor(broker)
+    {
+    }
+
+  public:
+    static WasmDelayedCallExecutor& GetInstance()
+    {
+      if (broker_ == NULL)
+      {
+        printf("WasmDelayedCallExecutor::GetInstance(): broker not initialized\n");
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+      static WasmDelayedCallExecutor instance(*broker_);
+      return instance;
+    }
+
+    static void SetBroker(OrthancStone::MessageBroker& broker)
+    {
+      broker_ = &broker;
+    }
+
+    virtual void Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
+                         unsigned int timeoutInMs = 1000);
+
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmDelayedCallExecutor.js	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,7 @@
+mergeInto(LibraryManager.library, {
+  WasmDelayedCallExecutor_Schedule: function(callable, timeoutInMs/*, payload*/) {
+    setTimeout(function() {
+      window.WasmDelayedCallExecutor_ExecuteCallback(callable/*, payload*/);
+    }, timeoutInMs);
+  }
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmPlatformApplicationAdapter.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,59 @@
+#include "WasmPlatformApplicationAdapter.h"
+
+#include "Framework/StoneException.h"
+#include <stdio.h>
+#include "Platforms/Wasm/Defaults.h"
+
+namespace OrthancStone
+{
+  WasmPlatformApplicationAdapter::WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application)
+  : IObserver(broker),
+    application_(application)
+  {
+  }
+
+  void WasmPlatformApplicationAdapter::HandleSerializedMessageFromWeb(std::string& output, const std::string& input)
+  {
+    try
+    {
+      application_.HandleSerializedMessage(input.c_str());
+    }
+    catch (StoneException& exc)
+    {
+      printf("Error while handling message from web (error code = %d):\n", exc.GetErrorCode());
+      printf("While interpreting input: '%s'\n", input.c_str());
+      output = std::string("ERROR : ");
+    }
+    catch (std::exception& exc)
+    {
+      printf("Error while handling message from web (error text = %s):\n", exc.what());
+      printf("While interpreting input: '%s'\n", input.c_str());
+      output = std::string("ERROR : ");
+    }
+  }
+
+  void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage)
+  {
+    try
+    {
+      UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str());
+    }
+    catch (...)
+    {
+      printf("Error while handling string message to web\n");
+    }
+  }
+
+  void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage)
+  {
+    try
+    {
+      UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str());
+    }
+    catch (...)
+    {
+      printf("Error while handling serialized message to web\n");
+    }
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmPlatformApplicationAdapter.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <string>
+#include <Framework/Messages/IObserver.h>
+#include <Applications/IStoneApplication.h>
+
+namespace OrthancStone
+{
+  class WasmPlatformApplicationAdapter : public IObserver
+  {
+      IStoneApplication&  application_;
+    public:
+      WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application);
+
+      virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input);
+      virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage);
+      virtual void NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage);
+  };
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmViewport.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,13 @@
+#include "WasmViewport.h"
+
+#include <vector>
+#include <memory>
+
+std::vector<std::shared_ptr<Deprecated::WidgetViewport>> wasmViewports;
+
+void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget) {
+    std::shared_ptr<Deprecated::WidgetViewport> viewport(CreateWasmViewportFromCpp(htmlCanvasId));
+    viewport->SetCentralWidget(centralWidget);
+
+    wasmViewports.push_back(viewport);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmViewport.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "../../Framework/Deprecated/Viewport/WidgetViewport.h"
+
+#include <emscripten/emscripten.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  // JS methods accessible from C++
+  extern Deprecated::WidgetViewport* CreateWasmViewportFromCpp(const char* htmlCanvasId);
+
+#ifdef __cplusplus
+}
+#endif
+
+extern void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmWebService.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,168 @@
+#include "WasmWebService.h"
+#include "json/value.h"
+#include "json/writer.h"
+#include <emscripten/emscripten.h>
+#include <boost/shared_ptr.hpp>
+
+struct CachedSuccessNotification
+{
+  boost::shared_ptr<Deprecated::BaseWebService::CachedHttpRequestSuccessMessage>    cachedMessage;
+  std::unique_ptr<Orthanc::IDynamicObject>                                              payload;
+  OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback;
+};
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  extern void WasmWebService_GetAsync(void* callableSuccess,
+                                      void* callableFailure,
+                                      const char* uri,
+                                      const char* headersInJsonString,
+                                      void* payload,
+                                      unsigned int timeoutInSeconds);
+
+  extern void WasmWebService_ScheduleLaterCachedSuccessNotification(void* brol);
+
+  extern void WasmWebService_PostAsync(void* callableSuccess,
+                                       void* callableFailure,
+                                       const char* uri,
+                                       const char* headersInJsonString,
+                                       const void* body,
+                                       size_t bodySize,
+                                       void* payload,
+                                       unsigned int timeoutInSeconds);
+
+  extern void WasmWebService_DeleteAsync(void* callableSuccess,
+                                         void* callableFailure,
+                                         const char* uri,
+                                         const char* headersInJsonString,
+                                         void* payload,
+                                         unsigned int timeoutInSeconds);
+
+  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyError(void* failureCallable,
+                                                       const char* uri,
+                                                       unsigned int httpStatus,
+                                                       void* payload)
+  {
+    if (failureCallable != NULL)
+    {
+      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>*>(failureCallable)->
+        Apply(Deprecated::IWebService::HttpRequestErrorMessage(uri, static_cast<Orthanc::HttpStatus>(httpStatus), reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
+    }
+  }
+
+  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyCachedSuccess(void* notification_)
+  {
+    // notification has been allocated in C++ and passed to JS.  It must be deleted by this method
+    std::unique_ptr<CachedSuccessNotification> notification(reinterpret_cast<CachedSuccessNotification*>(notification_));
+
+    notification->successCallback->Apply(Deprecated::IWebService::HttpRequestSuccessMessage(
+      notification->cachedMessage->GetUri(), 
+      notification->cachedMessage->GetAnswer(),
+      notification->cachedMessage->GetAnswerSize(),
+      notification->cachedMessage->GetAnswerHttpHeaders(),
+      notification->payload.get()
+      ));
+  }
+
+  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifySuccess(void* successCallable,
+                                                         const char* uri,
+                                                         const void* body,
+                                                         size_t bodySize,
+                                                         const char* answerHeaders,
+                                                         void* payload)
+  {
+    if (successCallable != NULL)
+    {
+      Deprecated::IWebService::HttpHeaders headers;
+
+      // TODO - Parse "answerHeaders"
+      //printf("TODO: parse headers [%s]\n", answerHeaders);
+      
+      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>*>(successCallable)->
+        Apply(Deprecated::IWebService::HttpRequestSuccessMessage(uri, body, bodySize, headers,
+                                                                   reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
+    }
+  }
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+namespace Deprecated
+{
+  OrthancStone::MessageBroker* WasmWebService::broker_ = NULL;
+
+  void ToJsonString(std::string& output, const IWebService::HttpHeaders& headers)
+  {
+    Json::Value jsonHeaders;
+    for (IWebService::HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ )
+    {
+      jsonHeaders[it->first] = it->second;
+    }
+
+    Json::StreamWriterBuilder builder;
+    std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
+    std::ostringstream outputStr;
+
+    writer->write(jsonHeaders, &outputStr);
+    output = outputStr.str();
+  }
+
+  void WasmWebService::PostAsync(const std::string& relativeUri,
+                                 const HttpHeaders& headers,
+                                 const std::string& body,
+                                 Orthanc::IDynamicObject* payload,
+                                 OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
+                                 OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
+                                 unsigned int timeoutInSeconds)
+  {
+    std::string headersInJsonString;
+    ToJsonString(headersInJsonString, headers);
+    WasmWebService_PostAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(),
+                             body.c_str(), body.size(), payload, timeoutInSeconds);
+  }
+
+  void WasmWebService::DeleteAsync(const std::string& relativeUri,
+                                   const HttpHeaders& headers,
+                                   Orthanc::IDynamicObject* payload,
+                                   OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
+                                   OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
+                                   unsigned int timeoutInSeconds)
+  {
+    std::string headersInJsonString;
+    ToJsonString(headersInJsonString, headers);
+    WasmWebService_DeleteAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(),
+                               payload, timeoutInSeconds);
+  }
+
+  void WasmWebService::GetAsyncInternal(const std::string &relativeUri,
+                                        const HttpHeaders &headers,
+                                        Orthanc::IDynamicObject *payload,
+                                        OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
+                                        OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable,
+                                        unsigned int timeoutInSeconds)
+  {
+    std::string headersInJsonString;
+    ToJsonString(headersInJsonString, headers);
+    WasmWebService_GetAsync(successCallable, failureCallable, relativeUri.c_str(),
+                            headersInJsonString.c_str(), payload, timeoutInSeconds);
+  }
+
+  void WasmWebService::NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
+                                              Orthanc::IDynamicObject* payload, // takes ownership
+                                              OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback)
+  {
+    CachedSuccessNotification* notification = new CachedSuccessNotification();  // allocated on the heap, it will be passed to JS and deleted when coming back to C++
+    notification->cachedMessage = cachedMessage;
+    notification->payload.reset(payload);
+    notification->successCallback = successCallback;
+
+    WasmWebService_ScheduleLaterCachedSuccessNotification(notification);
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmWebService.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,62 @@
+#pragma once
+
+#include "../../Framework/Deprecated/Toolbox/BaseWebService.h"
+#include <Core/OrthancException.h>
+
+namespace Deprecated
+{
+class WasmWebService : public BaseWebService
+{
+private:
+  static OrthancStone::MessageBroker *broker_;
+
+  // Private constructor => Singleton design pattern
+  WasmWebService(OrthancStone::MessageBroker &broker) : BaseWebService(broker)
+  {
+  }
+
+public:
+  static WasmWebService &GetInstance()
+  {
+    if (broker_ == NULL)
+    {
+      printf("WasmWebService::GetInstance(): broker not initialized\n");
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    static WasmWebService instance(*broker_);
+    return instance;
+  }
+
+  static void SetBroker(OrthancStone::MessageBroker &broker)
+  {
+    broker_ = &broker;
+  }
+
+  virtual void PostAsync(const std::string &uri,
+                         const HttpHeaders &headers,
+                         const std::string &body,
+                         Orthanc::IDynamicObject *payload,
+                         OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
+                         OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
+                         unsigned int timeoutInSeconds = 60);
+
+  virtual void DeleteAsync(const std::string &uri,
+                           const HttpHeaders &headers,
+                           Orthanc::IDynamicObject *payload,
+                           OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
+                           OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
+                           unsigned int timeoutInSeconds = 60);
+
+protected:
+  virtual void GetAsyncInternal(const std::string &uri,
+                                const HttpHeaders &headers,
+                                Orthanc::IDynamicObject *payload,
+                                OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
+                                OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
+                                unsigned int timeoutInSeconds = 60);
+
+  virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage,
+                                      Orthanc::IDynamicObject *payload, // takes ownership
+                                      OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallback);
+};
+} // namespace Deprecated
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/WasmWebService.js	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,108 @@
+mergeInto(LibraryManager.library, {
+  WasmWebService_GetAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) {
+    // Directly use XMLHttpRequest (no jQuery) to retrieve the raw binary data
+    // http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/
+    var xhr = new XMLHttpRequest();
+    var url_ = UTF8ToString(url);
+    var headersInJsonString_ = UTF8ToString(headersInJsonString);
+
+    xhr.open('GET', url_, true);
+    xhr.responseType = 'arraybuffer';
+    xhr.timeout = timeoutInSeconds * 1000;
+    var headers = JSON.parse(headersInJsonString_);
+    for (var key in headers) {
+      xhr.setRequestHeader(key, headers[key]);
+    }
+    //console.log(xhr); 
+    xhr.onreadystatechange = function() {
+      if (this.readyState == XMLHttpRequest.DONE) {
+        if (xhr.status === 200) {
+          var s = xhr.getAllResponseHeaders();
+          var headers = _malloc(s.length + 1);
+          stringToUTF8(s, headers, s.length + 1);
+          
+          // TODO - Is "new Uint8Array()" necessary? This copies the
+          // answer to the WebAssembly stack, hence necessitating
+          // increasing the TOTAL_STACK parameter of Emscripten
+          window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
+                                       this.response.byteLength, headers, payload);
+        } else {
+          window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload);
+        }
+      }
+    }
+    
+    xhr.send();
+  },
+
+  WasmWebService_ScheduleLaterCachedSuccessNotification: function (brol) {
+    setTimeout(function() {
+      window.WasmWebService_NotifyCachedSuccess(brol);
+    }, 0);
+  },
+
+  WasmWebService_PostAsync: function(callableSuccess, callableFailure, url, headersInJsonString, body, bodySize, payload, timeoutInSeconds) {
+    var xhr = new XMLHttpRequest();
+    var url_ = UTF8ToString(url);
+    var headersInJsonString_ = UTF8ToString(headersInJsonString);
+    xhr.open('POST', url_, true);
+    xhr.timeout = timeoutInSeconds * 1000;
+    xhr.responseType = 'arraybuffer';
+    xhr.setRequestHeader('Content-type', 'application/octet-stream');
+
+    var headers = JSON.parse(headersInJsonString_);
+    for (var key in headers) {
+      xhr.setRequestHeader(key, headers[key]);
+    }
+    
+    xhr.onreadystatechange = function() {
+      if (this.readyState == XMLHttpRequest.DONE) {
+        if (xhr.status === 200) {
+          var s = xhr.getAllResponseHeaders();
+          var headers = _malloc(s.length + 1);
+          stringToUTF8(s, headers, s.length + 1);
+
+          window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
+                                       this.response.byteLength, headers, payload);
+        } else {
+          window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload);
+        }
+      }
+    }
+
+    xhr.send(new Uint8ClampedArray(HEAPU8.buffer, body, bodySize));
+  },
+
+  WasmWebService_DeleteAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) {
+    var xhr = new XMLHttpRequest();
+    var url_ = UTF8ToString(url);
+    var headersInJsonString_ = UTF8ToString(headersInJsonString);
+    xhr.open('DELETE', url_, true);
+    xhr.timeout = timeoutInSeconds * 1000;
+    xhr.responseType = 'arraybuffer';
+    xhr.setRequestHeader('Content-type', 'application/octet-stream');
+  
+    var headers = JSON.parse(headersInJsonString_);
+    for (var key in headers) {
+      xhr.setRequestHeader(key, headers[key]);
+    }
+    
+    xhr.onreadystatechange = function() {
+      if (this.readyState == XMLHttpRequest.DONE) {
+        if (xhr.status === 200) {
+          var s = xhr.getAllResponseHeaders();
+          var headers = _malloc(s.length + 1);
+          stringToUTF8(s, headers, s.length + 1);
+
+          window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
+                                       this.response.byteLength, headers, payload);
+        } else {
+          window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload);
+        }
+      }
+    }
+  
+    xhr.send();
+  }
+  
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/default-library.js	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,49 @@
+// this file contains the JS method you want to expose to C++ code
+
+mergeInto(LibraryManager.library, {
+
+  ScheduleWebViewportRedrawFromCpp: function(cppViewportHandle) {
+    window.ScheduleWebViewportRedraw(cppViewportHandle);
+  },
+
+  CreateWasmViewportFromCpp: function(htmlCanvasId) {
+    return window.CreateWasmViewport(htmlCanvasId);
+  },
+
+  // each time the StoneApplication updates its status, it may signal it 
+  // through this method. i.e, to change the status of a button in the web interface
+  UpdateStoneApplicationStatusFromCppWithString: function(statusUpdateMessage) {
+    var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage);
+    window.UpdateWebApplicationWithString(statusUpdateMessage_);
+  },
+
+  // same, but with a serialized message
+  UpdateStoneApplicationStatusFromCppWithSerializedMessage: function(statusUpdateMessage) {
+    var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage);
+    window.UpdateWebApplicationWithSerializedMessage(statusUpdateMessage_);
+  },
+
+  // These functions are called from C++ (through an extern declaration) 
+  // and call the standard logger that, here, routes to the console.
+
+  stone_console_error : function(message) {
+    var text = UTF8ToString(message);
+    window.errorFromCpp(text);
+  },
+
+  stone_console_warning : function(message) {
+    var text = UTF8ToString(message);
+    window.warningFromCpp(text);
+  },
+
+  stone_console_info: function(message) {
+    var text = UTF8ToString(message);
+    window.infoFromCpp(text);
+  },
+  
+  stone_console_trace : function(message) {
+    var text = UTF8ToString(message);
+    window.debugFromCpp(text);
+  }
+
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/logger.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,111 @@
+export enum LogSource {
+  Cpp,
+  Typescript
+}
+
+export class StandardConsoleLogger {
+  public showSource: boolean = true;
+
+  public debug(...args: any[]): void {
+    this._debug(LogSource.Typescript, ...args);
+  }
+
+  public debugFromCpp(...args: any[]): void {
+    this._debug(LogSource.Cpp, ...args);
+  }
+
+  public info(...args: any[]): void {
+    this._info(LogSource.Typescript, ...args);
+  }
+
+  public infoFromCpp(message: string): void {
+    this._info(LogSource.Cpp, message);
+  }
+
+  public warning(...args: any[]): void {
+    this._warning(LogSource.Typescript, ...args);
+  }
+
+  public warningFromCpp(message: string): void {
+    this._warning(LogSource.Cpp, message);
+  }
+
+  public error(...args: any[]): void {
+    this._error(LogSource.Typescript, ...args);
+  }
+
+  public errorFromCpp(message: string): void {
+    this._error(LogSource.Cpp, message);
+  }
+
+  public _debug(source: LogSource, ...args: any[]): void {
+    if ((<any> window).IsTraceLevelEnabled)
+    {
+      if ((<any> window).IsTraceLevelEnabled())
+      {
+        var output = this.getOutput(source, args);
+        console.debug(...output);
+      }
+    }
+  }
+
+  private _info(source: LogSource, ...args: any[]): void {
+    if ((<any> window).IsInfoLevelEnabled)
+    {
+      if ((<any> window).IsInfoLevelEnabled())
+      {
+        var output = this.getOutput(source, args);
+        console.info(...output);
+      }
+    }
+  }
+
+  public _warning(source: LogSource, ...args: any[]): void {
+    var output = this.getOutput(source, args);
+    console.warn(...output);
+  }
+
+  public _error(source: LogSource, ...args: any[]): void {
+    var output = this.getOutput(source, args);
+    console.error(...output);
+  }
+
+
+  private getOutput(source: LogSource, args: any[]): any[] {
+    var prefix = this.getPrefix();
+    var prefixAndSource = Array<string>();
+
+    if (prefix != null) {
+      prefixAndSource = [prefix];
+    } 
+
+    if (this.showSource) {
+      if (source == LogSource.Typescript) {
+        prefixAndSource = [...prefixAndSource, "TS "];
+      } else if (source == LogSource.Cpp) {
+        prefixAndSource = [...prefixAndSource, "C++"];
+      }
+    }
+
+    if (prefixAndSource.length > 0) {
+      prefixAndSource = [...prefixAndSource, "|"];
+    }
+
+    return [...prefixAndSource, ...args];
+  }
+
+  protected getPrefix(): string | null {
+    return null;
+  }
+}
+
+export class TimeConsoleLogger extends StandardConsoleLogger {
+  protected getPrefix(): string {
+    let now = new Date();
+    let timeString = now.getHours().toString().padStart(2, "0") + ":" + now.getMinutes().toString().padStart(2, "0") + ":" + now.getSeconds().toString().padStart(2, "0") + "." + now.getMilliseconds().toString().padStart(3, "0");
+    return timeString;
+  }
+}
+
+export var defaultLogger: StandardConsoleLogger = new TimeConsoleLogger();
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/stone-framework-loader.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,95 @@
+/**
+ * This file contains primitives to interface with WebAssembly and
+ * with the Stone framework.
+ **/
+import * as Logger from './logger'
+
+export declare type InitializationCallback = () => void;
+
+//export declare var StoneFrameworkModule : any;
+export var StoneFrameworkModule : any;
+
+//const ASSETS_FOLDER : string = "assets/lib";
+//const WASM_FILENAME : string = "orthanc-framework";
+
+export class Framework
+{
+  private static singleton_ : Framework = null;
+  private static wasmModuleName_ : string = null;
+
+  public static Configure(wasmModuleName: string) {
+    this.wasmModuleName_ = wasmModuleName;
+  }
+
+  private constructor(verbose : boolean) 
+  {
+    //this.ccall('Initialize', null, [ 'number' ], [ verbose ]);
+  }
+
+  
+  public ccall( name: string,
+                returnType: string,
+                argTypes: Array<string>,
+                argValues: Array<any>) : any
+  {
+    return (<any> window).StoneFrameworkModule.ccall(name, returnType, argTypes, argValues);
+  }
+
+  
+  public cwrap( name: string,
+                returnType: string,
+                argTypes: Array<string>) : any
+  {
+    return (<any> window).StoneFrameworkModule.cwrap(name, returnType, argTypes);
+  }
+
+  
+  public static GetInstance() : Framework
+  {
+    if (Framework.singleton_ == null) {
+      throw new Error('The WebAssembly module is not loaded yet');
+    } else {
+      return Framework.singleton_;
+    }
+  }
+
+
+  public static Initialize( verbose: boolean,
+                            callback: InitializationCallback)
+  {
+    Logger.defaultLogger.debug('Initializing WebAssembly Module');
+
+    (<any> window).errorFromCpp = function(text:any) { Logger.defaultLogger.errorFromCpp(text); };
+    (<any> window).warningFromCpp = function(text:any) { Logger.defaultLogger.warningFromCpp(text); };
+    (<any> window).infoFromCpp = function(text:any) { Logger.defaultLogger.infoFromCpp(text); };
+    (<any> window).debugFromCpp = function(text:any) { Logger.defaultLogger.debugFromCpp(text); };
+
+    // (<any> window).
+    (<any> window).StoneFrameworkModule = {
+      preRun: [ 
+        function() {
+          Logger.defaultLogger.debug('Loading the Stone Framework using WebAssembly');
+        }
+      ],
+      postRun: [ 
+        function()  {
+          // This function is called by ".js" wrapper once the ".wasm"
+          // WebAssembly module has been loaded and compiled by the
+          // browser
+          Logger.defaultLogger.debug('WebAssembly is ready');
+          Framework.singleton_ = new Framework(verbose);
+          callback();
+        }
+      ],
+      totalDependencies: 0
+    };
+
+    // Dynamic loading of the JavaScript wrapper around WebAssembly
+    var script = document.createElement('script');
+    script.type = 'application/javascript';
+    //script.src = "orthanc-stone.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js';
+    script.src = this.wasmModuleName_ + ".js";//  "OrthancStoneSimpleViewer.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js';
+    script.async = true;
+    document.head.appendChild(script);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/tsconfig-stone.json	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,8 @@
+{
+    "include" : [
+        "stone-framework-loader.ts",
+        "logger.ts",
+        "wasm-application-runner.ts",
+        "wasm-viewport.ts"
+    ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/wasm-application-runner.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,141 @@
+import * as Stone from './stone-framework-loader'
+import * as StoneViewport from './wasm-viewport'
+import * as Logger from './logger'
+
+if (!('WebAssembly' in window)) {
+  alert('Sorry, your browser does not support WebAssembly :(');
+}
+
+//var StoneFrameworkModule : Stone.Framework = (<any>window).StoneFrameworkModule;
+//export declare var StoneFrameworkModule : Stone.Framework;
+
+// global functions
+var WasmWebService_NotifyError: Function = null;
+var WasmWebService_NotifySuccess: Function = null;
+var WasmWebService_NotifyCachedSuccess: Function = null;
+var WasmDelayedCallExecutor_ExecuteCallback: Function = null;
+var WasmDoAnimation: Function = null;
+var SetStartupParameter: Function = null;
+var CreateWasmApplication: Function = null;
+export var CreateCppViewport: Function = null;
+var ReleaseCppViewport: Function = null;
+var StartWasmApplication: Function = null;
+export var SendSerializedMessageToStoneApplication: Function = null;
+
+var auxiliaryParameters : Map<string,string>  = null;
+
+export function SetApplicationParameters(params : Map<string,string>) {
+  if (auxiliaryParameters != null) {
+    console.warn("wasm-application-runner.SetApplicationParameters: about to overwrite the existing application parameters!")
+  }
+  auxiliaryParameters = params;
+}
+
+function DoAnimationThread() {
+  if (WasmDoAnimation != null) {
+    WasmDoAnimation();
+  }
+
+  // Update the viewport content every 100ms if need be
+  setTimeout(DoAnimationThread, 100);  
+}
+
+
+function GetUriParameters(): Map<string, string> {
+  var parameters = window.location.search.substr(1);
+
+  if (parameters != null &&
+    parameters != '') {
+    var result = new Map<string, string>();
+    var tokens = parameters.split('&');
+
+    for (var i = 0; i < tokens.length; i++) {
+      var tmp = tokens[i].split('=');
+      if (tmp.length == 2) {
+        result[tmp[0]] = decodeURIComponent(tmp[1]);
+      } else if(tmp.length == 1) {
+        // if there is no '=', we treat ot afterwards as a flag-style param
+        result[tmp[0]] = "";
+      }
+    }
+  return result;
+  }
+  else {
+    return new Map<string, string>();
+  }
+}
+
+// function UpdateWebApplication(statusUpdateMessage: string) {
+//   console.log(statusUpdateMessage);
+// }
+
+function _InitializeWasmApplication(orthancBaseUrl: string): void {
+
+  CreateWasmApplication();
+
+  // transmit the API-specified parameters to the app before initializing it
+  for (let key in auxiliaryParameters) {
+    if (auxiliaryParameters.hasOwnProperty(key)) {
+      Logger.defaultLogger.debug(
+        `About to call SetStartupParameter("${key}","${auxiliaryParameters[key]}")`);
+      SetStartupParameter(key, auxiliaryParameters[key]);
+    }
+  }
+
+  // parse uri and transmit the URI parameters to the app before initializing it
+  let parameters = GetUriParameters();
+
+  for (let key in parameters) {
+    if (parameters.hasOwnProperty(key)) {
+      Logger.defaultLogger.debug(
+        `About to call SetStartupParameter("${key}","${parameters[key]}")`);
+      SetStartupParameter(key, parameters[key]);
+    }
+  }
+
+  StartWasmApplication(orthancBaseUrl);
+
+  // trigger a first resize of the canvas that has just been initialized
+  StoneViewport.WasmViewport.ResizeAll();
+
+  DoAnimationThread();
+}
+
+export function InitializeWasmApplication(wasmModuleName: string, orthancBaseUrl: string) {
+  
+  Stone.Framework.Configure(wasmModuleName);
+
+  // Wait for the Orthanc Framework to be initialized (this initializes
+  // the WebAssembly environment) and then, create and initialize the Wasm application
+  Stone.Framework.Initialize(true, function () {
+
+    Logger.defaultLogger.debug("Connecting C++ methods to JS methods");
+    
+    SetStartupParameter = (<any> window).StoneFrameworkModule.cwrap('SetStartupParameter', null, ['string', 'string']);
+    CreateWasmApplication = (<any> window).StoneFrameworkModule.cwrap('CreateWasmApplication', null, ['number']);
+    CreateCppViewport = (<any> window).StoneFrameworkModule.cwrap('CreateCppViewport', 'number', []);
+    ReleaseCppViewport = (<any> window).StoneFrameworkModule.cwrap('ReleaseCppViewport', null, ['number']);
+    StartWasmApplication = (<any> window).StoneFrameworkModule.cwrap('StartWasmApplication', null, ['string']);
+    (<any> window).IsTraceLevelEnabled = (<any> window).StoneFrameworkModule.cwrap('WasmIsTraceLevelEnabled', 'boolean', null);
+    (<any> window).IsInfoLevelEnabled = (<any> window).StoneFrameworkModule.cwrap('WasmIsInfoLevelEnabled', 'boolean', null);
+
+    (<any> window).WasmWebService_NotifyCachedSuccess = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifyCachedSuccess', null, ['number']);
+    (<any> window).WasmWebService_NotifySuccess = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifySuccess', null, ['number', 'string', 'array', 'number', 'number']);
+    (<any> window).WasmWebService_NotifyError = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifyError', null, ['number', 'string', 'number', 'number']);
+    (<any> window).WasmDelayedCallExecutor_ExecuteCallback = (<any> window).StoneFrameworkModule.cwrap('WasmDelayedCallExecutor_ExecuteCallback', null, ['number']);
+    // no need to put this into the globals for it's only used in this very module
+    WasmDoAnimation = (<any> window).StoneFrameworkModule.cwrap('WasmDoAnimation', null, []);
+
+    SendSerializedMessageToStoneApplication = (<any> window).StoneFrameworkModule.cwrap('SendSerializedMessageToStoneApplication', 'string', ['string']);
+
+    Logger.defaultLogger.debug("Connecting C++ methods to JS methods - done");
+
+    _InitializeWasmApplication(orthancBaseUrl);
+  });
+}
+
+
+// exports.InitializeWasmApplication = InitializeWasmApplication;
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Platforms/Wasm/wasm-viewport.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,359 @@
+import * as wasmApplicationRunner from './wasm-application-runner'
+import * as Logger from './logger'
+
+var isPendingRedraw = false;
+
+function ScheduleWebViewportRedraw(cppViewportHandle: any) : void
+{
+  if (!isPendingRedraw) {
+    isPendingRedraw = true;
+    Logger.defaultLogger.debug('Scheduling a refresh of the viewport, as its content changed');
+    window.requestAnimationFrame(function() {
+      isPendingRedraw = false;
+      let viewport = WasmViewport.GetFromCppViewport(cppViewportHandle);
+      if (viewport) {
+        viewport.Redraw();
+      }
+    });
+  }
+}
+
+(<any>window).ScheduleWebViewportRedraw = ScheduleWebViewportRedraw;
+
+declare function UTF8ToString(v: any): string;
+
+function CreateWasmViewport(htmlCanvasId: string) : any {
+  var cppViewportHandle = wasmApplicationRunner.CreateCppViewport();
+  var canvasId = UTF8ToString(htmlCanvasId);
+  var webViewport = new WasmViewport((<any> window).StoneFrameworkModule, canvasId, cppViewportHandle);  // viewports are stored in a static map in WasmViewport -> won't be deleted
+  webViewport.Initialize();
+
+  return cppViewportHandle;
+}
+ 
+(<any>window).CreateWasmViewport = CreateWasmViewport;
+
+export class WasmViewport {
+
+    private static viewportsMapByCppHandle_ : Map<number, WasmViewport> = new Map<number, WasmViewport>(); // key = the C++ handle
+    private static viewportsMapByCanvasId_ : Map<string, WasmViewport> = new Map<string, WasmViewport>(); // key = the canvasId
+
+    private module_ : any;
+    private canvasId_ : string;
+    private htmlCanvas_ : HTMLCanvasElement;
+    private context_ : CanvasRenderingContext2D | null;
+    private imageData_ : any = null;
+    private renderingBuffer_ : any = null;
+    
+    private touchGestureInProgress_: boolean = false;
+    private touchCount_: number = 0;
+    private touchGestureLastCoordinates_: [number, number][] = []; // last x,y coordinates of each touch
+    
+    private touchZoom_ : any = false;
+    private touchTranslation_ : any = false;
+
+    private ViewportSetSize : Function;
+    private ViewportRender : Function;
+    private ViewportMouseDown : Function;
+    private ViewportMouseMove : Function;
+    private ViewportMouseUp : Function;
+    private ViewportMouseEnter : Function;
+    private ViewportMouseLeave : Function;
+    private ViewportMouseWheel : Function;
+    private ViewportKeyPressed : Function;
+    private ViewportTouchStart : Function;
+    private ViewportTouchMove : Function;
+    private ViewportTouchEnd : Function;
+
+    private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object
+
+    public constructor(module: any, canvasId: string, cppViewport: any) {
+      
+      this.pimpl_ = cppViewport;
+      WasmViewport.viewportsMapByCppHandle_[this.pimpl_] = this;
+      WasmViewport.viewportsMapByCanvasId_[canvasId] = this;
+
+      this.module_ = module;
+      this.canvasId_ = canvasId;
+      this.htmlCanvas_ = document.getElementById(this.canvasId_) as HTMLCanvasElement;
+      if (this.htmlCanvas_ == null) {
+        Logger.defaultLogger.error("Can not create WasmViewport, did not find the canvas whose id is '", this.canvasId_, "'");
+      }
+      this.context_ = this.htmlCanvas_.getContext('2d');
+
+      this.ViewportSetSize = this.module_.cwrap('ViewportSetSize', null, [ 'number', 'number', 'number' ]);
+      this.ViewportRender = this.module_.cwrap('ViewportRender', null, [ 'number', 'number', 'number', 'number' ]);
+      this.ViewportMouseDown = this.module_.cwrap('ViewportMouseDown', null, [ 'number', 'number', 'number', 'number', 'number' ]);
+      this.ViewportMouseMove = this.module_.cwrap('ViewportMouseMove', null, [ 'number', 'number', 'number' ]);
+      this.ViewportMouseUp = this.module_.cwrap('ViewportMouseUp', null, [ 'number' ]);
+      this.ViewportMouseEnter = this.module_.cwrap('ViewportMouseEnter', null, [ 'number' ]);
+      this.ViewportMouseLeave = this.module_.cwrap('ViewportMouseLeave', null, [ 'number' ]);
+      this.ViewportMouseWheel = this.module_.cwrap('ViewportMouseWheel', null, [ 'number', 'number', 'number', 'number', 'number' ]);
+      this.ViewportKeyPressed = this.module_.cwrap('ViewportKeyPressed', null, [ 'number', 'number', 'string', 'number', 'number' ]);
+      this.ViewportTouchStart = this.module_.cwrap('ViewportTouchStart', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
+      this.ViewportTouchMove = this.module_.cwrap('ViewportTouchMove', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
+      this.ViewportTouchEnd = this.module_.cwrap('ViewportTouchEnd', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
+    }
+
+    public GetCppViewport() : number {
+      return this.pimpl_;
+    }
+
+    public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport | null {
+      if (WasmViewport.viewportsMapByCppHandle_[cppViewportHandle] !== undefined) {
+        return WasmViewport.viewportsMapByCppHandle_[cppViewportHandle];
+      }
+      Logger.defaultLogger.error("WasmViewport not found !");
+      return null;
+    }
+
+    public static GetFromCanvasId(canvasId: string) : WasmViewport | null {
+      if (WasmViewport.viewportsMapByCanvasId_[canvasId] !== undefined) {
+        return WasmViewport.viewportsMapByCanvasId_[canvasId];
+      }
+      Logger.defaultLogger.error("WasmViewport not found !");
+      return null;
+    }
+
+    public static ResizeAll() {
+      for (let canvasId in WasmViewport.viewportsMapByCanvasId_) {
+        WasmViewport.viewportsMapByCanvasId_[canvasId].Resize();
+      }
+    }
+
+    public Redraw() {
+      if (this.imageData_ === null ||
+          this.renderingBuffer_ === null ||
+          this.ViewportRender(this.pimpl_,
+                         this.imageData_.width,
+                         this.imageData_.height,
+                         this.renderingBuffer_) == 0) {
+        Logger.defaultLogger.error('The rendering has failed');
+      } else {
+        // Create an accessor to the rendering buffer (i.e. create a
+        // "window" above the heap of the WASM module), then copy it to
+        // the ImageData object
+        this.imageData_.data.set(new Uint8ClampedArray(
+          this.module_.HEAPU8.buffer,
+          this.renderingBuffer_,
+          this.imageData_.width * this.imageData_.height * 4));
+        
+        if (this.context_) {
+          this.context_.putImageData(this.imageData_, 0, 0);
+        }
+      }
+    }
+  
+    public Resize() {
+      if (this.imageData_ != null &&
+          (this.imageData_.width != window.innerWidth ||
+           this.imageData_.height != window.innerHeight)) {
+        this.imageData_ = null;
+      }
+      
+      // width/height is defined by the parent width/height
+      if (this.htmlCanvas_.parentElement) {
+        this.htmlCanvas_.width = this.htmlCanvas_.parentElement.offsetWidth;  
+        this.htmlCanvas_.height = this.htmlCanvas_.parentElement.offsetHeight;  
+
+        Logger.defaultLogger.debug("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height);
+
+        if (this.imageData_ === null && this.context_) {
+          this.imageData_ = this.context_.getImageData(0, 0, this.htmlCanvas_.width, this.htmlCanvas_.height);
+          this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height);
+    
+          if (this.renderingBuffer_ != null) {
+            this.module_._free(this.renderingBuffer_);
+          }
+          
+          this.renderingBuffer_ = this.module_._malloc(this.imageData_.width * this.imageData_.height * 4);
+        } else {
+          this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height);
+        }
+        
+        this.Redraw();
+      }
+    }
+
+    public Initialize() {
+      
+      // Force the rendering of the viewport for the first time
+      this.Resize();
+    
+      var that : WasmViewport = this;
+      // Register an event listener to call the Resize() function 
+      // each time the window is resized.
+      window.addEventListener('resize', function(event) {
+        that.Resize();
+      }, false);
+  
+      this.htmlCanvas_.addEventListener('contextmenu', function(event) {
+        // Prevent right click on the canvas
+        event.preventDefault();
+      }, false);
+      
+      this.htmlCanvas_.addEventListener('mouseleave', function(event) {
+        that.ViewportMouseLeave(that.pimpl_);
+      });
+      
+      this.htmlCanvas_.addEventListener('mouseenter', function(event) {
+        that.ViewportMouseEnter(that.pimpl_);
+      });
+    
+      this.htmlCanvas_.addEventListener('mousedown', function(event) {
+        var x = event.pageX - this.offsetLeft;
+        var y = event.pageY - this.offsetTop;
+
+       that.ViewportMouseDown(that.pimpl_, event.button, x, y, 0 /* TODO detect modifier keys*/);    
+      });
+    
+      this.htmlCanvas_.addEventListener('mousemove', function(event) {
+        var x = event.pageX - this.offsetLeft;
+        var y = event.pageY - this.offsetTop;
+        that.ViewportMouseMove(that.pimpl_, x, y);
+      });
+    
+      this.htmlCanvas_.addEventListener('mouseup', function(event) {
+        that.ViewportMouseUp(that.pimpl_);
+      });
+    
+      window.addEventListener('keydown', function(event) {
+        var keyChar: string | null = event.key;
+        var keyCode = event.keyCode
+        if (keyChar.length == 1) {
+          keyCode = 0; // maps to OrthancStone::KeyboardKeys_Generic
+        } else {
+          keyChar = null;
+        }
+//        console.log("key: ", keyCode, keyChar);
+        that.ViewportKeyPressed(that.pimpl_, keyCode, keyChar, event.shiftKey, event.ctrlKey, event.altKey);
+      });
+    
+      this.htmlCanvas_.addEventListener('wheel', function(event) {
+        var x = event.pageX - this.offsetLeft;
+        var y = event.pageY - this.offsetTop;
+        that.ViewportMouseWheel(that.pimpl_, event.deltaY, x, y, event.ctrlKey);
+        event.preventDefault();
+      }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
+
+      this.htmlCanvas_.addEventListener('touchstart', function(event: TouchEvent) {
+        // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
+        event.preventDefault();
+        event.stopPropagation();
+
+        // TODO: find a way to pass the coordinates as an array between JS and C++
+        var x0 = 0;
+        var y0 = 0;
+        var x1 = 0;
+        var y1 = 0;
+        var x2 = 0;
+        var y2 = 0;
+        if (event.targetTouches.length > 0) {
+          x0 = event.targetTouches[0].pageX;
+          y0 = event.targetTouches[0].pageY;
+        }
+        if (event.targetTouches.length > 1) {
+          x1 = event.targetTouches[1].pageX;
+          y1 = event.targetTouches[1].pageY;
+        }
+        if (event.targetTouches.length > 2) {
+          x2 = event.targetTouches[2].pageX;
+          y2 = event.targetTouches[2].pageY;
+        }
+
+        that.ViewportTouchStart(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
+      }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
+    
+      this.htmlCanvas_.addEventListener('touchend', function(event) {
+        // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
+        event.preventDefault();
+        event.stopPropagation();
+
+        // TODO: find a way to pass the coordinates as an array between JS and C++
+        var x0 = 0;
+        var y0 = 0;
+        var x1 = 0;
+        var y1 = 0;
+        var x2 = 0;
+        var y2 = 0;
+        if (event.targetTouches.length > 0) {
+          x0 = event.targetTouches[0].pageX;
+          y0 = event.targetTouches[0].pageY;
+        }
+        if (event.targetTouches.length > 1) {
+          x1 = event.targetTouches[1].pageX;
+          y1 = event.targetTouches[1].pageY;
+        }
+        if (event.targetTouches.length > 2) {
+          x2 = event.targetTouches[2].pageX;
+          y2 = event.targetTouches[2].pageY;
+        }
+
+        that.ViewportTouchEnd(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
+      });
+    
+      this.htmlCanvas_.addEventListener('touchmove', function(event: TouchEvent) {
+
+        // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
+        event.preventDefault();
+        event.stopPropagation();
+
+
+        // TODO: find a way to pass the coordinates as an array between JS and C++
+        var x0 = 0;
+        var y0 = 0;
+        var x1 = 0;
+        var y1 = 0;
+        var x2 = 0;
+        var y2 = 0;
+        if (event.targetTouches.length > 0) {
+          x0 = event.targetTouches[0].pageX;
+          y0 = event.targetTouches[0].pageY;
+        }
+        if (event.targetTouches.length > 1) {
+          x1 = event.targetTouches[1].pageX;
+          y1 = event.targetTouches[1].pageY;
+        }
+        if (event.targetTouches.length > 2) {
+          x2 = event.targetTouches[2].pageX;
+          y2 = event.targetTouches[2].pageY;
+        }
+
+        that.ViewportTouchMove(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
+        return;
+
+      }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
+    }  
+
+  public ResetTouch() {
+    if (this.touchTranslation_ ||
+        this.touchZoom_) {
+      this.ViewportMouseUp(this.pimpl_);
+    }
+
+    this.touchTranslation_ = false;
+    this.touchZoom_ = false;
+  }
+  
+  public GetTouchTranslation(event: any) {
+    var touch = event.targetTouches[0];
+    return [
+      touch.pageX,
+      touch.pageY
+    ];
+  }
+    
+  public GetTouchZoom(event: any) {
+    var touch1 = event.targetTouches[0];
+    var touch2 = event.targetTouches[1];
+    var dx = (touch1.pageX - touch2.pageX);
+    var dy = (touch1.pageY - touch2.pageY);
+    var d = Math.sqrt(dx * dx + dy * dy);
+    return [
+      (touch1.pageX + touch2.pageX) / 2.0,
+      (touch1.pageY + touch2.pageY) / 2.0,
+      d
+    ];
+  }
+   
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/Graveyard/playground.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,259 @@
+/*
+         1         2         3         4         5         6         7
+12345678901234567890123456789012345678901234567890123456789012345678901234567890
+*/
+
+namespace VsolStuff
+{
+  enum EnumMonth0
+  {
+    January,
+    February,
+    March
+  };
+
+  // interface Serializer
+  // {
+  //   Serialize(value: number): string;
+  //   Serialize(value: string): string;
+  //   Serialize(value: EnumMonth0): string;
+  // };
+  function printf(value: any):void
+  {
+    console.log(value)
+  }
+
+  // function StoneSerialize(value: string) : string;
+  // function StoneSerialize(value: number) : string;
+  // function StoneSerialize(value: EnumMonth0) : string;
+  function StoneSerialize<T>(value: T[]) : string;
+
+  function StoneSerialize<T>(value: T[] | EnumMonth0) : string
+  {
+    let valueType = typeof value;
+    printf(`About to serialize value. Type is ${valueType}`)
+    printf(`About to serialize value. Type is ${typeof value}`)
+    return "Choucroute";
+  }
+
+  function main():number
+  {
+    enum Color {Red = 1, Green = 2, Blue = 4}
+    let color: Color = Color.Green;
+    printf("---------------------------");
+    printf(`typeof color: ${typeof color}`);
+    printf("---------------------------");
+    let colors: Color[] = []
+    colors.push(Color.Green);
+    colors.push(Color.Red);
+    printf(`typeof colors: ${typeof colors}`);
+    printf(`Array.isArray(colors): ${Array.isArray(colors)}`);
+    printf("---------------------------");
+    
+
+    let toto:EnumMonth0[] = [];
+
+    toto.push(EnumMonth0.February);
+    toto.push(EnumMonth0.March);
+
+    printf(JSON.stringify(toto));
+
+    return 0;
+
+  }
+
+  main()
+
+//   string StoneSerialize_number(int32_t value)
+//   {
+
+//     Json::Value result(value);
+//     return result;
+//   }
+
+//   Json::Value StoneSerialize(double value)
+//   {
+//     Json::Value result(value);
+//     return result;
+//   }
+
+//   Json::Value StoneSerialize(bool value)
+//   {
+//     Json::Value result(value);
+//     return result;
+//   }
+
+//   Json::Value StoneSerialize(const std::string& value)
+//   {
+//     // the following is better than 
+//     Json::Value result(value.data(),value.data()+value.size());
+//     return result;
+//   }
+
+//   template<typename T>
+//   Json::Value StoneSerialize(const std::map<std::string,T>& value)
+//   {
+//     Json::Value result(Json::objectValue);
+
+//     for (std::map<std::string, T>::const_iterator it = value.cbegin();
+//       it != value.cend(); ++it)
+//     {
+//       // it->first it->second
+//       result[it->first] = StoneSerialize(it->second);
+//     }
+//     return result;
+//   }
+
+//   template<typename T>
+//   Json::Value StoneSerialize(const std::vector<T>& value)
+//   {
+//     Json::Value result(Json::arrayValue);
+//     for (size_t i = 0; i < value.size(); ++i)
+//     {
+//       result.append(StoneSerialize(value[i]));
+//     }
+//     return result;
+//   }
+
+//   enum EnumMonth0
+//   {
+//     January,
+//     February,
+//     March
+//   };
+
+//   std::string ToString(EnumMonth0 value)
+//   {
+//     switch(value)
+//     {
+//       case January: 
+//         return "January";
+//       case February:
+//         return "February";
+//       case March:
+//         return "March";
+//       default:
+//         {
+//           std::stringstream ss;
+//           ss << "Unrecognized EnumMonth0 value (" << static_cast<int64_t>(value) << ")";
+//           throw std::runtime_error(ss.str());
+//         }
+//     }
+//   }
+
+//   void FromString(EnumMonth0& value, std::string strValue)
+//   {
+//     if (strValue == "January" || strValue == "EnumMonth0_January")
+//     {
+//       return January;
+//     }
+//     else if (strValue == "February" || strValue == "EnumMonth0_February")
+//     {
+//       return February;
+//     }
+// #error Not implemented yet
+//   }
+
+//   Json::Value StoneSerialize(const EnumMonth0& value)
+//   {
+//     return StoneSerialize(ToString(value));
+//   }
+//   struct Message1
+//   {
+//     int32_t a;
+//     std::string b;
+//     EnumMonth0 c;
+//     bool d;
+//   };
+
+//   struct Message2
+//   {
+//     std::string toto;
+//     std::vector<Message1> tata;
+//     std::vector<std::string> tutu;
+//     std::map<std::string, std::string> titi;
+//     std::map<std::string, Message1> lulu;
+//   };
+
+//   Json::Value StoneSerialize(const Message1& value)
+//   {
+//     Json::Value result(Json::objectValue);
+//     result["a"] = StoneSerialize(value.a);
+//     result["b"] = StoneSerialize(value.b);
+//     result["c"] = StoneSerialize(value.c);
+//     result["d"] = StoneSerialize(value.d);
+//     return result;
+//   }
+    
+//   Json::Value StoneSerialize(const Message2& value)
+//   {
+//     Json::Value result(Json::objectValue);
+//     result["toto"] = StoneSerialize(value.toto);
+//     result["tata"] = StoneSerialize(value.tata);
+//     result["tutu"] = StoneSerialize(value.tutu);
+//     result["titi"] = StoneSerialize(value.titi);
+//     result["lulu"] = StoneSerialize(value.lulu);
+//     return result;
+//   }
+// }
+
+// int main()
+// {
+//   VsolStuff::Message1 msg1_0;
+//   msg1_0.a = 42;
+//   msg1_0.b = "Benjamin";
+//   msg1_0.c = VsolStuff::January;
+//   msg1_0.d = true;
+
+//   VsolStuff::Message1 msg1_1;
+//   msg1_1.a = 43;
+//   msg1_1.b = "Sandrine";
+//   msg1_1.c = VsolStuff::March;
+//   msg1_0.d = false;
+
+//   // std::string toto;
+//   // std::vector<Message1> tata;
+//   // std::vector<std::string> tutu;
+//   // std::map<int32_t, std::string> titi;
+//   // std::map<int32_t, Message1> lulu;
+
+//   VsolStuff::Message2 msg2_0;
+//   msg2_0.toto = "Prout zizi";
+//   msg2_0.tata.push_back(msg1_0);
+//   msg2_0.tata.push_back(msg1_1);
+//   msg2_0.tutu.push_back("Mercadet");
+//   msg2_0.tutu.push_back("Poisson");
+//   msg2_0.titi["44"] = "key 44";
+//   msg2_0.titi["45"] = "key 45";
+//   msg2_0.lulu["54"] = msg1_1;
+//   msg2_0.lulu["55"] = msg1_0;
+//   auto result = VsolStuff::StoneSerialize(msg2_0);
+//   auto resultStr = result.toStyledString();
+
+//   Json::Value readValue;
+
+//   Json::CharReaderBuilder builder;
+//   Json::CharReader* reader = builder.newCharReader();
+//   std::string errors;
+
+//   bool ok = reader->parse(
+//     resultStr.c_str(),
+//     resultStr.c_str() + resultStr.size(),
+//     &readValue,
+//     &errors
+//   );
+//   delete reader;
+
+//   if (!ok)
+//   {
+//     std::stringstream ss;
+//     ss << "Json parsing error: " << errors;
+//     throw std::runtime_error(ss.str());
+//   }
+//   std::cout << readValue.get("toto", "Default Value").asString() << std::endl;
+//   return 0;
+// }
+
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/Graveyard/playground2.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,72 @@
+class Greeter {
+    greeting: string;
+    constructor(message: string) {
+        this.greeting = message;
+    }
+    greet() {
+        return "Hello, " + this.greeting;
+    }
+}
+enum Color {
+    Red,
+    Green,
+    Blue,
+};
+
+function ColorToString(value: Color)
+{
+    switch (value)
+    {
+        case Color.Red:
+            return "Red";
+        case Color.Green:
+            return "Green";
+        case Color.Blue:
+            return "Blue";
+        default:
+            throw new Error(`Unrecognized Color value(${value})`);
+    }
+}
+
+let color: Color = Color.Red;
+
+document.body.textContent = "<p>---------------------</p>"
+document.body.textContent += "<p>********************************</p>"
+
+class TestMessage {
+    s1: string;
+    s2: Array<string>;
+    s3: Array<Array<string>>;
+    s4: Map<string, number>;
+    s5: Map<number, Array<string>>;
+    s6: Color;
+    s7: boolean;
+}
+
+let tm = new TestMessage();
+tm.s2 = new Array<string>()
+tm.s2.push("toto");
+tm.s2.push("toto2");
+tm.s2.push("toto3");
+tm.s4 = new Map<string, number>();
+tm.s4["toto"] = 42;
+tm.s4["toto"] = 1999;
+tm.s4["tatata"] = 1999;
+tm.s6 = Color.Red;
+tm.s7 = true
+
+let txt = JSON.stringify(tm)
+let txtElem = document.createElement('textarea');
+txtElem.value = txt;
+
+document.body.appendChild(txtElem);
+
+let greeter = new Greeter("world");
+
+let button = document.createElement('button');
+button.textContent = "Say Hello";
+button.onclick = function() {
+    alert(greeter.greet());
+}
+
+document.body.appendChild(button);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/Graveyard/playground3.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,275 @@
+/*
+         1         2         3         4         5         6         7
+12345678901234567890123456789012345678901234567890123456789012345678901234567890
+*/
+
+namespace VsolStuff222 {
+  export enum EnumMonth0 {
+    January,
+    February,
+    March
+  };
+
+  export class Message1 {
+    a: number;
+    b: string;
+    c: EnumMonth0;
+    d: boolean;
+    public StoneSerialize(): string {
+      let container: object = {};
+      container['type'] = 'VsolStuff.Message1';
+      container['value'] = this;
+      return JSON.stringify(container);
+    }
+  };
+
+  export class Message2 {
+    toto: string;
+    tata: Message1[];
+    tutu: string[];
+    titi: Map<string, string>;
+    lulu: Map<string, Message1>;
+
+    public StoneSerialize(): string {
+      let container: object = {};
+      container['type'] = 'VsolStuff.Message2';
+      container['value'] = this;
+      return JSON.stringify(container);
+    }
+  };
+}
+
+function printf(value: any): void {
+  console.log(value)
+}
+
+function main(): number {
+
+  let msg1_0 = new VsolStuff.Message1();
+  msg1_0.a = 42;
+  msg1_0.b = "Benjamin";
+  msg1_0.c = VsolStuff.EnumMonth0.January;
+  msg1_0.d = true;
+
+  let msg1_1 = new VsolStuff.Message1();
+  msg1_1.a = 43;
+  msg1_1.b = "Sandrine";
+  msg1_1.c = VsolStuff.EnumMonth0.March;
+  msg1_0.d = false;
+
+  // std::string toto;
+  // std::vector<Message1> tata;
+  // std::vector<std::string> tutu;
+  // std::map<int32_t, std::string> titi;
+  // std::map<int32_t, Message1> lulu;
+
+  let msg2_0 = new VsolStuff.Message2();
+  msg2_0.toto = "Prout zizi";
+  msg2_0.tata = new Array<VsolStuff.Message1>();
+  msg2_0.tata.push(msg1_0);
+  msg2_0.tata.push(msg1_1);
+  msg2_0.tutu.push("Mercadet");
+  msg2_0.tutu.push("Poisson");ing
+  msg2_0.titi["44"] = "key 44";
+  msg2_0.titi["45"] = "key 45";
+  msg2_0.lulu["54"] = msg1_1;
+  msg2_0.lulu["55"] = msg1_0;
+  let result:string = VsolStuff.StoneSerialize(msg2_0);
+  return 0;
+}
+
+main()
+
+//   string StoneSerialize_number(int32_t value)
+//   {
+
+//     Json::Value result(value);
+//     return result;
+//   }
+
+//   Json::Value StoneSerialize(double value)
+//   {
+//     Json::Value result(value);
+//     return result;
+//   }
+
+//   Json::Value StoneSerialize(bool value)
+//   {
+//     Json::Value result(value);
+//     return result;
+//   }
+
+//   Json::Value StoneSerialize(const std::string& value)
+//   {
+//     // the following is better than 
+//     Json::Value result(value.data(),value.data()+value.size());
+//     return result;
+//   }
+
+//   template<typename T>
+//   Json::Value StoneSerialize(const std::map<std::string,T>& value)
+//   {
+//     Json::Value result(Json::objectValue);
+
+//     for (std::map<std::string, T>::const_iterator it = value.cbegin();
+//       it != value.cend(); ++it)
+//     {
+//       // it->first it->second
+//       result[it->first] = StoneSerialize(it->second);
+//     }
+//     return result;
+//   }
+
+//   template<typename T>
+//   Json::Value StoneSerialize(const std::vector<T>& value)
+//   {
+//     Json::Value result(Json::arrayValue);
+//     for (size_t i = 0; i < value.size(); ++i)
+//     {
+//       result.append(StoneSerialize(value[i]));
+//     }
+//     return result;
+//   }
+
+//   enum EnumMonth0
+//   {
+//     January,
+//     February,
+//     March
+//   };
+
+//   std::string ToString(EnumMonth0 value)
+//   {
+//     switch(value)
+//     {
+//       case January: 
+//         return "January";
+//       case February:
+//         return "February";
+//       case March:
+//         return "March";
+//       default:
+//         {
+//           std::stringstream ss;
+//           ss << "Unrecognized EnumMonth0 value (" << static_cast<int64_t>(value) << ")";
+//           throw std::runtime_error(ss.str());
+//         }
+//     }
+//   }
+
+//   void FromString(EnumMonth0& value, std::string strValue)
+//   {
+//     if (strValue == "January" || strValue == "EnumMonth0_January")
+//     {
+//       return January;
+//     }
+//     else if (strValue == "February" || strValue == "EnumMonth0_February")
+//     {
+//       return February;
+//     }
+// #error Not implemented yet
+//   }
+
+//   Json::Value StoneSerialize(const EnumMonth0& value)
+//   {
+//     return StoneSerialize(ToString(value));
+//   }
+//   struct Message1
+//   {
+//     int32_t a;
+//     std::string b;
+//     EnumMonth0 c;
+//     bool d;
+//   };
+
+//   struct Message2
+//   {
+//     std::string toto;
+//     std::vector<Message1> tata;
+//     std::vector<std::string> tutu;
+//     std::map<std::string, std::string> titi;
+//     std::map<std::string, Message1> lulu;
+//   };
+
+//   Json::Value StoneSerialize(const Message1& value)
+//   {
+//     Json::Value result(Json::objectValue);
+//     result["a"] = StoneSerialize(value.a);
+//     result["b"] = StoneSerialize(value.b);
+//     result["c"] = StoneSerialize(value.c);
+//     result["d"] = StoneSerialize(value.d);
+//     return result;
+//   }
+
+//   Json::Value StoneSerialize(const Message2& value)
+//   {
+//     Json::Value result(Json::objectValue);
+//     result["toto"] = StoneSerialize(value.toto);
+//     result["tata"] = StoneSerialize(value.tata);
+//     result["tutu"] = StoneSerialize(value.tutu);
+//     result["titi"] = StoneSerialize(value.titi);
+//     result["lulu"] = StoneSerialize(value.lulu);
+//     return result;
+//   }
+// }
+
+// int main()
+// {
+//   VsolStuff::Message1 msg1_0;
+//   msg1_0.a = 42;
+//   msg1_0.b = "Benjamin";
+//   msg1_0.c = VsolStuff::January;
+//   msg1_0.d = true;
+
+//   VsolStuff::Message1 msg1_1;
+//   msg1_1.a = 43;
+//   msg1_1.b = "Sandrine";
+//   msg1_1.c = VsolStuff::March;
+//   msg1_0.d = false;
+
+//   // std::string toto;
+//   // std::vector<Message1> tata;
+//   // std::vector<std::string> tutu;
+//   // std::map<int32_t, std::string> titi;
+//   // std::map<int32_t, Message1> lulu;
+
+//   VsolStuff::Message2 msg2_0;
+//   msg2_0.toto = "Prout zizi";
+//   msg2_0.tata.push_back(msg1_0);
+//   msg2_0.tata.push_back(msg1_1);
+//   msg2_0.tutu.push_back("Mercadet");
+//   msg2_0.tutu.push_back("Poisson");
+//   msg2_0.titi["44"] = "key 44";
+//   msg2_0.titi["45"] = "key 45";
+//   msg2_0.lulu["54"] = msg1_1;
+//   msg2_0.lulu["55"] = msg1_0;
+//   auto result = VsolStuff::StoneSerialize(msg2_0);
+//   auto resultStr = result.toStyledString();
+
+//   Json::Value readValue;
+
+//   Json::CharReaderBuilder builder;
+//   Json::CharReader* reader = builder.newCharReader();
+//   std::string errors;
+
+//   bool ok = reader->parse(
+//     resultStr.c_str(),
+//     resultStr.c_str() + resultStr.size(),
+//     &readValue,
+//     &errors
+//   );
+//   delete reader;
+
+//   if (!ok)
+//   {
+//     std::stringstream ss;
+//     ss << "Json parsing error: " << errors;
+//     throw std::runtime_error(ss.str());
+//   }
+//   std::cout << readValue.get("toto", "Default Value").asString() << std::endl;
+//   return 0;
+// }
+
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/Graveyard/playground4.py	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,68 @@
+testYaml = """
+enum SomeEnum:
+ - january
+ - feb
+
+struct Message0:
+ a: string
+
+struct Message1:
+ a: string
+ b: int32
+ c: vector<Message0>
+ d: SomeEnum = january
+ e: SomeEnum= january
+ f: SomeEnum=january
+ g: SomeEnum =january
+  
+
+# github.com/AlDanial/cloc
+header2 : 
+  cloc_version       : 1.67
+  elapsed_seconds    : int32_t
+
+header : 
+  cloc_version       : 1.67
+  elapsed_seconds    : int32_t
+  cloc_url           : vector<map<string,int32>>
+  n_files            : 1
+  n_lines            : 3
+  files_per_second   : 221.393718659277
+  lines_per_second   : 664.181155977831
+  report_file        : IDL.idl.yaml
+IDL :
+  nFiles: 1
+  blank: 0
+  comment: 2
+  code: 1
+EnumSUM: 
+  - aaa
+  - bbb
+
+SUM: 
+  blank: 0
+  comment: 2
+  code: 1
+  nFiles: 1
+"""
+
+import yaml
+
+b = yaml.load(testYaml)
+print(b)
+
+c = {
+  'enum SomeEnum': ['january', 'feb'], 
+  'struct Message0': {'a': 'string'}, 
+  'struct Message1': {
+    'a': 'string', 
+    'b': 'int32', 
+    'c': 'vector<Message0>', 
+    'd': 'vector<map<string,int32>>', 
+    'e': 'SomeEnum= january', 
+    'f': 'SomeEnum=january', 
+    'g': 'SomeEnum =january'
+  }, 
+}
+
+print(c)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/Graveyard/runts.ps1	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,57 @@
+# echo "+----------------------+"
+# echo "|    playground.ts     |"
+# echo "+----------------------+"
+
+# tsc -t ES2015 .\playground.ts; node .\playground.js
+
+# echo "+----------------------+"
+# echo "|    playground3.ts     |"
+# echo "+----------------------+"
+
+# tsc -t ES2015 .\playground3.ts; node .\playground3.js
+
+echo "+----------------------+"
+echo "|      stonegen        |"
+echo "+----------------------+"
+
+if(-not (test-Path "build")) {
+  mkdir "build"
+}
+
+echo "Generate the TS and CPP wrapper... (to build/)"
+python stonegentool.py -o "." test_data/test1.yaml
+if($LASTEXITCODE -ne 0) {
+  Write-Error ("Code generation failed!")
+  exit $LASTEXITCODE
+}
+
+echo "Compile the TS wrapper to JS... (in build/)"
+tsc --module commonjs --sourceMap -t ES2015 --outDir "build/" VsolMessages_generated.ts
+if($LASTEXITCODE -ne 0) {
+  Write-Error ("Code compilation failed!")
+  exit $LASTEXITCODE
+}
+
+echo "Compile the test app..."
+tsc --module commonjs --sourceMap -t ES2015 --outDir "build/" test_stonegen.ts
+if($LASTEXITCODE -ne 0) {
+  Write-Error ("Code compilation failed!")
+  exit $LASTEXITCODE
+}
+
+browserify "build/test_stonegen.js" "build/VsolMessages_generated.js" -o "build_browser/test_stonegen_fused.js"
+
+cp .\test_stonegen.html .\build_browser\
+
+echo "Run the test app..."
+Push-Location
+cd build_browser
+node .\test_stonegen_fused.js
+Pop-Location
+if($LASTEXITCODE -ne 0) {
+  Write-Error ("Code execution failed!")
+  exit $LASTEXITCODE
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/Graveyard/test_stonegen.html	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,1 @@
+<script type="text/javascript" src="test_stonegen_fused.js"></script>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/Graveyard/test_stonegen.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,206 @@
+import * as VsolMessages from "./VsolMessages_generated";
+
+function TEST_StoneGen_SerializeComplex() {
+  let msg1_0 = new VsolMessages.Message1();
+  msg1_0.a = 42;
+  msg1_0.b = "Benjamin";
+  msg1_0.c = VsolMessages.EnumMonth0.January;
+  msg1_0.d = true;
+  let msg1_1 = new VsolMessages.Message1();
+  msg1_1.a = 43;
+  msg1_1.b = "Sandrine";
+  msg1_1.c = VsolMessages.EnumMonth0.March;
+  msg1_0.d = false;
+  let result1_0 = msg1_0.StoneSerialize();
+  let resultStr1_0 = JSON.stringify(result1_0);
+  let result1_1 = msg1_1.StoneSerialize();
+  let resultStr1_1 = JSON.stringify(result1_1);
+  // std::string toto;
+  // std::vector<Message1> tata;
+  // std::vector<std::string> tutu;
+  // std::map<int32_t, std::string> titi;
+  // std::map<int32_t, Message1> lulu;
+  let msg2_0 = new VsolMessages.Message2();
+  msg2_0.toto = "Prout zizi";
+  msg2_0.tata.push(msg1_0);
+  msg2_0.tata.push(msg1_1);
+  msg2_0.tutu.push("Mercadet");
+  msg2_0.tutu.push("Poisson");
+  msg2_0.titi["44"] = "key 44";
+  msg2_0.titi["45"] = "key 45";
+  msg2_0.lulu["54"] = msg1_1;
+  msg2_0.lulu["55"] = msg1_0;
+  let result2 = msg2_0.StoneSerialize();
+  let resultStr2 = JSON.stringify(result2);
+  let refResult2 = `{
+"type" : "VsolMessages.Message2",
+"value" : 
+{
+  "lulu" : 
+  {
+    "54" : 
+    {
+      "a" : 43,
+      "b" : "Sandrine",
+      "c" : 2,
+      "d" : true
+    },
+    "55" : 
+    {
+      "a" : 42,
+      "b" : "Benjamin",
+      "c" : 0,
+      "d" : false
+    }
+  },
+  "tata" : 
+  [
+    {
+      "a" : 42,
+      "b" : "Benjamin",
+      "c" : 0,
+      "d" : false
+    },
+    {
+      "a" : 43,
+      "b" : "Sandrine",
+      "c" : 2,
+      "d" : true
+    }
+  ],
+  "titi" : 
+  {
+    "44" : "key 44",
+    "45" : "key 45"
+  },
+  "toto" : "Prout zizi",
+  "tutu" : 
+  [
+    "Mercadet",
+    "Poisson"
+  ]
+}
+}
+`;
+  let refResult2Obj = JSON.parse(refResult2);
+  let resultStr2Obj = JSON.parse(resultStr2);
+  if (false) {
+    if (refResult2Obj !== resultStr2Obj) {
+      console.log("Results are different!");
+      console.log(`refResult2Obj['value']['lulu']['54'] = ${refResult2Obj['value']['lulu']['54']}`);
+      console.log(`refResult2Obj['value']['lulu']['54']['a'] = ${refResult2Obj['value']['lulu']['54']['a']}`);
+      console.log("************************************************************");
+      console.log("**                  REFERENCE OBJ                         **");
+      console.log("************************************************************");
+      console.log(refResult2Obj);
+      console.log("************************************************************");
+      console.log("**                  ACTUAL OBJ                            **");
+      console.log("************************************************************");
+      console.log(resultStr2Obj);
+      console.log("************************************************************");
+      console.log("**                  REFERENCE                             **");
+      console.log("************************************************************");
+      console.log(refResult2);
+      console.log("************************************************************");
+      console.log("**                  ACTUAL                                **");
+      console.log("************************************************************");
+      console.log(resultStr2);
+      throw new Error("Wrong serialization");
+    }
+  }
+  let refResultValue = JSON.parse(resultStr2);
+  console.log(refResultValue);
+}
+class MyDispatcher {
+  message1: VsolMessages.Message1;
+  message2: VsolMessages.Message2;
+
+  HandleMessage1(value: VsolMessages.Message1) {
+    this.message1 = value;
+    return true;
+  }
+  HandleMessage2(value: VsolMessages.Message2) {
+    this.message2 = value;
+    return true;
+  }
+  HandleA(value) {
+    return true;
+  }
+  HandleB(value) {
+    return true;
+  }
+  HandleC(value) {
+    return true;
+  }
+}
+;
+function TEST_StoneGen_DeserializeOkAndNok() {
+  let serializedMessage = `{
+"type" : "VsolMessages.Message2",
+"value" : 
+{
+  "lulu" : 
+  {
+    "54" : 
+    {
+      "a" : 43,
+      "b" : "Sandrine",
+      "c" : 2,
+      "d" : true
+    },
+    "55" : 
+    {
+      "a" : 42,
+      "b" : "Benjamin",
+      "c" : 0,
+      "d" : false
+    }
+  },
+  "tata" : 
+  [
+    {
+      "a" : 42,
+      "b" : "Benjamin",
+      "c" : 0,
+      "d" : false
+    },
+    {
+      "a" : 43,
+      "b" : "Sandrine",
+      "c" : 2,
+      "d" : true
+    }
+  ],
+  "titi" : 
+  {
+    "44" : "key 44",
+    "45" : "key 45"
+  },
+  "toto" : "Prout zizi",
+  "tutu" : 
+  [
+    "Mercadet",
+    "Poisson"
+  ]
+}
+}`;
+  let myDispatcher = new MyDispatcher();
+  let ok = VsolMessages.StoneDispatchToHandler(serializedMessage, myDispatcher);
+  if (!ok) {
+    throw Error("Error when dispatching message!");
+  }
+  if (myDispatcher.message1 != undefined) {
+    throw Error("(myDispatcher.Message1 != undefined)");
+  }
+  if (myDispatcher.message2 == undefined) {
+    throw Error("(myDispatcher.Message2 == undefined)");
+  }
+  console.log("TEST_StoneGen_DeserializeOkAndNok: OK!");
+}
+function main() {
+  console.log("Entering main()");
+  TEST_StoneGen_SerializeComplex();
+  TEST_StoneGen_DeserializeOkAndNok();
+  return 0;
+}
+console.log(`Exit code is: ${main()}`);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/Graveyard/tsconfig.json	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,22 @@
+{
+  // "extends": "../../../../../orthanc-stone/Platforms/Wasm/tsconfig-stone",
+  "compilerOptions": {
+    // "outFile": "../../../WebApplication-build/to-embed/app.js",
+    // "module": "system",
+    // "sourceMap": false,
+    "lib": [
+      "es2017",
+      "es2017",
+      "dom",
+      "dom.iterable"
+    ]
+  },
+  "include": [
+    // "commands/*.ts",
+    // "logger.ts",
+    // "app.ts",
+    // "main.ts",
+    // "ui.ts",
+    // "popup.ts"
+  ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/README.md	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,20 @@
+Requirements
+----------------
+
+Install Node and npm.
+
+Then:
+- `npm install browserify`
+- `npm install typescript`
+- `npm install tsify`
+
+`testCppHandler` contains a C++ project that produces an executable 
+slurping a set of text files representing messages defined against 
+the `test_data/testTestStoneCodeGen.yaml' schema and dumping them to `cout`.
+
+'testWasmIntegrated` contains a small Web app demonstrating the 
+interaction between TypeScript and C++ in WASM.
+source ~/apps/emsdk/emsdk_env.sh
+
+
+Install Python and the following packages `pip install pyyaml yamlloader jinja2`
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/stonegentool.py	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,605 @@
+import json
+import yaml
+import re
+import os
+import sys
+from jinja2 import Template
+from io import StringIO
+import time
+import datetime
+import yamlloader
+
+"""
+         1         2         3         4         5         6         7
+12345678901234567890123456789012345678901234567890123456789012345678901234567890
+"""
+
+# see https://stackoverflow.com/a/2504457/2927708
+def trim(docstring):
+    if not docstring:
+        return ''
+    # Convert tabs to spaces (following the normal Python rules)
+    # and split into a list of lines:
+    lines = docstring.expandtabs().splitlines()
+    # Determine minimum indentation (first line doesn't count):
+    indent = sys.maxsize
+    for line in lines[1:]:
+        stripped = line.lstrip()
+        if stripped:
+            indent = min(indent, len(line) - len(stripped))
+    # Remove indentation (first line is special):
+    trimmed = [lines[0].strip()]
+    if indent < sys.maxsize:
+        for line in lines[1:]:
+            trimmed.append(line[indent:].rstrip())
+    # Strip off trailing and leading blank lines:
+    while trimmed and not trimmed[-1]:
+        trimmed.pop()
+    while trimmed and not trimmed[0]:
+        trimmed.pop(0)
+    # Return a single string:
+    return '\n'.join(trimmed)
+
+class JsonHelpers:
+    """A set of utilities to perform JSON operations"""
+
+    @staticmethod
+    def removeCommentsFromJsonContent(string):
+        """
+      Remove comments from a JSON file
+
+      Comments are not allowed in JSON but, i.e., Orthanc configuration files
+      contains C++ like comments that we need to remove before python can
+      parse the file
+      """
+        # remove all occurrence streamed comments (/*COMMENT */) from string
+        string = re.sub(re.compile("/\*.*?\*/", re.DOTALL), "", string)
+
+        # remove all occurrence singleline comments (//COMMENT\n ) from string
+        string = re.sub(re.compile("//.*?\n"), "", string)
+
+        return string
+
+    @staticmethod
+    def loadJsonWithComments(path):
+        """
+      Reads a JSON file that may contain C++ like comments
+      """
+        with open(path, "r") as fp:
+            fileContent = fp.read()
+        fileContent = JsonHelpers.removeCommentsFromJsonContent(fileContent)
+        return json.loads(fileContent)
+
+class FieldDefinition:
+
+    def __init__(self, name: str, type: str, defaultValue: str):
+        self.name = name
+        self.type = type
+        self.defaultValue = defaultValue
+
+    @staticmethod
+    def fromKeyValue(key: str, value: str):
+
+        if "=" in value:
+            splitValue = value.split(sep="=")
+            type = splitValue[0].strip(" ")
+            defaultValue = splitValue[1].strip(" ")
+        else:
+            type = value
+            defaultValue = None
+
+        return FieldDefinition(name = key, type = type, defaultValue = defaultValue)
+
+
+def LoadSchemaFromJson(filePath):
+    return JsonHelpers.loadJsonWithComments(filePath)
+
+def CanonToCpp(canonicalTypename):
+  # C++: prefix map vector and string with std::map, std::vector and
+  # std::string
+  # replace int32... by int32_t...
+  # replace float32 by float
+  # replace float64 by double
+  retVal = canonicalTypename
+  retVal = retVal.replace("map", "std::map")
+  retVal = retVal.replace("vector", "std::vector")
+  retVal = retVal.replace("set", "std::set")
+  retVal = retVal.replace("string", "std::string")
+  #uint32 and uint64 are handled by int32 and uint32 (because search and replace are done as partial words)
+  retVal = retVal.replace("int32", "int32_t")
+  retVal = retVal.replace("int64", "int64_t")
+  retVal = retVal.replace("float32", "float")
+  retVal = retVal.replace("float64", "double")
+  retVal = retVal.replace("json", "Json::Value")
+  return retVal
+
+def CanonToTs(canonicalTypename):
+  # TS: replace vector with Array and map with Map
+  # string remains string
+  # replace int32... by number
+  # replace float32... by number
+  retVal = canonicalTypename
+  retVal = retVal.replace("map", "Map")
+  retVal = retVal.replace("vector", "Array")
+  retVal = retVal.replace("set", "Set")
+  retVal = retVal.replace("uint32", "number")
+  retVal = retVal.replace("uint64", "number")
+  retVal = retVal.replace("int32", "number")
+  retVal = retVal.replace("int64", "number")
+  retVal = retVal.replace("float32", "number")
+  retVal = retVal.replace("float64", "number")
+  retVal = retVal.replace("bool", "boolean")
+  retVal = retVal.replace("json", "Object")
+  return retVal
+
+def NeedsTsConstruction(enums, tsType):
+  if tsType == 'boolean':
+    return False
+  elif tsType == 'number':
+    return False
+  elif tsType == 'string':
+    return False
+  else:
+    enumNames = []
+    for enum in enums:
+      enumNames.append(enum['name'])
+    if tsType in enumNames:
+      return False
+  return True
+
+def NeedsCppConstruction(canonTypename):
+  return False
+
+def DefaultValueToTs(enums, field:FieldDefinition):
+    tsType = CanonToTs(field.type)
+
+    enumNames = []
+    for enum in enums:
+        enumNames.append(enum['name'])
+
+    if tsType in enumNames:
+        return tsType + "." + field.defaultValue
+    else:
+        return field.defaultValue
+
+def DefaultValueToCpp(root, enums, field:FieldDefinition):
+    cppType = CanonToCpp(field.type)
+
+    enumNames = []
+    for enum in enums:
+        enumNames.append(enum['name'])
+
+    if cppType in enumNames:
+        return root + "::" + cppType + "_" + field.defaultValue
+    else:
+        return field.defaultValue
+
+def RegisterTemplateFunction(template,func):
+  """Makes a function callable by a jinja2 template"""
+  template.globals[func.__name__] = func
+  return func
+
+def MakeTemplate(templateStr):
+  template = Template(templateStr)
+  RegisterTemplateFunction(template,CanonToCpp)
+  RegisterTemplateFunction(template,CanonToTs)
+  RegisterTemplateFunction(template,NeedsTsConstruction)
+  RegisterTemplateFunction(template,NeedsCppConstruction)
+  RegisterTemplateFunction(template, DefaultValueToTs)
+  RegisterTemplateFunction(template, DefaultValueToCpp)
+  return template
+
+def MakeTemplateFromFile(templateFileName):
+
+  with open(templateFileName, "r") as templateFile:
+    templateFileContents = templateFile.read()
+    return MakeTemplate(templateFileContents)
+
+
+def EatToken(sentence):
+    """splits "A,B,C" into "A" and "B,C" where A, B and C are type names
+  (including templates) like "int32", "TotoTutu", or 
+  "map<map<int32,vector<string>>,map<string,int32>>" """
+
+    if sentence.count("<") != sentence.count(">"):
+        raise Exception(
+            "Error in the partial template type list " + str(sentence) + "."
+            + " The number of < and > do not match!"
+        )
+
+    # the template level we're currently in
+    templateLevel = 0
+    for i in range(len(sentence)):
+        if (sentence[i] == ",") and (templateLevel == 0):
+            return (sentence[0:i], sentence[i + 1 :])
+        elif sentence[i] == "<":
+            templateLevel += 1
+        elif sentence[i] == ">":
+            templateLevel -= 1
+    return (sentence, "")
+
+
+def SplitListOfTypes(typename):
+    """Splits something like
+  vector<string>,int32,map<string,map<string,int32>> 
+  in:
+  - vector<string>
+  - int32
+    map<string,map<string,int32>>
+  
+  This is not possible with a regex so 
+  """
+    stillStuffToEat = True
+    tokenList = []
+    restOfString = typename
+    while stillStuffToEat:
+        firstToken, restOfString = EatToken(restOfString)
+        tokenList.append(firstToken)
+        if restOfString == "":
+            stillStuffToEat = False
+    return tokenList
+
+
+templateRegex = \
+  re.compile(r"([a-zA-Z0-9_]*[a-zA-Z0-9_]*)<([a-zA-Z0-9_,:<>]+)>")
+
+
+def ParseTemplateType(typename):
+    """ If the type is a template like "SOMETHING<SOME<THING,EL<SE>>>", 
+    then it returns (true,"SOMETHING","SOME<THING,EL<SE>>")
+  otherwise it returns (false,"","")"""
+
+    # let's remove all whitespace from the type
+    # split without argument uses any whitespace string as separator
+    # (space, tab, newline, return or formfeed)
+    typename = "".join(typename.split())
+    matches = templateRegex.match(typename)
+    if matches == None:
+        return (False, "", [])
+    else:
+        m = matches
+        assert len(m.groups()) == 2
+        # we need to split with the commas that are outside of the
+        # defined types. Simply splitting at commas won't work
+        listOfDependentTypes = SplitListOfTypes(m.group(2))
+        return (True, m.group(1), listOfDependentTypes)
+
+def GetStructFields(struct):
+  """This filters out the special metadata key from the struct fields"""
+  return [k for k in struct.keys() if k != '__handler']
+
+def ComputeOrderFromTypeTree(
+  ancestors, 
+  genOrder, 
+  shortTypename, schema):
+
+  if shortTypename in ancestors:
+    raise Exception(
+      "Cyclic dependency chain found: the last of " + str(ancestors) +
+      + " depends on " + str(shortTypename) + " that is already in the list."
+    )
+
+  if not (shortTypename in genOrder):
+    (isTemplate, _, dependentTypenames) = ParseTemplateType(shortTypename)
+    if isTemplate:
+      # if it is a template, it HAS dependent types... They can be 
+      # anything (primitive, collection, enum, structs..). 
+      # Let's process them!
+      for dependentTypename in dependentTypenames:
+        # childAncestors = ancestors.copy()  NO TEMPLATE ANCESTOR!!!
+        # childAncestors.append(typename)
+        ComputeOrderFromTypeTree(
+            ancestors, genOrder, dependentTypename, schema
+        )
+    else:
+      # If it is not template, we are only interested if it is a 
+      # dependency that we must take into account in the dep graph,
+      # i.e., a struct.
+      if IsShortStructType(shortTypename, schema):
+        struct = schema[GetLongTypename(shortTypename, schema)]
+        # The keys in the struct dict are the member names
+        # The values in the struct dict are the member types
+        if struct:
+          # we reach this if struct is not None AND not empty
+          for field in GetStructFields(struct):
+            # we fill the chain of dependent types (starting here)
+            ancestors.append(shortTypename)
+            ComputeOrderFromTypeTree(
+              ancestors, genOrder, struct[field], schema)
+            # don't forget to restore it!
+            ancestors.pop()
+        
+        # now we're pretty sure our dependencies have been processed,
+        # we can start marking our code for generation (it might 
+        # already have been done if someone referenced us earlier)
+        if not shortTypename in genOrder:
+          genOrder.append(shortTypename)
+
+# +-----------------------+
+# |   Utility functions   |
+# +-----------------------+
+
+def IsShortStructType(typename, schema):
+  fullStructName = "struct " + typename
+  return (fullStructName in schema)
+
+def GetLongTypename(shortTypename, schema):
+  if shortTypename.startswith("enum "):
+    raise RuntimeError('shortTypename.startswith("enum "):')
+  enumName = "enum " + shortTypename
+  isEnum = enumName in schema
+
+  if shortTypename.startswith("struct "):
+    raise RuntimeError('shortTypename.startswith("struct "):')
+  structName = "struct " + shortTypename
+  isStruct = ("struct " + shortTypename) in schema
+
+  if isEnum and isStruct:
+    raise RuntimeError('Enums and structs cannot have the same name')
+
+  if isEnum:
+    return enumName
+  if isStruct:
+    return structName
+
+def IsTypename(fullName):
+  return (fullName.startswith("enum ") or fullName.startswith("struct "))
+
+def IsEnumType(fullName):
+  return fullName.startswith("enum ")
+
+def IsStructType(fullName):
+  return fullName.startswith("struct ")
+
+def GetShortTypename(fullTypename):
+  if fullTypename.startswith("struct "):
+    return fullTypename[7:] 
+  elif fullTypename.startswith("enum"):
+    return fullTypename[5:] 
+  else:
+    raise RuntimeError \
+      ('fullTypename should start with either "struct " or "enum "')
+
+def CheckSchemaSchema(schema):
+  if not "rootName" in schema:
+      raise Exception("schema lacks the 'rootName' key")
+  for name in schema.keys():
+    if (not IsEnumType(name)) and (not IsStructType(name)) and \
+      (name != 'rootName'):
+      raise RuntimeError \
+        ('Type "' + str(name) + '" should start with "enum " or "struct "')
+
+  # TODO: check enum fields are unique (in whole namespace)
+  # TODO: check struct fields are unique (in each struct)
+  # TODO: check that in the source schema, there are spaces after each colon
+
+nonTypeKeys = ['rootName']
+def GetTypesInSchema(schema):
+  """Returns the top schema keys that are actual type names"""
+  typeList = [k for k in schema if k not in nonTypeKeys]
+  return typeList
+
+# +-----------------------+
+# | Main processing logic |
+# +-----------------------+
+
+def ComputeRequiredDeclarationOrder(schema):
+  # sanity check
+  CheckSchemaSchema(schema)
+
+  # we traverse the type dependency graph and we fill a queue with
+  # the required struct types, in a bottom-up fashion, to compute
+  # the declaration order
+  # The genOrder list contains the struct full names in the order
+  # where they must be defined.
+  # We do not care about the enums here... They do not depend upon
+  # anything and we'll handle them, in their original declaration 
+  # order, at the start
+  genOrder = []
+  for fullName in GetTypesInSchema(schema):
+    if IsStructType(fullName):
+      realName = GetShortTypename(fullName)
+      ancestors = []
+      ComputeOrderFromTypeTree(ancestors, genOrder, realName, schema)
+  return genOrder
+
+def GetStructFields(fieldDict):
+  """Returns the regular (non __handler) struct fields"""
+  # the following happens for empty structs
+  if fieldDict == None:
+    return fieldDict
+  ret = {}
+  for k,v in fieldDict.items():
+    if k != "__handler":
+      ret[k] = FieldDefinition.fromKeyValue(k, v)
+    if k.startswith("__") and k != "__handler":
+      raise RuntimeError("Fields starting with __ (double underscore) are reserved names!")
+  return ret
+
+def GetStructMetadata(fieldDict):
+  """Returns the __handler struct fields (there are default values that
+     can be overridden by entries in the schema
+     Not tested because it's a fail-safe: if something is broken in this, 
+     dependent projects will not build."""
+  metadataDict = {}
+  metadataDict['handleInCpp'] = False
+  metadataDict['handleInTypescript'] = False
+  
+  if fieldDict != None:
+    for k,v in fieldDict.items():
+      if k.startswith("__") and k != "__handler":
+        raise RuntimeError("Fields starting with __ (double underscore) are reserved names")
+      if k == "__handler":
+        if type(v) == list:
+          for i in v:
+            if i == "cpp":
+              metadataDict['handleInCpp'] = True
+            elif i == "ts":
+              metadataDict['handleInTypescript'] = True
+            else:
+              raise RuntimeError("Error in schema. Allowed values for __handler are \"cpp\" or \"ts\"")
+        elif type(v) == str:
+          if v == "cpp":
+            metadataDict['handleInCpp'] = True
+          elif v == "ts":
+            metadataDict['handleInTypescript'] = True
+          else:
+            raise RuntimeError("Error in schema. Allowed values for __handler are \"cpp\" or \"ts\" (or a list of both)")
+        else:
+            raise RuntimeError("Error in schema. Allowed values for __handler are \"cpp\" or \"ts\" (or a list of both)")
+  return metadataDict
+
+def ProcessSchema(schema, genOrder):
+  # sanity check
+  CheckSchemaSchema(schema)
+
+  # let's doctor the schema to clean it up a bit
+  # order DOES NOT matter for enums, even though it's a list
+  enums = []
+  for fullName in schema.keys():
+    if IsEnumType(fullName):
+      # convert "enum Toto" to "Toto"
+      typename = GetShortTypename(fullName)
+      enum = {}
+      enum['name'] = typename
+      assert(type(schema[fullName]) == list)
+      enum['fields'] = schema[fullName] # must be a list
+      enums.append(enum)
+
+  # now that the order has been established, we actually store\
+  # the structs in the correct order
+  # the structs are like:
+  # example = [
+  #   {
+  #     "name": "Message1",
+  #     "fields": {
+  #       "someMember":"int32",
+  #       "someOtherMember":"vector<string>"
+  #     }
+  #   },
+  #   { 
+  #     "name": "Message2",
+  #     "fields": {
+  #       "someMember":"int32",
+  #       "someOtherMember22":"vector<Message1>"
+  #     }
+  #   }
+  # ]
+
+  structs = []
+  for i in range(len(genOrder)):
+    # this is already the short name
+    typename = genOrder[i]
+    fieldDict = schema["struct " + typename]
+    struct = {}
+    struct['name'] = typename
+    struct['fields'] = GetStructFields(fieldDict)
+    struct['__meta__'] = GetStructMetadata(fieldDict)
+    structs.append(struct)
+  
+  templatingDict = {}
+  templatingDict['enums'] = enums
+  templatingDict['structs'] = structs
+  templatingDict['rootName'] = schema['rootName']
+
+  return templatingDict
+
+# +-----------------------+
+# |    Write to files     |
+# +-----------------------+
+
+# def WriteStreamsToFiles(rootName: str, genc: Dict[str, StringIO]) \
+#   -> None:
+#   pass
+
+def LoadSchema(fn):
+  # latin-1 is a trick, when we do NOT care about NON-ascii chars but
+  # we wish to avoid using a decoding error handler
+  # (see http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html#files-in-an-ascii-compatible-encoding-best-effort-is-acceptable)
+  # TL;DR: all 256 values are mapped to characters in latin-1 so the file 
+  # contents never cause an error.
+  with open(fn, 'r', encoding='latin-1') as f:
+    schemaText = f.read()
+    assert(type(schemaText) == str)
+  return LoadSchemaFromString(schemaText = schemaText)
+
+def LoadSchemaFromString(schemaText:str):
+    # ensure there is a space after each colon. Otherwise, dicts could be
+    # erroneously recognized as an array of strings containing ':'
+    for i in range(len(schemaText)-1):
+      ch = schemaText[i]
+      nextCh = schemaText[i+1]
+      if ch == ':':
+        if not (nextCh == ' ' or nextCh == '\n'):
+          lineNumber = schemaText.count("\n",0,i) + 1
+          raise RuntimeError("Error at line " + str(lineNumber) + " in the schema: colons must be followed by a space or a newline!")
+    schema = yaml.load(schemaText, Loader = yamlloader.ordereddict.SafeLoader)
+    return schema
+
+def GetTemplatingDictFromSchemaFilename(fn):
+  return GetTemplatingDictFromSchema(LoadSchema(fn))
+
+def GetTemplatingDictFromSchema(schema):
+  genOrder = ComputeRequiredDeclarationOrder(schema)
+  templatingDict = ProcessSchema(schema, genOrder)
+  currentDT = datetime.datetime.now()
+  templatingDict['currentDatetime'] = str(currentDT)
+  return templatingDict
+
+# +-----------------------+
+# |      ENTRY POINT      |
+# +-----------------------+
+def Process(schemaFile, outDir):
+  tdico = GetTemplatingDictFromSchemaFilename(schemaFile)
+
+  tsTemplateFile = \
+    os.path.join(os.path.dirname(__file__), 'template.in.ts.j2')
+  template = MakeTemplateFromFile(tsTemplateFile)
+  renderedTsCode = template.render(**tdico)
+  outputTsFile = os.path.join( \
+    outDir,str(tdico['rootName']) + "_generated.ts")
+  with open(outputTsFile,"wt",encoding='utf8') as outFile:
+    outFile.write(renderedTsCode)
+
+  cppTemplateFile = \
+    os.path.join(os.path.dirname(__file__), 'template.in.h.j2')
+  template = MakeTemplateFromFile(cppTemplateFile)
+  renderedCppCode = template.render(**tdico)
+  outputCppFile = os.path.join( \
+    outDir, str(tdico['rootName']) + "_generated.hpp")
+  with open(outputCppFile,"wt",encoding='utf8') as outFile:
+    outFile.write(renderedCppCode)
+
+if __name__ == "__main__":
+  import argparse
+
+  parser = argparse.ArgumentParser(
+    usage="""stonegentool.py [-h] [-o OUT_DIR] [-v] input_schema
+    EXAMPLE: python stonegentool.py -o "generated_files/" """
+      + """ "mainSchema.yaml,App Specific Commands.json" """
+  )
+  parser.add_argument("input_schema", type=str, \
+    help="path to the schema file")
+  parser.add_argument(
+    "-o",
+    "--out_dir",
+    type=str,
+    default=".",
+    help="""path of the directory where the files 
+                          will be generated. Default is current
+                          working folder""",
+  )
+  parser.add_argument(
+    "-v",
+    "--verbosity",
+    action="count",
+    default=0,
+    help="""increase output verbosity (0 == errors 
+                          only, 1 == some verbosity, 2 == nerd
+                          mode""",
+  )
+
+  args = parser.parse_args()
+  schemaFile = args.input_schema
+  outDir = args.out_dir
+  Process(schemaFile, outDir)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/stonegentool_test.py	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,438 @@
+#
+#        1         2         3         4         5         6         7         8
+# 345678901234567890123456789012345678901234567890123456789012345678901234567890
+#
+
+from stonegentool import \
+EatToken,SplitListOfTypes,ParseTemplateType,ProcessSchema, \
+CheckSchemaSchema,LoadSchema,trim,ComputeRequiredDeclarationOrder, \
+GetTemplatingDictFromSchemaFilename,MakeTemplate,MakeTemplateFromFile,LoadSchemaFromString,GetTemplatingDictFromSchema
+import unittest
+import os
+import re
+import pprint
+from jinja2 import Template
+
+def RemoveDateTimeLine(s : str):
+  # regex are non-multiline by default, and $ does NOT match the end of the line
+  s2 = re.sub(r"^// autogenerated by stonegentool on .*\n","",s)
+  return s2
+
+class TestStonegentool(unittest.TestCase):
+  def test_EatToken_empty(self):
+    c = r""
+    a,b = EatToken(c)
+    self.assertEqual(a,r"")
+    self.assertEqual(b,r"")
+
+  def test_EatToken_simpleNonTemplate(self):
+    c = r"int32"
+    a,b = EatToken(c)
+    self.assertEqual(a,r"int32")
+    self.assertEqual(b,r"")
+
+  def test_EatToken_simpleTemplate(self):
+    c = r"vector<string>"
+    a,b = EatToken(c)
+    self.assertEqual(a,r"vector<string>")
+    self.assertEqual(b,r"")
+
+  def test_EatToken_complexTemplate(self):
+    c = r"vector<map<int64,string>>,vector<map<int32,string>>"
+    a,b = EatToken(c)
+    self.assertEqual(a,r"vector<map<int64,string>>")
+    self.assertEqual(b,r"vector<map<int32,string>>")
+
+  def test_EatToken_complexTemplates(self):
+    c = r"vector<map<vector<string>,map<int32,string>>>,map<int32,string>,map<map<int32,string>,string>"
+    a,b = EatToken(c)
+    self.assertEqual(a,r"vector<map<vector<string>,map<int32,string>>>")
+    self.assertEqual(b,r"map<int32,string>,map<map<int32,string>,string>")
+    a,b = EatToken(b)
+    self.assertEqual(a,r"map<int32,string>")
+    self.assertEqual(b,r"map<map<int32,string>,string>")
+
+  def test_SplitListOfTypes(self):
+    c = r"vector<map<vector<string>,map<int32,string>>>,map<int32,string>,map<map<int32,string>,string>"
+    lot = SplitListOfTypes(c)
+    self.assertEqual(3,len(lot))
+    self.assertEqual("vector<map<vector<string>,map<int32,string>>>",lot[0])
+    self.assertEqual("map<int32,string>",lot[1])
+    self.assertEqual("map<map<int32,string>,string>",lot[2])
+
+  def test_SplitListOfTypes_bogus(self):
+    c = r"vector<map<vector<string>,map<int32,string>>,map<int32,string>,map<map<int32,string>,string"
+    self.assertRaises(Exception,SplitListOfTypes,c) # the argument c must be passed to assertRaises, not as a normal call of SplitListOfTypes
+    
+  def test_ParseTemplateType_true(self):
+    c = "map<vector<map<int,vector<string>>>,map<vector<int>,vector<string>>>"
+    (ok,a,b) = ParseTemplateType(c)
+    self.assertEqual(ok,True)
+    self.assertEqual(a,"map")
+    self.assertEqual(b,["vector<map<int,vector<string>>>","map<vector<int>,vector<string>>"])
+
+    (ok2,a2,b2) = ParseTemplateType(b[0])
+    self.assertEqual(ok2,True)
+    self.assertEqual(a2,"vector")
+    self.assertEqual(b2,["map<int,vector<string>>"])
+
+    (ok3,a3,b3) = ParseTemplateType(b[1])
+    self.assertEqual(ok3,True)
+    self.assertEqual(a3,"map")
+    self.assertEqual(b3,["vector<int>","vector<string>"])
+
+    (ok4,a4,b4) = ParseTemplateType(b2[0])
+    self.assertEqual(ok4,True)
+    self.assertEqual(a4,"map")
+    self.assertEqual(b4,["int","vector<string>"])
+    
+  def test_ParseSchema(self):
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
+    obj = LoadSchema(fn)
+    # we're happy if it does not crash :)
+    CheckSchemaSchema(obj)
+
+  def test_ComputeRequiredDeclarationOrder(self):
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
+    obj = LoadSchema(fn)
+    genOrder: str = ComputeRequiredDeclarationOrder(obj)
+    self.assertEqual(5,len(genOrder))
+    self.assertEqual("A",genOrder[0])
+    self.assertEqual("B",genOrder[1])
+    self.assertEqual("C",genOrder[2])
+    self.assertEqual("Message1",genOrder[3])
+    self.assertEqual("Message2",genOrder[4])
+
+  # def test_GeneratePreambleEnumerationAndStructs(self):
+  #   fn = os.path.join(os.path.dirname(__file__), 'test', 'test1.jsonc')
+  #   obj = LoadSchema(fn)
+  #   (_,genc,_) = ProcessSchema(obj)
+
+  def test_genEnums(self):
+    self.maxDiff = None
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
+    obj = LoadSchema(fn)
+    genOrder: str = ComputeRequiredDeclarationOrder(obj)
+    processedSchema = ProcessSchema(obj, genOrder)
+    self.assertTrue('rootName' in processedSchema)
+
+    structs = {}
+    for v in processedSchema['structs']:
+      structs[v['name']] = v
+    enums = {}
+    for v in processedSchema['enums']:
+      enums[v['name']] = v
+
+    self.assertTrue('C' in structs)
+    self.assertTrue('someBs' in structs['C']['fields'])
+    self.assertTrue('CrispType' in enums)
+    self.assertTrue('Message1' in structs)
+    self.assertEqual('int32', structs['Message1']['fields']['memberInt32'].type)
+    self.assertEqual('string', structs['Message1']['fields']['memberString'].type)
+    self.assertEqual('EnumMonth0', structs['Message1']['fields']['memberEnumMonth'].type)
+    self.assertEqual('bool', structs['Message1']['fields']['memberBool'].type)
+    self.assertEqual('float32', structs['Message1']['fields']['memberFloat32'].type)
+    self.assertEqual('float64', structs['Message1']['fields']['memberFloat64'].type)
+
+  def test_GenerateTypeScriptEnums(self):
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
+    tdico = GetTemplatingDictFromSchemaFilename(fn)
+    template = Template("""  // end of generic methods
+{% for enum in enums%}  export enum {{enum['name']}} {
+{% for key in enum['fields']%}    {{key}},
+{%endfor%}  };
+
+{%endfor%}""")
+    renderedCode = template.render(**tdico)
+    renderedCodeRef = """  // end of generic methods
+  export enum MovieType {
+    RomCom,
+    Horror,
+    ScienceFiction,
+    Vegetables,
+  };
+
+  export enum CrispType {
+    SaltAndPepper,
+    CreamAndChives,
+    Paprika,
+    Barbecue,
+  };
+
+  export enum EnumMonth0 {
+    January,
+    February,
+    March,
+  };
+
+"""
+    self.assertEqual(renderedCodeRef,renderedCode)
+
+  def test_GenerateCplusplusEnums(self):
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
+    tdico = GetTemplatingDictFromSchemaFilename(fn)
+    template = Template("""  // end of generic methods
+{% for enum in enums%}  enum {{enum['name']}} {
+{% for key in enum['fields']%}    {{key}},
+{%endfor%}  };
+
+{%endfor%}""")
+    renderedCode = template.render(**tdico)
+    renderedCodeRef = """  // end of generic methods
+  enum MovieType {
+    RomCom,
+    Horror,
+    ScienceFiction,
+    Vegetables,
+  };
+
+  enum CrispType {
+    SaltAndPepper,
+    CreamAndChives,
+    Paprika,
+    Barbecue,
+  };
+
+  enum EnumMonth0 {
+    January,
+    February,
+    March,
+  };
+
+"""
+    self.assertEqual(renderedCodeRef,renderedCode)
+
+  def test_generateTsStructType(self):
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
+    tdico = GetTemplatingDictFromSchemaFilename(fn)
+
+#     template = MakeTemplate("""  // end of generic methods
+# {% for struct in struct%}  export class {{struct['name']}} {
+#   {% for key in struct['fields']%}    {{key}}:{{struct['fields'][key]}},
+#   {% endfor %}  
+#     constructor() {
+#   {% for key in struct['fields']%}
+#     {% if NeedsConstruction(struct['fields']['key'])}
+#       {{key}} = new {{CanonToTs(struct['fields']['key'])}};
+#     {% end if %}
+#   {% endfor %}
+#     }
+# {% endfor %}
+#     public StoneSerialize(): string {
+#       let container: object = {};
+#       container['type'] = '{{rootName}}.{{struct['name']}}';
+#       container['value'] = this;
+#       return JSON.stringify(container);
+#     }  };""")
+    template = MakeTemplate("""  // end of generic methods
+{% for struct in structs%}  export class {{struct['name']}} {
+{% for key in struct['fields']%}    {{key}}:{{CanonToTs(struct['fields'][key]['type'])}};
+{% endfor %}
+    constructor() {
+{% for key in struct['fields']%}      this.{{key}} = new {{CanonToTs(struct['fields'][key]['type'])}}();
+{% endfor %}    }
+
+    public StoneSerialize(): string {
+      let container: object = {};
+      container['type'] = '{{rootName}}.{{struct['name']}}';
+      container['value'] = this;
+      return JSON.stringify(container);
+    }
+  };
+
+{% endfor %}""")
+    renderedCode = template.render(**tdico)
+    renderedCodeRef = """  // end of generic methods
+  export class A {
+    someStrings:Array<string>;
+    someInts2:Array<number>;
+    movies:Array<MovieType>;
+
+    constructor() {
+      this.someStrings = new Array<string>();
+      this.someInts2 = new Array<number>();
+      this.movies = new Array<MovieType>();
+    }
+
+    public StoneSerialize(): string {
+      let container: object = {};
+      container['type'] = 'TestStoneCodeGen.A';
+      container['value'] = this;
+      return JSON.stringify(container);
+    }
+  };
+
+  export class B {
+    someAs:Array<A>;
+    someInts:Array<number>;
+
+    constructor() {
+      this.someAs = new Array<A>();
+      this.someInts = new Array<number>();
+    }
+
+    public StoneSerialize(): string {
+      let container: object = {};
+      container['type'] = 'TestStoneCodeGen.B';
+      container['value'] = this;
+      return JSON.stringify(container);
+    }
+  };
+
+  export class C {
+    someBs:Array<B>;
+    ddd:Array<string>;
+
+    constructor() {
+      this.someBs = new Array<B>();
+      this.ddd = new Array<string>();
+    }
+
+    public StoneSerialize(): string {
+      let container: object = {};
+      container['type'] = 'TestStoneCodeGen.C';
+      container['value'] = this;
+      return JSON.stringify(container);
+    }
+  };
+
+  export class Message1 {
+    memberInt32:number;
+    memberString:string;
+    memberEnumMonth:EnumMonth0;
+    memberBool:boolean;
+    memberFloat32:number;
+    memberFloat64:number;
+
+    constructor() {
+      this.memberInt32 = new number();
+      this.memberString = new string();
+      this.memberEnumMonth = new EnumMonth0();
+      this.memberBool = new boolean();
+      this.memberFloat32 = new number();
+      this.memberFloat64 = new number();
+    }
+
+    public StoneSerialize(): string {
+      let container: object = {};
+      container['type'] = 'TestStoneCodeGen.Message1';
+      container['value'] = this;
+      return JSON.stringify(container);
+    }
+  };
+
+  export class Message2 {
+    memberString:string;
+    memberStringWithDefault:string;
+    memberVectorOfMessage1:Array<Message1>;
+    memberVectorOfString:Array<string>;
+    memberMapStringString:Map<string, string>;
+    memberMapStringStruct:Map<string, Message1>;
+    memberMapEnumFloat:Map<CrispType, number>;
+    memberEnumMovieType:MovieType;
+    memberJson:Object;
+
+    constructor() {
+      this.memberString = new string();
+      this.memberStringWithDefault = new string();
+      this.memberVectorOfMessage1 = new Array<Message1>();
+      this.memberVectorOfString = new Array<string>();
+      this.memberMapStringString = new Map<string, string>();
+      this.memberMapStringStruct = new Map<string, Message1>();
+      this.memberMapEnumFloat = new Map<CrispType, number>();
+      this.memberEnumMovieType = new MovieType();
+      this.memberJson = new Object();
+    }
+
+    public StoneSerialize(): string {
+      let container: object = {};
+      container['type'] = 'TestStoneCodeGen.Message2';
+      container['value'] = this;
+      return JSON.stringify(container);
+    }
+  };
+
+"""
+    # print(renderedCode)
+    self.maxDiff = None
+    self.assertEqual(renderedCodeRef, renderedCode)
+
+  def test_generateWholeTsFile(self):
+    schemaFile = \
+      os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
+    tdico = GetTemplatingDictFromSchemaFilename(schemaFile)
+    tsTemplateFile = \
+      os.path.join(os.path.dirname(__file__), 'template.in.ts.j2')
+    template = MakeTemplateFromFile(tsTemplateFile)
+    renderedCode = template.render(**tdico)
+    print(renderedCode)
+
+  def test_GenerateTypeScriptHandlerInterface(self):
+    pass
+
+  def test_GenerateCppHandlerInterface(self):
+    pass
+
+  def test_GenerateTypeScriptDispatcher(self):
+    pass
+
+  def test_GenerateCppDispatcher(self):
+    pass
+
+  def test_StringDefaultValueInTs(self):
+    schema = LoadSchemaFromString("""
+                                  rootName: MyTest
+                                  struct Toto:
+                                    withoutDefault: string
+                                    withDefault: string = \"tutu\"
+                                  """)
+    tdico = GetTemplatingDictFromSchema(schema)
+
+    tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.ts.j2')
+    template = MakeTemplateFromFile(tsTemplateFile)
+    renderedCode = template.render(**tdico)
+    self.assertIn("withDefault = \"tutu\"", renderedCode)
+    # print(renderedCode)
+
+    cppTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.h.j2')
+    template = MakeTemplateFromFile(cppTemplateFile)
+    renderedCode = template.render(**tdico)
+    print(renderedCode)
+    self.assertIn("withDefault = \"tutu\"", renderedCode)
+
+
+  def test_EnumDefaultValue(self):
+    schema = LoadSchemaFromString("""
+                                  rootName: MyTest
+                                  enum MyEnum:
+                                    - Toto
+                                    - Tutu
+                                  struct Toto:
+                                    withoutDefault: MyEnum
+                                    withDefault: MyEnum = Toto
+                                  """)
+    tdico = GetTemplatingDictFromSchema(schema)
+
+    tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.ts.j2')
+    template = MakeTemplateFromFile(tsTemplateFile)
+    renderedCode = template.render(**tdico)
+    # print(renderedCode)
+    self.assertIn("withDefault = MyEnum.Toto", renderedCode)
+
+    tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.h.j2')
+    template = MakeTemplateFromFile(tsTemplateFile)
+    renderedCode = template.render(**tdico)
+    self.assertIn("withDefault = MyTest::MyEnum_Toto", renderedCode)
+    # print(renderedCode)
+
+
+# def test(self):
+#   s = 'hello world'
+#   self.assertEqual(s.split(), ['hello', 'world'])
+#   # check that s.split fails when the separator is not a string
+#   with self.assertRaises(TypeError):
+#   s.split(2)
+
+if __name__ == '__main__':
+  unittest.main()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/template.in.h.j2	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,551 @@
+/*
+         1         2         3         4         5         6         7
+12345678901234567890123456789012345678901234567890123456789012345678901234567890
+
+Generated on {{currentDatetime}} by stonegentool
+
+*/
+#pragma once
+
+#include <exception>
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <assert.h>
+#include <memory>
+#include <set>
+#include <json/json.h>
+
+//#define STONEGEN_NO_CPP11 1
+
+#ifdef STONEGEN_NO_CPP11
+#define StoneSmartPtr std::unique_ptr
+#else 
+#define StoneSmartPtr std::unique_ptr
+#endif 
+
+namespace {{rootName}}
+{
+  /** Throws in case of problem */
+  inline void _StoneDeserializeValue(int32_t& destValue, const Json::Value& jsonValue)
+  {
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asInt();
+    }
+  }
+
+  inline Json::Value _StoneSerializeValue(int32_t value)
+  {
+    Json::Value result(value);
+    return result;
+  }
+
+  inline void _StoneDeserializeValue(int64_t& destValue, const Json::Value& jsonValue)
+  {
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asInt64();
+    }
+  }
+
+  inline Json::Value _StoneSerializeValue(int64_t value)
+  {
+    Json::Value result(static_cast<Json::Value::Int64>(value));
+    return result;
+  }
+
+  inline void _StoneDeserializeValue(uint32_t& destValue, const Json::Value& jsonValue)
+  {
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asUInt();
+    }
+  }
+
+  inline Json::Value _StoneSerializeValue(uint32_t value)
+  {
+    Json::Value result(value);
+    return result;
+  }
+
+  inline void _StoneDeserializeValue(uint64_t& destValue, const Json::Value& jsonValue)
+  {
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asUInt64();
+    }
+  }
+
+  inline Json::Value _StoneSerializeValue(uint64_t value)
+  {
+    Json::Value result(static_cast<Json::Value::UInt64>(value));
+    return result;
+  }
+
+  inline void _StoneDeserializeValue(Json::Value& destValue, const Json::Value& jsonValue)
+  {
+    destValue = jsonValue;
+  }
+
+  inline Json::Value _StoneSerializeValue(Json::Value value)
+  {
+    return value;
+  }
+
+  /** Throws in case of problem */
+  inline void _StoneDeserializeValue(double& destValue, const Json::Value& jsonValue)
+  {
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asDouble();
+    }
+  }
+
+  inline Json::Value _StoneSerializeValue(double value)
+  {
+    Json::Value result(value);
+    return result;
+  }
+
+  /** Throws in case of problem */
+  inline void _StoneDeserializeValue(float& destValue, const Json::Value& jsonValue)
+  {
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asFloat();
+    }
+  }
+
+  inline Json::Value _StoneSerializeValue(float value)
+  {
+    Json::Value result(value);
+    return result;
+  }
+
+  /** Throws in case of problem */
+  inline void _StoneDeserializeValue(bool& destValue, const Json::Value& jsonValue)
+  {
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asBool();
+    }
+  }
+
+  inline Json::Value _StoneSerializeValue(bool value)
+  {
+    Json::Value result(value);
+    return result;
+  }
+
+  /** Throws in case of problem */
+  inline void _StoneDeserializeValue(
+       std::string& destValue
+     , const Json::Value& jsonValue)
+  {
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asString();
+    }
+  }
+
+  inline Json::Value _StoneSerializeValue(const std::string& value)
+  {
+    // the following is better than 
+    Json::Value result(value.data(),value.data()+value.size());
+    return result;
+  }
+
+  inline std::string MakeIndent(size_t indent)
+  {
+    char* txt = reinterpret_cast<char*>(malloc(indent+1)); // NO EXCEPTION BELOW!!!!!!!!!!!!
+    for(size_t i = 0; i < indent; ++i)
+      txt[i] = ' ';
+    txt[indent] = 0;
+    std::string retVal(txt);
+    free(txt); // NO EXCEPTION ABOVE !!!!!!!!!!
+    return retVal;
+  }
+
+  // generic dumper
+  template<typename T>
+  std::ostream& StoneDumpValue(std::ostream& out, const T& value, size_t indent)
+  {
+    out << MakeIndent(indent) << value;
+    return out;
+  }
+
+  // string dumper
+  inline std::ostream& StoneDumpValue(std::ostream& out, const std::string& value, size_t indent)
+  {
+    out << MakeIndent(indent) << "\"" << value  << "\"";
+    return out;
+  }
+
+  inline std::string ToString(const std::string& str)
+  {
+    return str;
+  }
+
+  inline void FromString(std::string& value, std::string strValue)
+  {
+    value = strValue;
+  }
+
+
+  /** Throws in case of problem */
+  template<typename TK, typename TV>
+  void _StoneDeserializeValue(
+    std::map<TK, TV>& destValue, const Json::Value& jsonValue)
+  {
+    if (!jsonValue.isNull())
+    {
+      destValue.clear();
+      for (
+        Json::Value::const_iterator itr = jsonValue.begin();
+        itr != jsonValue.end();
+        itr++)
+      {
+        std::string strKey;
+        _StoneDeserializeValue(strKey, itr.key());
+        TK key;
+        FromString(key, strKey);  // if you have a compile error here, it means that your type is not suitable to be the key of a map (or you should overwrite the FromString/ToString in template.in.h.j2)
+
+        TV innerDestValue;
+        _StoneDeserializeValue(innerDestValue, *itr);
+
+        destValue[key] = innerDestValue;
+      }
+    }
+  }
+
+  template<typename TK, typename TV>
+  Json::Value _StoneSerializeValue(const std::map<TK, TV>& value)
+  {
+    Json::Value result(Json::objectValue);
+
+    for (typename std::map<TK, TV>::const_iterator it = value.cbegin();
+      it != value.cend(); ++it)
+    {
+      // it->first it->second
+      result[ToString(it->first)] = _StoneSerializeValue(it->second);
+    }
+    return result;
+  }
+
+  template<typename TK, typename TV>
+  std::ostream& StoneDumpValue(std::ostream& out, const std::map<TK, TV>& value, size_t indent)
+  {
+    out << MakeIndent(indent) << "{\n";
+    for (typename std::map<TK, TV>::const_iterator it = value.cbegin();
+      it != value.cend(); ++it)
+    {
+      out << MakeIndent(indent+2) << "\"" << it->first << "\" : ";
+      StoneDumpValue(out, it->second, indent+2);
+      out << ", \n";
+    }
+    out << MakeIndent(indent) << "}\n";
+    return out;
+  }
+
+  /** Throws in case of problem */
+  template<typename T>
+  void _StoneDeserializeValue(
+    std::vector<T>& destValue, const Json::Value& jsonValue)
+  {
+    if (!jsonValue.isNull() && jsonValue.isArray())
+    {
+      destValue.clear();
+      destValue.reserve(jsonValue.size());
+      for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++)
+      {
+        T innerDestValue;
+        _StoneDeserializeValue(innerDestValue, jsonValue[i]);
+        destValue.push_back(innerDestValue);
+      }
+    }
+  }
+
+  template<typename T>
+  Json::Value _StoneSerializeValue(const std::vector<T>& value)
+  {
+    Json::Value result(Json::arrayValue);
+    for (size_t i = 0; i < value.size(); ++i)
+    {
+      result.append(_StoneSerializeValue(value[i]));
+    }
+    return result;
+  }
+
+  template<typename T>
+  std::ostream& StoneDumpValue(std::ostream& out, const std::vector<T>& value, size_t indent)
+  {
+    out << MakeIndent(indent) << "[\n";
+    for (size_t i = 0; i < value.size(); ++i)
+    {
+      StoneDumpValue(out, value[i], indent+2);
+      out << ", \n";
+    }
+    out << MakeIndent(indent) << "]\n";
+    return out;
+  }
+
+  /** Throws in case of problem */
+  template<typename T>
+  void _StoneDeserializeValue(
+    std::set<T>& destValue, const Json::Value& jsonValue)
+  {
+    if (!jsonValue.isNull() && jsonValue.isArray())
+    {
+      destValue.clear();
+      for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++)
+      {
+        T innerDestValue;
+        _StoneDeserializeValue(innerDestValue, jsonValue[i]);
+        destValue.insert(innerDestValue);
+      }
+    }
+  }
+
+  template<typename T>
+  Json::Value _StoneSerializeValue(const std::set<T>& value)
+  {
+    Json::Value result(Json::arrayValue);
+    for (typename std::set<T>::const_iterator it = value.begin(); it != value.end(); ++it)
+    {
+      result.append(_StoneSerializeValue(*it));
+    }
+    return result;
+  }
+
+  template<typename T>
+  std::ostream& StoneDumpValue(std::ostream& out, const std::set<T>& value, size_t indent)
+  {
+    out << MakeIndent(indent) << "[\n";
+    for (typename std::set<T>::const_iterator it = value.begin(); it != value.end(); ++it)
+    {
+      StoneDumpValue(out, *it, indent+2);
+      out << ", \n";
+    }
+    out << MakeIndent(indent) << "]\n";
+    return out;
+  }
+
+  inline void StoneCheckSerializedValueTypeGeneric(const Json::Value& value)
+  {
+    if ((!value.isMember("type")) || (!value["type"].isString()))
+    {
+      std::stringstream ss;
+      ss << "Cannot deserialize value ('type' key invalid)";
+      throw std::runtime_error(ss.str());
+    }
+  }
+
+  inline void StoneCheckSerializedValueType(
+    const Json::Value& value, std::string typeStr)
+  {
+    StoneCheckSerializedValueTypeGeneric(value);
+
+    std::string actTypeStr = value["type"].asString();
+    if (actTypeStr != typeStr)
+    {
+      std::stringstream ss;
+      ss << "Cannot deserialize type" << actTypeStr
+        << "into " << typeStr;
+      throw std::runtime_error(ss.str());
+    }
+  }
+
+  // end of generic methods
+
+// end of generic methods
+{% for enum in enums%}
+  enum {{enum['name']}} {
+{% for key in enum['fields']%}    {{enum['name']}}_{{key}},
+{%endfor%}  };
+
+  inline std::string ToString(const {{enum['name']}}& value)
+  {
+{% for key in enum['fields']%}    if( value == {{enum['name']}}_{{key}})
+    {
+      return std::string("{{key}}");
+    }
+{%endfor%}    std::stringstream ss;
+    ss << "Value \"" << value << "\" cannot be converted to {{enum['name']}}. Possible values are: "
+{% for key in enum['fields']%}        << " {{key}} = " << static_cast<int64_t>({{enum['name']}}_{{key}})  << ", " 
+{% endfor %}        << std::endl;
+    std::string msg = ss.str();
+    throw std::runtime_error(msg);
+  }
+
+  inline void FromString({{enum['name']}}& value, std::string strValue)
+  {
+{% for key in enum['fields']%}    if( strValue == std::string("{{key}}") )
+    {
+      value = {{enum['name']}}_{{key}};
+      return;
+    }
+{%endfor%}
+    std::stringstream ss;
+    ss << "String \"" << strValue << "\" cannot be converted to {{enum['name']}}. Possible values are: {% for key in enum['fields']%}{{key}} {% endfor %}";
+    std::string msg = ss.str();
+    throw std::runtime_error(msg);
+  }
+
+
+  inline void _StoneDeserializeValue(
+    {{enum['name']}}& destValue, const Json::Value& jsonValue)
+  {
+    if (!jsonValue.isNull())
+    {
+      FromString(destValue, jsonValue.asString());
+    }
+  }
+
+  inline Json::Value _StoneSerializeValue(const {{enum['name']}}& value)
+  {
+    std::string strValue = ToString(value);
+    return Json::Value(strValue);
+  }
+
+  inline std::ostream& StoneDumpValue(std::ostream& out, const {{enum['name']}}& value, size_t indent = 0)
+  {
+{% for key in enum['fields']%}    if( value == {{enum['name']}}_{{key}})
+    {
+      out << MakeIndent(indent) << "{{key}}";
+    }
+{%endfor%}    return out;
+  }
+
+{%endfor%}
+{% for struct in structs%}
+#ifdef _MSC_VER
+#pragma region {{struct['name']}}
+#endif //_MSC_VER
+
+  struct {{struct['name']}}
+  {
+{% if struct %}{% if struct['fields'] %}{% for key in struct['fields'] %}    {{CanonToCpp(struct['fields'][key]['type'])}} {{key}};
+{% endfor %}{% endif %}{% endif %}
+    {{struct['name']}}({% if struct %}{% if struct['fields'] %}{% for key in struct['fields'] %}{{CanonToCpp(struct['fields'][key]['type'])}} {{key}} = {% if struct['fields'][key]['defaultValue'] %}{{DefaultValueToCpp(rootName,enums,struct['fields'][key])}} {%else%} {{CanonToCpp(struct['fields'][key]['type'])}}() {%endif%} {{ ", " if not loop.last }}{% endfor %}{% endif %}{% endif %})
+    {
+{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}      this->{{key}} = {{key}};
+{% endfor %}{% endif %}{% endif %}    }
+  };
+
+  inline void _StoneDeserializeValue({{struct['name']}}& destValue, const Json::Value& value)
+  {
+    if (!value.isNull())
+    {
+{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}      _StoneDeserializeValue(destValue.{{key}}, value["{{key}}"]);
+{% endfor %}{% endif %}{% endif %}    }
+  }
+
+  inline Json::Value _StoneSerializeValue(const {{struct['name']}}& value)
+  {
+    Json::Value result(Json::objectValue);
+{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}    result["{{key}}"] = _StoneSerializeValue(value.{{key}});
+{% endfor %}{% endif %}{% endif %}
+    return result;
+  }
+
+  inline std::ostream& StoneDumpValue(std::ostream& out, const {{struct['name']}}& value, size_t indent = 0)
+  {
+    out << MakeIndent(indent) << "{\n";
+{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}    out << MakeIndent(indent+2) << "{{key}}: ";
+    StoneDumpValue(out, value.{{key}},indent+2);
+    out << ", \n";
+{% endfor %}{% endif %}{% endif %}
+    out << MakeIndent(indent) << "}";
+    return out;
+  }
+
+  inline void StoneDeserialize({{struct['name']}}& destValue, const Json::Value& value)
+  {
+    StoneCheckSerializedValueType(value, "{{rootName}}.{{struct['name']}}");
+    _StoneDeserializeValue(destValue, value["value"]);
+  }
+
+  inline Json::Value StoneSerializeToJson(const {{struct['name']}}& value)
+  {
+    Json::Value result(Json::objectValue);
+    result["type"] = "{{rootName}}.{{struct['name']}}";
+    result["value"] = _StoneSerializeValue(value);
+    return result;
+  }
+
+  inline std::string StoneSerialize(const {{struct['name']}}& value)
+  {
+    Json::Value resultJson = StoneSerializeToJson(value);
+    std::string resultStr = resultJson.toStyledString();
+    return resultStr;
+  }
+
+#ifdef _MSC_VER
+#pragma endregion {{struct['name']}}
+#endif //_MSC_VER
+{% endfor %}
+#ifdef _MSC_VER
+#pragma region Dispatching code
+#endif //_MSC_VER
+
+  class IHandler
+  {
+  public:
+{% for struct in structs%}{% if struct['__meta__'].handleInCpp %}    virtual bool Handle(const {{struct['name']}}& value) = 0;
+{% endif %}{% endfor %}  };
+
+  /** Service function for StoneDispatchToHandler */
+  inline bool StoneDispatchJsonToHandler(
+    const Json::Value& jsonValue, IHandler* handler)
+  {
+    StoneCheckSerializedValueTypeGeneric(jsonValue);
+    std::string type = jsonValue["type"].asString();
+    if (type == "")
+    {
+      // this should never ever happen
+      throw std::runtime_error("Caught empty type while dispatching");
+    }
+{% for struct in structs%}{% if struct['__meta__'].handleInCpp %}    else if (type == "{{rootName}}.{{struct['name']}}")
+    {
+      {{struct['name']}} value;
+      _StoneDeserializeValue(value, jsonValue["value"]);
+      return handler->Handle(value);
+    }
+{% endif %}{% endfor %}    else
+    {
+      return false;
+    }
+  }
+
+  /** Takes a serialized type and passes this to the handler */
+  inline bool StoneDispatchToHandler(std::string strValue, IHandler* handler)
+  {
+    Json::Value readValue;
+
+    Json::CharReaderBuilder builder;
+    Json::CharReader* reader = builder.newCharReader();
+
+    StoneSmartPtr<Json::CharReader> ptr(reader);
+
+    std::string errors;
+
+    bool ok = reader->parse(
+      strValue.c_str(),
+      strValue.c_str() + strValue.size(),
+      &readValue,
+      &errors
+    );
+    if (!ok)
+    {
+      std::stringstream ss;
+      ss << "Jsoncpp parsing error: " << errors;
+      throw std::runtime_error(ss.str());
+    }
+    return StoneDispatchJsonToHandler(readValue, handler);
+  }
+
+#ifdef _MSC_VER
+#pragma endregion Dispatching code
+#endif //_MSC_VER
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/template.in.ts.j2	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,136 @@
+/*
+         1         2         3         4         5         6         7
+12345678901234567890123456789012345678901234567890123456789012345678901234567890
+
+Generated on {{currentDatetime}} by stonegentool
+
+*/
+
+function StoneCheckSerializedValueType(value: any, typeStr: string)
+{
+  StoneCheckSerializedValueTypeGeneric(value);
+
+  if (value['type'] != typeStr)
+  {
+    throw new Error(
+      `Cannot deserialize type ${value['type']} into ${typeStr}`);
+  }
+}
+
+function isString(val: any) :boolean
+{
+  return ((typeof val === 'string') || (val instanceof String));
+}
+
+function StoneCheckSerializedValueTypeGeneric(value: any)
+{
+  // console.//log("+-------------------------------------------------+");
+  // console.//log("|            StoneCheckSerializedValueTypeGeneric |");
+  // console.//log("+-------------------------------------------------+");
+  // console.//log("value = ");
+  // console.//log(value);
+  if ( (!('type' in value)) || (!isString(value.type)) )
+  {
+    throw new Error(
+      "Cannot deserialize value ('type' key invalid)");
+  }
+}
+
+// end of generic methods
+{% for enum in enums%}
+export enum {{enum['name']}} {
+{% for key in enum['fields']%}  {{key}} = "{{key}}"{% if not loop.last %},{%endif%}
+{%endfor%}};
+
+export function {{enum['name']}}_FromString(strValue:string) : {{enum['name']}}
+{
+{% for key in enum['fields'] %}  if( strValue == "{{key}}" )
+  {
+    return {{enum['name']}}.{{key}};
+  }
+{%endfor%}
+  let msg : string =  `String ${strValue} cannot be converted to {{enum['name']}}. Possible values are: {% for key in enum['fields']%}{{key}}{% if not loop.last %}, {%endif%}{% endfor %}`;
+  throw new Error(msg);
+}
+
+export function {{enum['name']}}_ToString(value:{{enum['name']}}) : string
+{
+{% for key in enum['fields'] %}  if( value == {{enum['name']}}.{{key}} )
+  {
+    return "{{key}}";
+  }
+{%endfor%}
+  let msg : string = `Value ${value} cannot be converted to {{enum['name']}}. Possible values are: `;
+{% for key in enum['fields']%}  {
+    let _{{key}}_enumValue : string = {{enum['name']}}.{{key}}; // enums are strings in stonecodegen, so this will work.
+    let msg_{{key}} : string = `{{key}} (${_{{key}}_enumValue}){% if not loop.last %}, {%endif%}`;
+    msg = msg + msg_{{key}};
+  }
+{%endfor%}  throw new Error(msg);
+}
+{%endfor%}
+
+
+{% for struct in structs%}export class {{struct['name']}} {
+{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}  {{key}}:{{CanonToTs(struct['fields'][key]['type'])}};
+{% endfor %}{% endif %}{% endif %}
+  constructor() {
+{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}{% if NeedsTsConstruction(enums,CanonToTs(struct['fields'][key]['type'])) %}    this.{{key}} = new {{CanonToTs(struct['fields'][key]['type'])}}();
+{% endif %}
+{% if struct['fields'][key]['defaultValue'] %}    this.{{key}} = {{DefaultValueToTs(enums,struct['fields'][key])}};
+{% endif %}{% endfor %}{% endif %}{% endif %}  }
+
+  public StoneSerialize(): string {
+    let container: object = {};
+    container['type'] = '{{rootName}}.{{struct['name']}}';
+    container['value'] = this;
+    return JSON.stringify(container);
+  }
+
+  public static StoneDeserialize(valueStr: string) : {{struct['name']}}
+  {
+    let value: any = JSON.parse(valueStr);
+    StoneCheckSerializedValueType(value, '{{rootName}}.{{struct['name']}}');
+    let result: {{struct['name']}} = value['value'] as {{struct['name']}};
+    return result;
+  }
+}
+{% endfor %}
+export interface IHandler {
+{% for struct in structs%}{% if struct['__meta__'].handleInTypescript %}  Handle{{struct['name']}}(value:  {{struct['name']}}): boolean;
+{% endif %}{% endfor %}};
+
+/** Service function for StoneDispatchToHandler */
+export function StoneDispatchJsonToHandler(
+  jsonValue: any, handler: IHandler): boolean
+{
+  StoneCheckSerializedValueTypeGeneric(jsonValue);
+  let type: string = jsonValue["type"];
+  if (type == "")
+  {
+    // this should never ever happen
+    throw new Error("Caught empty type while dispatching");
+  }
+{% for struct in structs%}{% if struct['__meta__'].handleInTypescript %}  else if (type == "{{rootName}}.{{struct['name']}}")
+  {
+    let value = jsonValue["value"] as {{struct['name']}};
+    return handler.Handle{{struct['name']}}(value);
+  }
+{% endif %}{% endfor %}  else
+  {
+    return false;
+  }
+}
+
+/** Takes a serialized type and passes this to the handler */
+export function StoneDispatchToHandler(
+  strValue: string, handler: IHandler): boolean
+{
+  // console.//log("+------------------------------------------------+");
+  // console.//log("|            StoneDispatchToHandler              |");
+  // console.//log("+------------------------------------------------+");
+  // console.//log("strValue = ");
+  // console.//log(strValue);
+  let jsonValue: any = JSON.parse(strValue)
+  return StoneDispatchJsonToHandler(jsonValue, handler);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testCppHandler/CMakeLists.txt	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,28 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(testCppHandler)
+
+set(testCppHandler_Codegen_Deps
+  ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml 
+  ${CMAKE_CURRENT_LIST_DIR}/../template.in.h.j2
+) 
+
+add_custom_command(
+    OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/VsolMessages_generated.hpp
+    COMMAND python ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml
+    DEPENDS ${testCppHandler_Codegen_Deps}
+)
+
+include(${CMAKE_BINARY_DIR}/conanbuildinfo_multi.cmake)
+conan_basic_setup()
+
+add_executable(testCppHandler main.cpp ${CMAKE_CURRENT_BINARY_DIR}/VsolMessages_generated.hpp ${testCppHandler_Codegen_Deps})
+
+target_include_directories(testCppHandler PUBLIC ${CMAKE_BINARY_DIR})
+
+conan_target_link_libraries(testCppHandler)
+
+set_property(TARGET testCppHandler PROPERTY CXX_STANDARD 17)
+
+install(TARGETS testCppHandler DESTINATION bin)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testCppHandler/README.md	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,34 @@
+Requirements
+==============
+- Install Python 3.x (tested with 3.7)
+- Install conan with `pip install conan` (tested with 1.12.2)
+- Install CMake (tested with 3.12)
+- Under Windows: Visual Studio 2017
+- Under *nix*: Ninja
+
+How to build under *nix*
+===============================
+- Navigate to `testCppHandler` folder
+- `conan install . -g cmake`
+- `mkdir build`
+- `cd build`
+- `cmake -G "Ninja" ..`
+- `cmake --build . --config Debug` or - `cmake --build . --config Release`
+
+How to build under Windows with Visual Studio
+==============================================
+- Navigate to repo root
+- `mkdir build`
+- `cd build`
+- `conan install .. -g cmake_multi -s build_type=Release`
+- `conan install .. -g cmake_multi -s build_type=Debug`
+- `cmake -G "Visual Studio 15 2017 Win64" ..`  (modify for your current Visual Studio version)
+- `cmake --build . --config Debug` or - `cmake --build . --config Release`
+
+How to execute the test
+=======================
+- `cd test_data && testCppHandler --pattern=*.json`
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testCppHandler/conanfile.txt	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,4 @@
+[requires]
+jsoncpp/1.8.4@theirix/stable
+gtest/1.8.1@bincrafters/stable
+boost/1.69.0@conan/stable
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testCppHandler/main.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,139 @@
+#include <string>
+#include <fstream>
+#include <filesystem>
+#include <regex>
+using namespace std;
+namespace fs = std::filesystem;
+
+#include <boost/program_options.hpp>
+using namespace boost::program_options;
+
+#include "TestStoneCodeGen_generated.hpp"
+
+/**
+Transforms `str` by replacing occurrences of `oldStr` with `newStr`, using 
+plain text (*not* regular expressions.)
+*/
+static inline void ReplaceInString(
+  string& str,
+  const std::string& oldStr,
+  const std::string& newStr)
+{
+  std::string::size_type pos = 0u;
+  while ((pos = str.find(oldStr, pos)) != std::string::npos) {
+    str.replace(pos, oldStr.length(), newStr);
+    pos += newStr.length();
+  }
+}
+
+string SlurpFile(const string& fileName)
+{
+  ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate);
+
+  ifstream::pos_type fileSize = ifs.tellg();
+  ifs.seekg(0, ios::beg);
+
+  vector<char> bytes(fileSize);
+  ifs.read(bytes.data(), fileSize);
+
+  return string(bytes.data(), fileSize);
+}
+
+class MyHandler : public TestStoneCodeGen::IHandler
+{
+public:
+  virtual bool Handle(const TestStoneCodeGen::A& value) override
+  {
+    TestStoneCodeGen::StoneDumpValue(cout, value);
+    return true;
+  }
+  virtual bool Handle(const TestStoneCodeGen::B& value) override
+  {
+    TestStoneCodeGen::StoneDumpValue(cout, value);
+    return true;
+  }
+  virtual bool Handle(const TestStoneCodeGen::C& value) override
+  {
+    TestStoneCodeGen::StoneDumpValue(cout, value);
+    return true;
+  }
+  virtual bool Handle(const TestStoneCodeGen::Message1& value) override
+  {
+    TestStoneCodeGen::StoneDumpValue(cout, value);
+    return true;
+  }
+  virtual bool Handle(const TestStoneCodeGen::Message2& value) override
+  {
+    TestStoneCodeGen::StoneDumpValue(cout, value);
+    return true;
+  }
+};
+
+template<typename T>
+void ProcessPath(T filePath)
+{
+  cout << "+--------------------------------------------+\n";
+  cout << "| Processing: " << filePath.path().string() << "\n";
+  cout << "+--------------------------------------------+\n";
+  MyHandler handler;
+  auto contents = SlurpFile(filePath.path().string());
+  TestStoneCodeGen::StoneDispatchToHandler(contents, &handler);
+}
+
+int main(int argc, char** argv)
+{
+  try
+  {
+
+    options_description desc("Allowed options");
+    desc.add_options()
+      // First parameter describes option name/short name
+      // The second is parameter to option
+      // The third is description
+      ("help,h", "print usage message")
+      ("pattern,p", value<string>(), "pattern for input")
+      ;
+
+    variables_map vm;
+    store(parse_command_line(argc, argv, desc), vm);
+
+    if (vm.count("help"))
+    {
+      cout << desc << "\n";
+      return 0;
+    }
+
+    notify(vm);
+
+    string pattern = vm["pattern"].as<string>();
+
+    // tranform globbing pattern into regex
+    // we should deal with -, ., *...
+    string regexPatternStr = pattern;
+    cout << "Pattern is: " << regexPatternStr << endl;
+    ReplaceInString(regexPatternStr, "\\", "\\\\");
+    ReplaceInString(regexPatternStr, "-", "\\-");
+    ReplaceInString(regexPatternStr, ".", "\\.");
+    ReplaceInString(regexPatternStr, "*", ".*");
+    ReplaceInString(regexPatternStr, "?", ".");
+    cout << "Corresponding regex is: " << regexPatternStr << endl;
+
+    regex regexPattern(regexPatternStr);
+
+    for (auto& p : fs::directory_iterator("."))
+    {
+      auto fileName = p.path().filename().string();
+      if (regex_match(fileName, regexPattern))
+      {
+        ProcessPath(p);
+      }
+    }
+    return 0;
+
+
+  }
+  catch (exception& e)
+  {
+    cerr << e.what() << "\n";
+  }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testCppHandler/test_data/test_Message2.json	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,47 @@
+{
+  "type": "TestStoneCodeGen.Message2",
+  "value": {
+    "memberVectorOfMessage1": [
+      {
+        "memberInt32": 42,
+        "memberString": "Benjamin",
+        "memberEnumMonth": "January",
+        "memberBool": false,
+        "memberFloat32": 0.1,
+        "memberFloat64": -0.2
+      },
+      {
+        "memberInt32": 43,
+        "memberString": "Sandrine",
+        "memberEnumMonth": "March"
+      }
+    ],
+    "memberVectorOfString": [
+      "Mercadet",
+      "Poisson"
+    ],
+    "memberMapStringString": {
+      "44": "key 44",
+      "45": "key 45"
+    },
+    "memberMapStringStruct": {
+      "54": {
+        "memberInt32": 43,
+        "memberString": "Sandrine",
+        "memberEnumMonth": "March"
+      },
+      "55": {
+        "memberInt32": 42,
+        "memberString": "Benjamin",
+        "memberEnumMonth": "January",
+        "memberBool": false
+      }
+    },
+    "memberString": "Prout zizi",
+    "memberMapEnumFloat" : {
+      "SaltAndPepper" : 0.1,
+      "CreamAndChives" : -0.2
+    },
+    "memberJson" : {"custom-key": "custom-value"}
+  }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/CMakeLists.txt	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,40 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(testWasmIntegratedCpp)
+
+set(WASM_FLAGS "-s WASM=1 -O0 -g0")
+set(WASM_MODULE_NAME "TestWasmIntegratedModule" CACHE STRING "Name of the WebAssembly module")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}")
+#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js  -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${CMAKE_CURRENT_LIST_DIR}/DefaultLibrary.js -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0 -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"' -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=536870912 -s TOTAL_STACK=128000000")  # 512MB + resize
+
+add_definitions(-DORTHANC_ENABLE_WASM=1)
+
+set(testWasmIntegratedCpp_Codegen_Deps
+  ${CMAKE_CURRENT_LIST_DIR}/testWasmIntegratedCpp_api.yaml 
+  ${CMAKE_CURRENT_LIST_DIR}/../template.in.h.j2
+  ${CMAKE_CURRENT_LIST_DIR}/../template.in.ts.j2
+) 
+
+set(jsoncppRootDir ${CMAKE_CURRENT_LIST_DIR}/jsoncpp-1.8.4)
+
+add_custom_command(
+    OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.hpp ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.ts
+    COMMAND python3 ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml
+    DEPENDS ${testCppHandler_Codegen_Deps} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml 
+)
+
+add_executable(testWasmIntegratedCpp
+  main.cpp
+  ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.hpp
+  ${jsoncppRootDir}/jsoncpp.cpp
+  ${testCppHandler_Codegen_Deps})
+
+target_include_directories(testWasmIntegratedCpp  PUBLIC ${CMAKE_BINARY_DIR})
+target_include_directories(testWasmIntegratedCpp  PUBLIC ${jsoncppRootDir})
+
+set_property(TARGET testWasmIntegratedCpp PROPERTY CXX_STANDARD 11)
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/DefaultLibrary.js	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,15 @@
+// this file contains the JS method you want to expose to C++ code
+
+mergeInto(LibraryManager.library, {
+  // each time the Application updates its status, it may signal it through this method. i.e, to change the status of a button in the web interface
+  // It needs to be put in this file so that the emscripten SDK linker knows where to find it.
+  SendFreeTextFromCppJS: function(statusUpdateMessage) {
+    var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage);
+    window.SendFreeTextFromCpp(statusUpdateMessage_);
+  },
+  SendMessageFromCppJS: function(statusUpdateMessage) {
+    var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage);
+    window.SendMessageFromCpp(statusUpdateMessage_);
+  }
+});
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/build-web.sh	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,31 @@
+#!/bin/bash
+set -e
+
+mkdir -p build-final
+
+# compile TS to JS
+tsc --module commonjs --sourceMap -t ES2015 --outDir "build-tsc/" build-wasm/TestStoneCodeGen_generated.ts testWasmIntegrated.ts 
+
+# bundle JS files to final build dir 
+browserify "build-tsc/build-wasm/testWasmIntegratedCpp_generated.js" "build-tsc/testWasmIntegrated.js" -o "build-final/testWasmIntegratedApp.js"
+
+# copy WASM loader JS file to final build dir 
+cp build-wasm/testWasmIntegratedCpp.js build-final/
+
+# copy HTML start page to output dir
+cp testWasmIntegrated.html build-final/
+
+
+# copy styles to output dir
+cp styles.css build-final/
+
+# copy WASM binary to output dir
+cp build-wasm/testWasmIntegratedCpp.wasm  build-final/
+
+cp ../test_data/testTestStoneCodeGen.yaml build-final/
+cp ../testCppHandler/test_data/test_Message2.json build-final/cppHandler_test_Message2.json
+
+echo "...Serving files at http://127.0.0.1:8080/build-final/testWasmIntegrated.html"
+
+sudo python3 serve.py
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/build.sh	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+
+mkdir -p build-wasm
+cd build-wasm
+
+# shellcheck source="$HOME/apps/emsdk/emsdk_env.sh"
+source "$HOME/apps/emsdk/emsdk_env.sh"
+cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_WASM=ON ..
+
+ninja
+
+cd ..
+
+./build-web.sh
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/json/json-forwards.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,344 @@
+/// Json-cpp amalgamated forward header (http://jsoncpp.sourceforge.net/).
+/// It is intended to be used with #include "json/json-forwards.h"
+/// This header provides forward declaration for all JsonCpp types.
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+/*
+The JsonCpp library's source code, including accompanying documentation, 
+tests and demonstration applications, are licensed under the following
+conditions...
+
+Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all 
+jurisdictions which recognize such a disclaimer. In such jurisdictions, 
+this software is released into the Public Domain.
+
+In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
+2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
+The JsonCpp Authors, and is released under the terms of the MIT License (see below).
+
+In jurisdictions which recognize Public Domain property, the user of this 
+software may choose to accept it either as 1) Public Domain, 2) under the 
+conditions of the MIT License (see below), or 3) under the terms of dual 
+Public Domain/MIT License conditions described here, as they choose.
+
+The MIT License is about as close to Public Domain as a license can get, and is
+described in clear, concise terms at:
+
+   http://en.wikipedia.org/wiki/MIT_License
+   
+The full text of the MIT License follows:
+
+========================================================================
+Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+========================================================================
+(END LICENSE TEXT)
+
+The MIT license is compatible with both the GPL and commercial
+software, affording one all of the rights of Public Domain with the
+minor nuisance of being required to keep the above copyright notice
+and license text in the source code. Note also that by accepting the
+Public Domain "license" you can re-license your copy using whatever
+license you like.
+
+*/
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED
+# define JSON_FORWARD_AMALGAMATED_H_INCLUDED
+/// If defined, indicates that the source file is amalgamated
+/// to prevent private header inclusion.
+#define JSON_IS_AMALGAMATION
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/config.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_CONFIG_H_INCLUDED
+#define JSON_CONFIG_H_INCLUDED
+#include <cstddef>
+#include <cstdint>
+#include <istream>
+#include <memory>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <type_traits>
+
+/// If defined, indicates that json library is embedded in CppTL library.
+//# define JSON_IN_CPPTL 1
+
+/// If defined, indicates that json may leverage CppTL library
+//#  define JSON_USE_CPPTL 1
+/// If defined, indicates that cpptl vector based map should be used instead of
+/// std::map
+/// as Value container.
+//#  define JSON_USE_CPPTL_SMALLMAP 1
+
+// If non-zero, the library uses exceptions to report bad input instead of C
+// assertion macros. The default is to use exceptions.
+#ifndef JSON_USE_EXCEPTION
+#define JSON_USE_EXCEPTION 1
+#endif
+
+/// If defined, indicates that the source file is amalgamated
+/// to prevent private header inclusion.
+/// Remarks: it is automatically defined in the generated amalgamated header.
+// #define JSON_IS_AMALGAMATION
+
+#ifdef JSON_IN_CPPTL
+#include <cpptl/config.h>
+#ifndef JSON_USE_CPPTL
+#define JSON_USE_CPPTL 1
+#endif
+#endif
+
+#ifdef JSON_IN_CPPTL
+#define JSON_API CPPTL_API
+#elif defined(JSON_DLL_BUILD)
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#define JSON_API __declspec(dllexport)
+#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#endif // if defined(_MSC_VER)
+#elif defined(JSON_DLL)
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#define JSON_API __declspec(dllimport)
+#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#endif // if defined(_MSC_VER)
+#endif // ifdef JSON_IN_CPPTL
+#if !defined(JSON_API)
+#define JSON_API
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+#error                                                                         \
+    "ERROR:  Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities"
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+// As recommended at
+// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
+extern JSON_API int
+msvc_pre1900_c99_snprintf(char* outBuf, size_t size, const char* format, ...);
+#define jsoncpp_snprintf msvc_pre1900_c99_snprintf
+#else
+#define jsoncpp_snprintf std::snprintf
+#endif
+
+// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
+// integer
+// Storages, and 64 bits integer support is disabled.
+// #define JSON_NO_INT64 1
+
+#if defined(_MSC_VER) // MSVC
+#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
+#endif // defined(_MSC_VER)
+
+// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
+// C++11 should be used directly in JSONCPP.
+#define JSONCPP_OVERRIDE override
+
+#if __cplusplus >= 201103L
+#define JSONCPP_NOEXCEPT noexcept
+#define JSONCPP_OP_EXPLICIT explicit
+#elif defined(_MSC_VER) && _MSC_VER < 1900
+#define JSONCPP_NOEXCEPT throw()
+#define JSONCPP_OP_EXPLICIT explicit
+#elif defined(_MSC_VER) && _MSC_VER >= 1900
+#define JSONCPP_NOEXCEPT noexcept
+#define JSONCPP_OP_EXPLICIT explicit
+#else
+#define JSONCPP_NOEXCEPT throw()
+#define JSONCPP_OP_EXPLICIT
+#endif
+
+#ifndef JSON_HAS_RVALUE_REFERENCES
+
+#if defined(_MSC_VER)
+#define JSON_HAS_RVALUE_REFERENCES 1
+#endif // MSVC >= 2013
+
+#ifdef __clang__
+#if __has_feature(cxx_rvalue_references)
+#define JSON_HAS_RVALUE_REFERENCES 1
+#endif // has_feature
+
+#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
+#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
+#define JSON_HAS_RVALUE_REFERENCES 1
+#endif // GXX_EXPERIMENTAL
+
+#endif // __clang__ || __GNUC__
+
+#endif // not defined JSON_HAS_RVALUE_REFERENCES
+
+#ifndef JSON_HAS_RVALUE_REFERENCES
+#define JSON_HAS_RVALUE_REFERENCES 0
+#endif
+
+#ifdef __clang__
+#if __has_extension(attribute_deprecated_with_message)
+#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
+#endif
+#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
+#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
+#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
+#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
+#endif // GNUC version
+#endif // __clang__ || __GNUC__
+
+#if !defined(JSONCPP_DEPRECATED)
+#define JSONCPP_DEPRECATED(message)
+#endif // if !defined(JSONCPP_DEPRECATED)
+
+#if __GNUC__ >= 6
+#define JSON_USE_INT64_DOUBLE_CONVERSION 1
+#endif
+
+#if !defined(JSON_IS_AMALGAMATION)
+
+#include "allocator.h"
+#include "version.h"
+
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+typedef int Int;
+typedef unsigned int UInt;
+#if defined(JSON_NO_INT64)
+typedef int LargestInt;
+typedef unsigned int LargestUInt;
+#undef JSON_HAS_INT64
+#else                 // if defined(JSON_NO_INT64)
+// For Microsoft Visual use specific types as long long is not supported
+#if defined(_MSC_VER) // Microsoft Visual Studio
+typedef __int64 Int64;
+typedef unsigned __int64 UInt64;
+#else                 // if defined(_MSC_VER) // Other platforms, use long long
+typedef int64_t Int64;
+typedef uint64_t UInt64;
+#endif                // if defined(_MSC_VER)
+typedef Int64 LargestInt;
+typedef UInt64 LargestUInt;
+#define JSON_HAS_INT64
+#endif // if defined(JSON_NO_INT64)
+
+template <typename T>
+using Allocator = typename std::conditional<JSONCPP_USING_SECURE_MEMORY,
+                                            SecureAllocator<T>,
+                                            std::allocator<T>>::type;
+using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
+using IStringStream = std::basic_istringstream<String::value_type,
+                                               String::traits_type,
+                                               String::allocator_type>;
+using OStringStream = std::basic_ostringstream<String::value_type,
+                                               String::traits_type,
+                                               String::allocator_type>;
+using IStream = std::istream;
+using OStream = std::ostream;
+} // namespace Json
+
+// Legacy names (formerly macros).
+using JSONCPP_STRING = Json::String;
+using JSONCPP_ISTRINGSTREAM = Json::IStringStream;
+using JSONCPP_OSTRINGSTREAM = Json::OStringStream;
+using JSONCPP_ISTREAM = Json::IStream;
+using JSONCPP_OSTREAM = Json::OStream;
+
+#endif // JSON_CONFIG_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/config.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/forwards.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_FORWARDS_H_INCLUDED
+#define JSON_FORWARDS_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "config.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+
+// writer.h
+class FastWriter;
+class StyledWriter;
+
+// reader.h
+class Reader;
+
+// features.h
+class Features;
+
+// value.h
+typedef unsigned int ArrayIndex;
+class StaticString;
+class Path;
+class PathArgument;
+class Value;
+class ValueIteratorBase;
+class ValueIterator;
+class ValueConstIterator;
+
+} // namespace Json
+
+#endif // JSON_FORWARDS_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/forwards.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#endif //ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/json/json.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,2366 @@
+/// Json-cpp amalgamated header (http://jsoncpp.sourceforge.net/).
+/// It is intended to be used with #include "json/json.h"
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+/*
+The JsonCpp library's source code, including accompanying documentation, 
+tests and demonstration applications, are licensed under the following
+conditions...
+
+Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all 
+jurisdictions which recognize such a disclaimer. In such jurisdictions, 
+this software is released into the Public Domain.
+
+In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
+2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
+The JsonCpp Authors, and is released under the terms of the MIT License (see below).
+
+In jurisdictions which recognize Public Domain property, the user of this 
+software may choose to accept it either as 1) Public Domain, 2) under the 
+conditions of the MIT License (see below), or 3) under the terms of dual 
+Public Domain/MIT License conditions described here, as they choose.
+
+The MIT License is about as close to Public Domain as a license can get, and is
+described in clear, concise terms at:
+
+   http://en.wikipedia.org/wiki/MIT_License
+   
+The full text of the MIT License follows:
+
+========================================================================
+Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+========================================================================
+(END LICENSE TEXT)
+
+The MIT license is compatible with both the GPL and commercial
+software, affording one all of the rights of Public Domain with the
+minor nuisance of being required to keep the above copyright notice
+and license text in the source code. Note also that by accepting the
+Public Domain "license" you can re-license your copy using whatever
+license you like.
+
+*/
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#ifndef JSON_AMALGAMATED_H_INCLUDED
+# define JSON_AMALGAMATED_H_INCLUDED
+/// If defined, indicates that the source file is amalgamated
+/// to prevent private header inclusion.
+#define JSON_IS_AMALGAMATION
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/version.h
+// //////////////////////////////////////////////////////////////////////
+
+// DO NOT EDIT. This file (and "version") is generated by CMake.
+// Run CMake configure step to update it.
+#ifndef JSON_VERSION_H_INCLUDED
+#define JSON_VERSION_H_INCLUDED
+
+#define JSONCPP_VERSION_STRING "1.8.4"
+#define JSONCPP_VERSION_MAJOR 1
+#define JSONCPP_VERSION_MINOR 8
+#define JSONCPP_VERSION_PATCH 4
+#define JSONCPP_VERSION_QUALIFIER
+#define JSONCPP_VERSION_HEXA                                                   \
+  ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) |             \
+   (JSONCPP_VERSION_PATCH << 8))
+
+#ifdef JSONCPP_USING_SECURE_MEMORY
+#undef JSONCPP_USING_SECURE_MEMORY
+#endif
+#define JSONCPP_USING_SECURE_MEMORY 0
+// If non-zero, the library zeroes any memory that it has allocated before
+// it frees its memory.
+
+#endif // JSON_VERSION_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/version.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/allocator.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_ALLOCATOR_H_INCLUDED
+#define CPPTL_JSON_ALLOCATOR_H_INCLUDED
+
+#include <cstring>
+#include <memory>
+
+#pragma pack(push, 8)
+
+namespace Json {
+template <typename T> class SecureAllocator {
+public:
+  // Type definitions
+  using value_type = T;
+  using pointer = T*;
+  using const_pointer = const T*;
+  using reference = T&;
+  using const_reference = const T&;
+  using size_type = std::size_t;
+  using difference_type = std::ptrdiff_t;
+
+  /**
+   * Allocate memory for N items using the standard allocator.
+   */
+  pointer allocate(size_type n) {
+    // allocate using "global operator new"
+    return static_cast<pointer>(::operator new(n * sizeof(T)));
+  }
+
+  /**
+   * Release memory which was allocated for N items at pointer P.
+   *
+   * The memory block is filled with zeroes before being released.
+   * The pointer argument is tagged as "volatile" to prevent the
+   * compiler optimizing out this critical step.
+   */
+  void deallocate(volatile pointer p, size_type n) {
+    std::memset(p, 0, n * sizeof(T));
+    // free using "global operator delete"
+    ::operator delete(p);
+  }
+
+  /**
+   * Construct an item in-place at pointer P.
+   */
+  template <typename... Args> void construct(pointer p, Args&&... args) {
+    // construct using "placement new" and "perfect forwarding"
+    ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
+  }
+
+  size_type max_size() const { return size_t(-1) / sizeof(T); }
+
+  pointer address(reference x) const { return std::addressof(x); }
+
+  const_pointer address(const_reference x) const { return std::addressof(x); }
+
+  /**
+   * Destroy an item in-place at pointer P.
+   */
+  void destroy(pointer p) {
+    // destroy using "explicit destructor"
+    p->~T();
+  }
+
+  // Boilerplate
+  SecureAllocator() {}
+  template <typename U> SecureAllocator(const SecureAllocator<U>&) {}
+  template <typename U> struct rebind { using other = SecureAllocator<U>; };
+};
+
+template <typename T, typename U>
+bool operator==(const SecureAllocator<T>&, const SecureAllocator<U>&) {
+  return true;
+}
+
+template <typename T, typename U>
+bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
+  return false;
+}
+
+} // namespace Json
+
+#pragma pack(pop)
+
+#endif // CPPTL_JSON_ALLOCATOR_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/allocator.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/config.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_CONFIG_H_INCLUDED
+#define JSON_CONFIG_H_INCLUDED
+#include <cstddef>
+#include <cstdint>
+#include <istream>
+#include <memory>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <type_traits>
+
+/// If defined, indicates that json library is embedded in CppTL library.
+//# define JSON_IN_CPPTL 1
+
+/// If defined, indicates that json may leverage CppTL library
+//#  define JSON_USE_CPPTL 1
+/// If defined, indicates that cpptl vector based map should be used instead of
+/// std::map
+/// as Value container.
+//#  define JSON_USE_CPPTL_SMALLMAP 1
+
+// If non-zero, the library uses exceptions to report bad input instead of C
+// assertion macros. The default is to use exceptions.
+#ifndef JSON_USE_EXCEPTION
+#define JSON_USE_EXCEPTION 1
+#endif
+
+/// If defined, indicates that the source file is amalgamated
+/// to prevent private header inclusion.
+/// Remarks: it is automatically defined in the generated amalgamated header.
+// #define JSON_IS_AMALGAMATION
+
+#ifdef JSON_IN_CPPTL
+#include <cpptl/config.h>
+#ifndef JSON_USE_CPPTL
+#define JSON_USE_CPPTL 1
+#endif
+#endif
+
+#ifdef JSON_IN_CPPTL
+#define JSON_API CPPTL_API
+#elif defined(JSON_DLL_BUILD)
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#define JSON_API __declspec(dllexport)
+#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#endif // if defined(_MSC_VER)
+#elif defined(JSON_DLL)
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#define JSON_API __declspec(dllimport)
+#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#endif // if defined(_MSC_VER)
+#endif // ifdef JSON_IN_CPPTL
+#if !defined(JSON_API)
+#define JSON_API
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+#error                                                                         \
+    "ERROR:  Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities"
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+// As recommended at
+// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
+extern JSON_API int
+msvc_pre1900_c99_snprintf(char* outBuf, size_t size, const char* format, ...);
+#define jsoncpp_snprintf msvc_pre1900_c99_snprintf
+#else
+#define jsoncpp_snprintf std::snprintf
+#endif
+
+// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
+// integer
+// Storages, and 64 bits integer support is disabled.
+// #define JSON_NO_INT64 1
+
+#if defined(_MSC_VER) // MSVC
+#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
+#endif // defined(_MSC_VER)
+
+// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
+// C++11 should be used directly in JSONCPP.
+#define JSONCPP_OVERRIDE override
+
+#if __cplusplus >= 201103L
+#define JSONCPP_NOEXCEPT noexcept
+#define JSONCPP_OP_EXPLICIT explicit
+#elif defined(_MSC_VER) && _MSC_VER < 1900
+#define JSONCPP_NOEXCEPT throw()
+#define JSONCPP_OP_EXPLICIT explicit
+#elif defined(_MSC_VER) && _MSC_VER >= 1900
+#define JSONCPP_NOEXCEPT noexcept
+#define JSONCPP_OP_EXPLICIT explicit
+#else
+#define JSONCPP_NOEXCEPT throw()
+#define JSONCPP_OP_EXPLICIT
+#endif
+
+#ifndef JSON_HAS_RVALUE_REFERENCES
+
+#if defined(_MSC_VER)
+#define JSON_HAS_RVALUE_REFERENCES 1
+#endif // MSVC >= 2013
+
+#ifdef __clang__
+#if __has_feature(cxx_rvalue_references)
+#define JSON_HAS_RVALUE_REFERENCES 1
+#endif // has_feature
+
+#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
+#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
+#define JSON_HAS_RVALUE_REFERENCES 1
+#endif // GXX_EXPERIMENTAL
+
+#endif // __clang__ || __GNUC__
+
+#endif // not defined JSON_HAS_RVALUE_REFERENCES
+
+#ifndef JSON_HAS_RVALUE_REFERENCES
+#define JSON_HAS_RVALUE_REFERENCES 0
+#endif
+
+#ifdef __clang__
+#if __has_extension(attribute_deprecated_with_message)
+#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
+#endif
+#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
+#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
+#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
+#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
+#endif // GNUC version
+#endif // __clang__ || __GNUC__
+
+#if !defined(JSONCPP_DEPRECATED)
+#define JSONCPP_DEPRECATED(message)
+#endif // if !defined(JSONCPP_DEPRECATED)
+
+#if __GNUC__ >= 6
+#define JSON_USE_INT64_DOUBLE_CONVERSION 1
+#endif
+
+#if !defined(JSON_IS_AMALGAMATION)
+
+#include "allocator.h"
+#include "version.h"
+
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+typedef int Int;
+typedef unsigned int UInt;
+#if defined(JSON_NO_INT64)
+typedef int LargestInt;
+typedef unsigned int LargestUInt;
+#undef JSON_HAS_INT64
+#else                 // if defined(JSON_NO_INT64)
+// For Microsoft Visual use specific types as long long is not supported
+#if defined(_MSC_VER) // Microsoft Visual Studio
+typedef __int64 Int64;
+typedef unsigned __int64 UInt64;
+#else                 // if defined(_MSC_VER) // Other platforms, use long long
+typedef int64_t Int64;
+typedef uint64_t UInt64;
+#endif                // if defined(_MSC_VER)
+typedef Int64 LargestInt;
+typedef UInt64 LargestUInt;
+#define JSON_HAS_INT64
+#endif // if defined(JSON_NO_INT64)
+
+template <typename T>
+using Allocator = typename std::conditional<JSONCPP_USING_SECURE_MEMORY,
+                                            SecureAllocator<T>,
+                                            std::allocator<T>>::type;
+using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
+using IStringStream = std::basic_istringstream<String::value_type,
+                                               String::traits_type,
+                                               String::allocator_type>;
+using OStringStream = std::basic_ostringstream<String::value_type,
+                                               String::traits_type,
+                                               String::allocator_type>;
+using IStream = std::istream;
+using OStream = std::ostream;
+} // namespace Json
+
+// Legacy names (formerly macros).
+using JSONCPP_STRING = Json::String;
+using JSONCPP_ISTRINGSTREAM = Json::IStringStream;
+using JSONCPP_OSTRINGSTREAM = Json::OStringStream;
+using JSONCPP_ISTREAM = Json::IStream;
+using JSONCPP_OSTREAM = Json::OStream;
+
+#endif // JSON_CONFIG_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/config.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/forwards.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_FORWARDS_H_INCLUDED
+#define JSON_FORWARDS_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "config.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+
+// writer.h
+class FastWriter;
+class StyledWriter;
+
+// reader.h
+class Reader;
+
+// features.h
+class Features;
+
+// value.h
+typedef unsigned int ArrayIndex;
+class StaticString;
+class Path;
+class PathArgument;
+class Value;
+class ValueIteratorBase;
+class ValueIterator;
+class ValueConstIterator;
+
+} // namespace Json
+
+#endif // JSON_FORWARDS_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/forwards.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/features.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_FEATURES_H_INCLUDED
+#define CPPTL_JSON_FEATURES_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "forwards.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+#pragma pack(push, 8)
+
+namespace Json {
+
+/** \brief Configuration passed to reader and writer.
+ * This configuration object can be used to force the Reader or Writer
+ * to behave in a standard conforming way.
+ */
+class JSON_API Features {
+public:
+  /** \brief A configuration that allows all features and assumes all strings
+   * are UTF-8.
+   * - C & C++ comments are allowed
+   * - Root object can be any JSON value
+   * - Assumes Value strings are encoded in UTF-8
+   */
+  static Features all();
+
+  /** \brief A configuration that is strictly compatible with the JSON
+   * specification.
+   * - Comments are forbidden.
+   * - Root object must be either an array or an object value.
+   * - Assumes Value strings are encoded in UTF-8
+   */
+  static Features strictMode();
+
+  /** \brief Initialize the configuration like JsonConfig::allFeatures;
+   */
+  Features();
+
+  /// \c true if comments are allowed. Default: \c true.
+  bool allowComments_{true};
+
+  /// \c true if root must be either an array or an object value. Default: \c
+  /// false.
+  bool strictRoot_{false};
+
+  /// \c true if dropped null placeholders are allowed. Default: \c false.
+  bool allowDroppedNullPlaceholders_{false};
+
+  /// \c true if numeric object key are allowed. Default: \c false.
+  bool allowNumericKeys_{false};
+};
+
+} // namespace Json
+
+#pragma pack(pop)
+
+#endif // CPPTL_JSON_FEATURES_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/features.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/value.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_H_INCLUDED
+#define CPPTL_JSON_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "forwards.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <exception>
+#include <string>
+#include <vector>
+
+#ifndef JSON_USE_CPPTL_SMALLMAP
+#include <map>
+#else
+#include <cpptl/smallmap.h>
+#endif
+#ifdef JSON_USE_CPPTL
+#include <cpptl/forwards.h>
+#endif
+
+// Conditional NORETURN attribute on the throw functions would:
+// a) suppress false positives from static code analysis
+// b) possibly improve optimization opportunities.
+#if !defined(JSONCPP_NORETURN)
+#if defined(_MSC_VER)
+#define JSONCPP_NORETURN __declspec(noreturn)
+#elif defined(__GNUC__)
+#define JSONCPP_NORETURN __attribute__((__noreturn__))
+#else
+#define JSONCPP_NORETURN
+#endif
+#endif
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(push)
+#pragma warning(disable : 4251)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#pragma pack(push, 8)
+
+/** \brief JSON (JavaScript Object Notation).
+ */
+namespace Json {
+
+/** Base class for all exceptions we throw.
+ *
+ * We use nothing but these internally. Of course, STL can throw others.
+ */
+class JSON_API Exception : public std::exception {
+public:
+  Exception(String msg);
+  ~Exception() JSONCPP_NOEXCEPT override;
+  char const* what() const JSONCPP_NOEXCEPT override;
+
+protected:
+  String msg_;
+};
+
+/** Exceptions which the user cannot easily avoid.
+ *
+ * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input
+ *
+ * \remark derived from Json::Exception
+ */
+class JSON_API RuntimeError : public Exception {
+public:
+  RuntimeError(String const& msg);
+};
+
+/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros.
+ *
+ * These are precondition-violations (user bugs) and internal errors (our bugs).
+ *
+ * \remark derived from Json::Exception
+ */
+class JSON_API LogicError : public Exception {
+public:
+  LogicError(String const& msg);
+};
+
+/// used internally
+JSONCPP_NORETURN void throwRuntimeError(String const& msg);
+/// used internally
+JSONCPP_NORETURN void throwLogicError(String const& msg);
+
+/** \brief Type of the value held by a Value object.
+ */
+enum ValueType {
+  nullValue = 0, ///< 'null' value
+  intValue,      ///< signed integer value
+  uintValue,     ///< unsigned integer value
+  realValue,     ///< double value
+  stringValue,   ///< UTF-8 string value
+  booleanValue,  ///< bool value
+  arrayValue,    ///< array value (ordered list)
+  objectValue    ///< object value (collection of name/value pairs).
+};
+
+enum CommentPlacement {
+  commentBefore = 0,      ///< a comment placed on the line before a value
+  commentAfterOnSameLine, ///< a comment just after a value on the same line
+  commentAfter, ///< a comment on the line after a value (only make sense for
+  /// root value)
+  numberOfCommentPlacement
+};
+
+/** \brief Type of precision for formatting of real values.
+ */
+enum PrecisionType {
+  significantDigits = 0, ///< we set max number of significant digits in string
+  decimalPlaces          ///< we set max number of digits after "." in string
+};
+
+//# ifdef JSON_USE_CPPTL
+//   typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;
+//   typedef CppTL::AnyEnumerator<const Value &> EnumValues;
+//# endif
+
+/** \brief Lightweight wrapper to tag static string.
+ *
+ * Value constructor and objectValue member assignment takes advantage of the
+ * StaticString and avoid the cost of string duplication when storing the
+ * string or the member name.
+ *
+ * Example of usage:
+ * \code
+ * Json::Value aValue( StaticString("some text") );
+ * Json::Value object;
+ * static const StaticString code("code");
+ * object[code] = 1234;
+ * \endcode
+ */
+class JSON_API StaticString {
+public:
+  explicit StaticString(const char* czstring) : c_str_(czstring) {}
+
+  operator const char*() const { return c_str_; }
+
+  const char* c_str() const { return c_str_; }
+
+private:
+  const char* c_str_;
+};
+
+/** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
+ *
+ * This class is a discriminated union wrapper that can represents a:
+ * - signed integer [range: Value::minInt - Value::maxInt]
+ * - unsigned integer (range: 0 - Value::maxUInt)
+ * - double
+ * - UTF-8 string
+ * - boolean
+ * - 'null'
+ * - an ordered list of Value
+ * - collection of name/value pairs (javascript object)
+ *
+ * The type of the held value is represented by a #ValueType and
+ * can be obtained using type().
+ *
+ * Values of an #objectValue or #arrayValue can be accessed using operator[]()
+ * methods.
+ * Non-const methods will automatically create the a #nullValue element
+ * if it does not exist.
+ * The sequence of an #arrayValue will be automatically resized and initialized
+ * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
+ *
+ * The get() methods can be used to obtain default value in the case the
+ * required element does not exist.
+ *
+ * It is possible to iterate over the list of member keys of an object using
+ * the getMemberNames() method.
+ *
+ * \note #Value string-length fit in size_t, but keys must be < 2^30.
+ * (The reason is an implementation detail.) A #CharReader will raise an
+ * exception if a bound is exceeded to avoid security holes in your app,
+ * but the Value API does *not* check bounds. That is the responsibility
+ * of the caller.
+ */
+class JSON_API Value {
+  friend class ValueIteratorBase;
+
+public:
+  typedef std::vector<String> Members;
+  typedef ValueIterator iterator;
+  typedef ValueConstIterator const_iterator;
+  typedef Json::UInt UInt;
+  typedef Json::Int Int;
+#if defined(JSON_HAS_INT64)
+  typedef Json::UInt64 UInt64;
+  typedef Json::Int64 Int64;
+#endif // defined(JSON_HAS_INT64)
+  typedef Json::LargestInt LargestInt;
+  typedef Json::LargestUInt LargestUInt;
+  typedef Json::ArrayIndex ArrayIndex;
+
+  // Required for boost integration, e. g. BOOST_TEST
+  typedef std::string value_type;
+
+  static const Value& null; ///< We regret this reference to a global instance;
+                            ///< prefer the simpler Value().
+  static const Value& nullRef; ///< just a kludge for binary-compatibility; same
+                               ///< as null
+  static Value const& nullSingleton(); ///< Prefer this to null or nullRef.
+
+  /// Minimum signed integer value that can be stored in a Json::Value.
+  static const LargestInt minLargestInt;
+  /// Maximum signed integer value that can be stored in a Json::Value.
+  static const LargestInt maxLargestInt;
+  /// Maximum unsigned integer value that can be stored in a Json::Value.
+  static const LargestUInt maxLargestUInt;
+
+  /// Minimum signed int value that can be stored in a Json::Value.
+  static const Int minInt;
+  /// Maximum signed int value that can be stored in a Json::Value.
+  static const Int maxInt;
+  /// Maximum unsigned int value that can be stored in a Json::Value.
+  static const UInt maxUInt;
+
+#if defined(JSON_HAS_INT64)
+  /// Minimum signed 64 bits int value that can be stored in a Json::Value.
+  static const Int64 minInt64;
+  /// Maximum signed 64 bits int value that can be stored in a Json::Value.
+  static const Int64 maxInt64;
+  /// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
+  static const UInt64 maxUInt64;
+#endif // defined(JSON_HAS_INT64)
+
+  /// Default precision for real value for string representation.
+  static const UInt defaultRealPrecision;
+
+// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler
+// when using gcc and clang backend compilers.  CZString
+// cannot be defined as private.  See issue #486
+#ifdef __NVCC__
+public:
+#else
+private:
+#endif
+#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+  class CZString {
+  public:
+    enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy };
+    CZString(ArrayIndex index);
+    CZString(char const* str, unsigned length, DuplicationPolicy allocate);
+    CZString(CZString const& other);
+#if JSON_HAS_RVALUE_REFERENCES
+    CZString(CZString&& other);
+#endif
+    ~CZString();
+    CZString& operator=(const CZString& other);
+
+#if JSON_HAS_RVALUE_REFERENCES
+    CZString& operator=(CZString&& other);
+#endif
+
+    bool operator<(CZString const& other) const;
+    bool operator==(CZString const& other) const;
+    ArrayIndex index() const;
+    // const char* c_str() const; ///< \deprecated
+    char const* data() const;
+    unsigned length() const;
+    bool isStaticString() const;
+
+  private:
+    void swap(CZString& other);
+
+    struct StringStorage {
+      unsigned policy_ : 2;
+      unsigned length_ : 30; // 1GB max
+    };
+
+    char const* cstr_; // actually, a prefixed string, unless policy is noDup
+    union {
+      ArrayIndex index_;
+      StringStorage storage_;
+    };
+  };
+
+public:
+#ifndef JSON_USE_CPPTL_SMALLMAP
+  typedef std::map<CZString, Value> ObjectValues;
+#else
+  typedef CppTL::SmallMap<CZString, Value> ObjectValues;
+#endif // ifndef JSON_USE_CPPTL_SMALLMAP
+#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
+
+public:
+  /** \brief Create a default Value of the given type.
+
+    This is a very useful constructor.
+    To create an empty array, pass arrayValue.
+    To create an empty object, pass objectValue.
+    Another Value can then be set to this one by assignment.
+This is useful since clear() and resize() will not alter types.
+
+    Examples:
+\code
+Json::Value null_value; // null
+Json::Value arr_value(Json::arrayValue); // []
+Json::Value obj_value(Json::objectValue); // {}
+\endcode
+  */
+  Value(ValueType type = nullValue);
+  Value(Int value);
+  Value(UInt value);
+#if defined(JSON_HAS_INT64)
+  Value(Int64 value);
+  Value(UInt64 value);
+#endif // if defined(JSON_HAS_INT64)
+  Value(double value);
+  Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)
+  Value(const char* begin, const char* end); ///< Copy all, incl zeroes.
+  /** \brief Constructs a value from a static string.
+
+   * Like other value string constructor but do not duplicate the string for
+   * internal storage. The given string must remain alive after the call to this
+   * constructor.
+   * \note This works only for null-terminated strings. (We cannot change the
+   *   size of this class, so we have nowhere to store the length,
+   *   which might be computed later for various operations.)
+   *
+   * Example of usage:
+   * \code
+   * static StaticString foo("some text");
+   * Json::Value aValue(foo);
+   * \endcode
+   */
+  Value(const StaticString& value);
+  Value(const String& value); ///< Copy data() til size(). Embedded
+                              ///< zeroes too.
+#ifdef JSON_USE_CPPTL
+  Value(const CppTL::ConstString& value);
+#endif
+  Value(bool value);
+  Value(const Value& other);
+  Value(Value&& other);
+  ~Value();
+
+  /// \note Overwrite existing comments. To preserve comments, use
+  /// #swapPayload().
+  Value& operator=(const Value& other);
+  Value& operator=(Value&& other);
+
+  /// Swap everything.
+  void swap(Value& other);
+  /// Swap values but leave comments and source offsets in place.
+  void swapPayload(Value& other);
+
+  /// copy everything.
+  void copy(const Value& other);
+  /// copy values but leave comments and source offsets in place.
+  void copyPayload(const Value& other);
+
+  ValueType type() const;
+
+  /// Compare payload only, not comments etc.
+  bool operator<(const Value& other) const;
+  bool operator<=(const Value& other) const;
+  bool operator>=(const Value& other) const;
+  bool operator>(const Value& other) const;
+  bool operator==(const Value& other) const;
+  bool operator!=(const Value& other) const;
+  int compare(const Value& other) const;
+
+  const char* asCString() const; ///< Embedded zeroes could cause you trouble!
+#if JSONCPP_USING_SECURE_MEMORY
+  unsigned getCStringLength() const; // Allows you to understand the length of
+                                     // the CString
+#endif
+  String asString() const; ///< Embedded zeroes are possible.
+  /** Get raw char* of string-value.
+   *  \return false if !string. (Seg-fault if str or end are NULL.)
+   */
+  bool getString(char const** begin, char const** end) const;
+#ifdef JSON_USE_CPPTL
+  CppTL::ConstString asConstString() const;
+#endif
+  Int asInt() const;
+  UInt asUInt() const;
+#if defined(JSON_HAS_INT64)
+  Int64 asInt64() const;
+  UInt64 asUInt64() const;
+#endif // if defined(JSON_HAS_INT64)
+  LargestInt asLargestInt() const;
+  LargestUInt asLargestUInt() const;
+  float asFloat() const;
+  double asDouble() const;
+  bool asBool() const;
+
+  bool isNull() const;
+  bool isBool() const;
+  bool isInt() const;
+  bool isInt64() const;
+  bool isUInt() const;
+  bool isUInt64() const;
+  bool isIntegral() const;
+  bool isDouble() const;
+  bool isNumeric() const;
+  bool isString() const;
+  bool isArray() const;
+  bool isObject() const;
+
+  bool isConvertibleTo(ValueType other) const;
+
+  /// Number of values in array or object
+  ArrayIndex size() const;
+
+  /// \brief Return true if empty array, empty object, or null;
+  /// otherwise, false.
+  bool empty() const;
+
+  /// Return !isNull()
+  JSONCPP_OP_EXPLICIT operator bool() const;
+
+  /// Remove all object members and array elements.
+  /// \pre type() is arrayValue, objectValue, or nullValue
+  /// \post type() is unchanged
+  void clear();
+
+  /// Resize the array to newSize elements.
+  /// New elements are initialized to null.
+  /// May only be called on nullValue or arrayValue.
+  /// \pre type() is arrayValue or nullValue
+  /// \post type() is arrayValue
+  void resize(ArrayIndex newSize);
+
+  /// Access an array element (zero based index ).
+  /// If the array contains less than index element, then null value are
+  /// inserted
+  /// in the array so that its size is index+1.
+  /// (You may need to say 'value[0u]' to get your compiler to distinguish
+  ///  this from the operator[] which takes a string.)
+  Value& operator[](ArrayIndex index);
+
+  /// Access an array element (zero based index ).
+  /// If the array contains less than index element, then null value are
+  /// inserted
+  /// in the array so that its size is index+1.
+  /// (You may need to say 'value[0u]' to get your compiler to distinguish
+  ///  this from the operator[] which takes a string.)
+  Value& operator[](int index);
+
+  /// Access an array element (zero based index )
+  /// (You may need to say 'value[0u]' to get your compiler to distinguish
+  ///  this from the operator[] which takes a string.)
+  const Value& operator[](ArrayIndex index) const;
+
+  /// Access an array element (zero based index )
+  /// (You may need to say 'value[0u]' to get your compiler to distinguish
+  ///  this from the operator[] which takes a string.)
+  const Value& operator[](int index) const;
+
+  /// If the array contains at least index+1 elements, returns the element
+  /// value,
+  /// otherwise returns defaultValue.
+  Value get(ArrayIndex index, const Value& defaultValue) const;
+  /// Return true if index < size().
+  bool isValidIndex(ArrayIndex index) const;
+  /// \brief Append value to array at the end.
+  ///
+  /// Equivalent to jsonvalue[jsonvalue.size()] = value;
+  Value& append(const Value& value);
+
+#if JSON_HAS_RVALUE_REFERENCES
+  Value& append(Value&& value);
+#endif
+
+  /// Access an object value by name, create a null member if it does not exist.
+  /// \note Because of our implementation, keys are limited to 2^30 -1 chars.
+  ///  Exceeding that will cause an exception.
+  Value& operator[](const char* key);
+  /// Access an object value by name, returns null if there is no member with
+  /// that name.
+  const Value& operator[](const char* key) const;
+  /// Access an object value by name, create a null member if it does not exist.
+  /// \param key may contain embedded nulls.
+  Value& operator[](const String& key);
+  /// Access an object value by name, returns null if there is no member with
+  /// that name.
+  /// \param key may contain embedded nulls.
+  const Value& operator[](const String& key) const;
+  /** \brief Access an object value by name, create a null member if it does not
+   exist.
+
+   * If the object has no entry for that name, then the member name used to
+   store
+   * the new entry is not duplicated.
+   * Example of use:
+   * \code
+   * Json::Value object;
+   * static const StaticString code("code");
+   * object[code] = 1234;
+   * \endcode
+   */
+  Value& operator[](const StaticString& key);
+#ifdef JSON_USE_CPPTL
+  /// Access an object value by name, create a null member if it does not exist.
+  Value& operator[](const CppTL::ConstString& key);
+  /// Access an object value by name, returns null if there is no member with
+  /// that name.
+  const Value& operator[](const CppTL::ConstString& key) const;
+#endif
+  /// Return the member named key if it exist, defaultValue otherwise.
+  /// \note deep copy
+  Value get(const char* key, const Value& defaultValue) const;
+  /// Return the member named key if it exist, defaultValue otherwise.
+  /// \note deep copy
+  /// \note key may contain embedded nulls.
+  Value
+  get(const char* begin, const char* end, const Value& defaultValue) const;
+  /// Return the member named key if it exist, defaultValue otherwise.
+  /// \note deep copy
+  /// \param key may contain embedded nulls.
+  Value get(const String& key, const Value& defaultValue) const;
+#ifdef JSON_USE_CPPTL
+  /// Return the member named key if it exist, defaultValue otherwise.
+  /// \note deep copy
+  Value get(const CppTL::ConstString& key, const Value& defaultValue) const;
+#endif
+  /// Most general and efficient version of isMember()const, get()const,
+  /// and operator[]const
+  /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
+  Value const* find(char const* begin, char const* end) const;
+  /// Most general and efficient version of object-mutators.
+  /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
+  /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
+  Value const* demand(char const* begin, char const* end);
+  /// \brief Remove and return the named member.
+  ///
+  /// Do nothing if it did not exist.
+  /// \pre type() is objectValue or nullValue
+  /// \post type() is unchanged
+  void removeMember(const char* key);
+  /// Same as removeMember(const char*)
+  /// \param key may contain embedded nulls.
+  void removeMember(const String& key);
+  /// Same as removeMember(const char* begin, const char* end, Value* removed),
+  /// but 'key' is null-terminated.
+  bool removeMember(const char* key, Value* removed);
+  /** \brief Remove the named map member.
+
+      Update 'removed' iff removed.
+      \param key may contain embedded nulls.
+      \return true iff removed (no exceptions)
+  */
+  bool removeMember(String const& key, Value* removed);
+  /// Same as removeMember(String const& key, Value* removed)
+  bool removeMember(const char* begin, const char* end, Value* removed);
+  /** \brief Remove the indexed array element.
+
+      O(n) expensive operations.
+      Update 'removed' iff removed.
+      \return true if removed (no exceptions)
+  */
+  bool removeIndex(ArrayIndex index, Value* removed);
+
+  /// Return true if the object has a member named key.
+  /// \note 'key' must be null-terminated.
+  bool isMember(const char* key) const;
+  /// Return true if the object has a member named key.
+  /// \param key may contain embedded nulls.
+  bool isMember(const String& key) const;
+  /// Same as isMember(String const& key)const
+  bool isMember(const char* begin, const char* end) const;
+#ifdef JSON_USE_CPPTL
+  /// Return true if the object has a member named key.
+  bool isMember(const CppTL::ConstString& key) const;
+#endif
+
+  /// \brief Return a list of the member names.
+  ///
+  /// If null, return an empty list.
+  /// \pre type() is objectValue or nullValue
+  /// \post if type() was nullValue, it remains nullValue
+  Members getMemberNames() const;
+
+  //# ifdef JSON_USE_CPPTL
+  //      EnumMemberNames enumMemberNames() const;
+  //      EnumValues enumValues() const;
+  //# endif
+
+  /// \deprecated Always pass len.
+  JSONCPP_DEPRECATED("Use setComment(String const&) instead.")
+  void setComment(const char* comment, CommentPlacement placement);
+  /// Comments must be //... or /* ... */
+  void setComment(const char* comment, size_t len, CommentPlacement placement);
+  /// Comments must be //... or /* ... */
+  void setComment(const String& comment, CommentPlacement placement);
+  bool hasComment(CommentPlacement placement) const;
+  /// Include delimiters and embedded newlines.
+  String getComment(CommentPlacement placement) const;
+
+  String toStyledString() const;
+
+  const_iterator begin() const;
+  const_iterator end() const;
+
+  iterator begin();
+  iterator end();
+
+  // Accessors for the [start, limit) range of bytes within the JSON text from
+  // which this value was parsed, if any.
+  void setOffsetStart(ptrdiff_t start);
+  void setOffsetLimit(ptrdiff_t limit);
+  ptrdiff_t getOffsetStart() const;
+  ptrdiff_t getOffsetLimit() const;
+
+private:
+  void setType(ValueType v) { bits_.value_type_ = v; }
+  bool isAllocated() const { return bits_.allocated_; }
+  void setIsAllocated(bool v) { bits_.allocated_ = v; }
+
+  void initBasic(ValueType type, bool allocated = false);
+  void dupPayload(const Value& other);
+  void releasePayload();
+  void dupMeta(const Value& other);
+
+  Value& resolveReference(const char* key);
+  Value& resolveReference(const char* key, const char* end);
+
+  struct CommentInfo {
+    CommentInfo();
+    ~CommentInfo();
+
+    void setComment(const char* text, size_t len);
+
+    char* comment_{nullptr};
+  };
+
+  // struct MemberNamesTransform
+  //{
+  //   typedef const char *result_type;
+  //   const char *operator()( const CZString &name ) const
+  //   {
+  //      return name.c_str();
+  //   }
+  //};
+
+  union ValueHolder {
+    LargestInt int_;
+    LargestUInt uint_;
+    double real_;
+    bool bool_;
+    char* string_; // if allocated_, ptr to { unsigned, char[] }.
+    ObjectValues* map_;
+  } value_;
+
+  struct {
+    // Really a ValueType, but types should agree for bitfield packing.
+    unsigned int value_type_ : 8;
+    // Unless allocated_, string_ must be null-terminated.
+    unsigned int allocated_ : 1;
+  } bits_;
+
+  CommentInfo* comments_;
+
+  // [start, limit) byte offsets in the source JSON text from which this Value
+  // was extracted.
+  ptrdiff_t start_;
+  ptrdiff_t limit_;
+};
+
+/** \brief Experimental and untested: represents an element of the "path" to
+ * access a node.
+ */
+class JSON_API PathArgument {
+public:
+  friend class Path;
+
+  PathArgument();
+  PathArgument(ArrayIndex index);
+  PathArgument(const char* key);
+  PathArgument(const String& key);
+
+private:
+  enum Kind { kindNone = 0, kindIndex, kindKey };
+  String key_;
+  ArrayIndex index_{};
+  Kind kind_{kindNone};
+};
+
+/** \brief Experimental and untested: represents a "path" to access a node.
+ *
+ * Syntax:
+ * - "." => root node
+ * - ".[n]" => elements at index 'n' of root node (an array value)
+ * - ".name" => member named 'name' of root node (an object value)
+ * - ".name1.name2.name3"
+ * - ".[0][1][2].name1[3]"
+ * - ".%" => member name is provided as parameter
+ * - ".[%]" => index is provied as parameter
+ */
+class JSON_API Path {
+public:
+  Path(const String& path,
+       const PathArgument& a1 = PathArgument(),
+       const PathArgument& a2 = PathArgument(),
+       const PathArgument& a3 = PathArgument(),
+       const PathArgument& a4 = PathArgument(),
+       const PathArgument& a5 = PathArgument());
+
+  const Value& resolve(const Value& root) const;
+  Value resolve(const Value& root, const Value& defaultValue) const;
+  /// Creates the "path" to access the specified node and returns a reference on
+  /// the node.
+  Value& make(Value& root) const;
+
+private:
+  typedef std::vector<const PathArgument*> InArgs;
+  typedef std::vector<PathArgument> Args;
+
+  void makePath(const String& path, const InArgs& in);
+  void addPathInArg(const String& path,
+                    const InArgs& in,
+                    InArgs::const_iterator& itInArg,
+                    PathArgument::Kind kind);
+  static void invalidPath(const String& path, int location);
+
+  Args args_;
+};
+
+/** \brief base class for Value iterators.
+ *
+ */
+class JSON_API ValueIteratorBase {
+public:
+  typedef std::bidirectional_iterator_tag iterator_category;
+  typedef unsigned int size_t;
+  typedef int difference_type;
+  typedef ValueIteratorBase SelfType;
+
+  bool operator==(const SelfType& other) const { return isEqual(other); }
+
+  bool operator!=(const SelfType& other) const { return !isEqual(other); }
+
+  difference_type operator-(const SelfType& other) const {
+    return other.computeDistance(*this);
+  }
+
+  /// Return either the index or the member name of the referenced value as a
+  /// Value.
+  Value key() const;
+
+  /// Return the index of the referenced Value, or -1 if it is not an
+  /// arrayValue.
+  UInt index() const;
+
+  /// Return the member name of the referenced Value, or "" if it is not an
+  /// objectValue.
+  /// \note Avoid `c_str()` on result, as embedded zeroes are possible.
+  String name() const;
+
+  /// Return the member name of the referenced Value. "" if it is not an
+  /// objectValue.
+  /// \deprecated This cannot be used for UTF-8 strings, since there can be
+  /// embedded nulls.
+  JSONCPP_DEPRECATED("Use `key = name();` instead.")
+  char const* memberName() const;
+  /// Return the member name of the referenced Value, or NULL if it is not an
+  /// objectValue.
+  /// \note Better version than memberName(). Allows embedded nulls.
+  char const* memberName(char const** end) const;
+
+protected:
+  Value& deref() const;
+
+  void increment();
+
+  void decrement();
+
+  difference_type computeDistance(const SelfType& other) const;
+
+  bool isEqual(const SelfType& other) const;
+
+  void copy(const SelfType& other);
+
+private:
+  Value::ObjectValues::iterator current_;
+  // Indicates that iterator is for a null value.
+  bool isNull_{true};
+
+public:
+  // For some reason, BORLAND needs these at the end, rather
+  // than earlier. No idea why.
+  ValueIteratorBase();
+  explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);
+};
+
+/** \brief const iterator for object and array value.
+ *
+ */
+class JSON_API ValueConstIterator : public ValueIteratorBase {
+  friend class Value;
+
+public:
+  typedef const Value value_type;
+  // typedef unsigned int size_t;
+  // typedef int difference_type;
+  typedef const Value& reference;
+  typedef const Value* pointer;
+  typedef ValueConstIterator SelfType;
+
+  ValueConstIterator();
+  ValueConstIterator(ValueIterator const& other);
+
+private:
+  /*! \internal Use by Value to create an iterator.
+   */
+  explicit ValueConstIterator(const Value::ObjectValues::iterator& current);
+
+public:
+  SelfType& operator=(const ValueIteratorBase& other);
+
+  SelfType operator++(int) {
+    SelfType temp(*this);
+    ++*this;
+    return temp;
+  }
+
+  SelfType operator--(int) {
+    SelfType temp(*this);
+    --*this;
+    return temp;
+  }
+
+  SelfType& operator--() {
+    decrement();
+    return *this;
+  }
+
+  SelfType& operator++() {
+    increment();
+    return *this;
+  }
+
+  reference operator*() const { return deref(); }
+
+  pointer operator->() const { return &deref(); }
+};
+
+/** \brief Iterator for object and array value.
+ */
+class JSON_API ValueIterator : public ValueIteratorBase {
+  friend class Value;
+
+public:
+  typedef Value value_type;
+  typedef unsigned int size_t;
+  typedef int difference_type;
+  typedef Value& reference;
+  typedef Value* pointer;
+  typedef ValueIterator SelfType;
+
+  ValueIterator();
+  explicit ValueIterator(const ValueConstIterator& other);
+  ValueIterator(const ValueIterator& other);
+
+private:
+  /*! \internal Use by Value to create an iterator.
+   */
+  explicit ValueIterator(const Value::ObjectValues::iterator& current);
+
+public:
+  SelfType& operator=(const SelfType& other);
+
+  SelfType operator++(int) {
+    SelfType temp(*this);
+    ++*this;
+    return temp;
+  }
+
+  SelfType operator--(int) {
+    SelfType temp(*this);
+    --*this;
+    return temp;
+  }
+
+  SelfType& operator--() {
+    decrement();
+    return *this;
+  }
+
+  SelfType& operator++() {
+    increment();
+    return *this;
+  }
+
+  reference operator*() const { return deref(); }
+
+  pointer operator->() const { return &deref(); }
+};
+
+inline void swap(Value& a, Value& b) { a.swap(b); }
+
+} // namespace Json
+
+#pragma pack(pop)
+
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#endif // CPPTL_JSON_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/value.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/reader.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_READER_H_INCLUDED
+#define CPPTL_JSON_READER_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "features.h"
+#include "value.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <deque>
+#include <iosfwd>
+#include <istream>
+#include <stack>
+#include <string>
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(push)
+#pragma warning(disable : 4251)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#pragma pack(push, 8)
+
+namespace Json {
+
+/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
+ *Value.
+ *
+ * \deprecated Use CharReader and CharReaderBuilder.
+ */
+class JSON_API Reader {
+public:
+  typedef char Char;
+  typedef const Char* Location;
+
+  /** \brief An error tagged with where in the JSON text it was encountered.
+   *
+   * The offsets give the [start, limit) range of bytes within the text. Note
+   * that this is bytes, not codepoints.
+   *
+   */
+  struct StructuredError {
+    ptrdiff_t offset_start;
+    ptrdiff_t offset_limit;
+    String message;
+  };
+
+  /** \brief Constructs a Reader allowing all features
+   * for parsing.
+   */
+  JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
+  Reader();
+
+  /** \brief Constructs a Reader allowing the specified feature set
+   * for parsing.
+   */
+  JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
+  Reader(const Features& features);
+
+  /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+   * document.
+   * \param document UTF-8 encoded string containing the document to read.
+   * \param root [out] Contains the root value of the document if it was
+   *             successfully parsed.
+   * \param collectComments \c true to collect comment and allow writing them
+   * back during
+   *                        serialization, \c false to discard comments.
+   *                        This parameter is ignored if
+   * Features::allowComments_
+   *                        is \c false.
+   * \return \c true if the document was successfully parsed, \c false if an
+   * error occurred.
+   */
+  bool
+  parse(const std::string& document, Value& root, bool collectComments = true);
+
+  /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+   document.
+   * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
+   document to read.
+   * \param endDoc Pointer on the end of the UTF-8 encoded string of the
+   document to read.
+   *               Must be >= beginDoc.
+   * \param root [out] Contains the root value of the document if it was
+   *             successfully parsed.
+   * \param collectComments \c true to collect comment and allow writing them
+   back during
+   *                        serialization, \c false to discard comments.
+   *                        This parameter is ignored if
+   Features::allowComments_
+   *                        is \c false.
+   * \return \c true if the document was successfully parsed, \c false if an
+   error occurred.
+   */
+  bool parse(const char* beginDoc,
+             const char* endDoc,
+             Value& root,
+             bool collectComments = true);
+
+  /// \brief Parse from input stream.
+  /// \see Json::operator>>(std::istream&, Json::Value&).
+  bool parse(IStream& is, Value& root, bool collectComments = true);
+
+  /** \brief Returns a user friendly string that list errors in the parsed
+   * document.
+   * \return Formatted error message with the list of errors with their location
+   * in
+   *         the parsed document. An empty string is returned if no error
+   * occurred
+   *         during parsing.
+   * \deprecated Use getFormattedErrorMessages() instead (typo fix).
+   */
+  JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.")
+  String getFormatedErrorMessages() const;
+
+  /** \brief Returns a user friendly string that list errors in the parsed
+   * document.
+   * \return Formatted error message with the list of errors with their location
+   * in
+   *         the parsed document. An empty string is returned if no error
+   * occurred
+   *         during parsing.
+   */
+  String getFormattedErrorMessages() const;
+
+  /** \brief Returns a vector of structured erros encounted while parsing.
+   * \return A (possibly empty) vector of StructuredError objects. Currently
+   *         only one error can be returned, but the caller should tolerate
+   * multiple
+   *         errors.  This can occur if the parser recovers from a non-fatal
+   *         parse error and then encounters additional errors.
+   */
+  std::vector<StructuredError> getStructuredErrors() const;
+
+  /** \brief Add a semantic error message.
+   * \param value JSON Value location associated with the error
+   * \param message The error message.
+   * \return \c true if the error was successfully added, \c false if the
+   * Value offset exceeds the document size.
+   */
+  bool pushError(const Value& value, const String& message);
+
+  /** \brief Add a semantic error message with extra context.
+   * \param value JSON Value location associated with the error
+   * \param message The error message.
+   * \param extra Additional JSON Value location to contextualize the error
+   * \return \c true if the error was successfully added, \c false if either
+   * Value offset exceeds the document size.
+   */
+  bool pushError(const Value& value, const String& message, const Value& extra);
+
+  /** \brief Return whether there are any errors.
+   * \return \c true if there are no errors to report \c false if
+   * errors have occurred.
+   */
+  bool good() const;
+
+private:
+  enum TokenType {
+    tokenEndOfStream = 0,
+    tokenObjectBegin,
+    tokenObjectEnd,
+    tokenArrayBegin,
+    tokenArrayEnd,
+    tokenString,
+    tokenNumber,
+    tokenTrue,
+    tokenFalse,
+    tokenNull,
+    tokenArraySeparator,
+    tokenMemberSeparator,
+    tokenComment,
+    tokenError
+  };
+
+  class Token {
+  public:
+    TokenType type_;
+    Location start_;
+    Location end_;
+  };
+
+  class ErrorInfo {
+  public:
+    Token token_;
+    String message_;
+    Location extra_;
+  };
+
+  typedef std::deque<ErrorInfo> Errors;
+
+  bool readToken(Token& token);
+  void skipSpaces();
+  bool match(Location pattern, int patternLength);
+  bool readComment();
+  bool readCStyleComment();
+  bool readCppStyleComment();
+  bool readString();
+  void readNumber();
+  bool readValue();
+  bool readObject(Token& token);
+  bool readArray(Token& token);
+  bool decodeNumber(Token& token);
+  bool decodeNumber(Token& token, Value& decoded);
+  bool decodeString(Token& token);
+  bool decodeString(Token& token, String& decoded);
+  bool decodeDouble(Token& token);
+  bool decodeDouble(Token& token, Value& decoded);
+  bool decodeUnicodeCodePoint(Token& token,
+                              Location& current,
+                              Location end,
+                              unsigned int& unicode);
+  bool decodeUnicodeEscapeSequence(Token& token,
+                                   Location& current,
+                                   Location end,
+                                   unsigned int& unicode);
+  bool addError(const String& message, Token& token, Location extra = nullptr);
+  bool recoverFromError(TokenType skipUntilToken);
+  bool addErrorAndRecover(const String& message,
+                          Token& token,
+                          TokenType skipUntilToken);
+  void skipUntilSpace();
+  Value& currentValue();
+  Char getNextChar();
+  void
+  getLocationLineAndColumn(Location location, int& line, int& column) const;
+  String getLocationLineAndColumn(Location location) const;
+  void addComment(Location begin, Location end, CommentPlacement placement);
+  void skipCommentTokens(Token& token);
+
+  static bool containsNewLine(Location begin, Location end);
+  static String normalizeEOL(Location begin, Location end);
+
+  typedef std::stack<Value*> Nodes;
+  Nodes nodes_;
+  Errors errors_;
+  String document_;
+  Location begin_{};
+  Location end_{};
+  Location current_{};
+  Location lastValueEnd_{};
+  Value* lastValue_{};
+  String commentsBefore_;
+  Features features_;
+  bool collectComments_{};
+}; // Reader
+
+/** Interface for reading JSON from a char array.
+ */
+class JSON_API CharReader {
+public:
+  virtual ~CharReader() = default;
+  /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
+   document.
+   * The document must be a UTF-8 encoded string containing the document to
+   read.
+   *
+   * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
+   document to read.
+   * \param endDoc Pointer on the end of the UTF-8 encoded string of the
+   document to read.
+   *        Must be >= beginDoc.
+   * \param root [out] Contains the root value of the document if it was
+   *             successfully parsed.
+   * \param errs [out] Formatted error messages (if not NULL)
+   *        a user friendly string that lists errors in the parsed
+   * document.
+   * \return \c true if the document was successfully parsed, \c false if an
+   error occurred.
+   */
+  virtual bool parse(char const* beginDoc,
+                     char const* endDoc,
+                     Value* root,
+                     String* errs) = 0;
+
+  class JSON_API Factory {
+  public:
+    virtual ~Factory() = default;
+    /** \brief Allocate a CharReader via operator new().
+     * \throw std::exception if something goes wrong (e.g. invalid settings)
+     */
+    virtual CharReader* newCharReader() const = 0;
+  }; // Factory
+};   // CharReader
+
+/** \brief Build a CharReader implementation.
+
+Usage:
+\code
+  using namespace Json;
+  CharReaderBuilder builder;
+  builder["collectComments"] = false;
+  Value value;
+  String errs;
+  bool ok = parseFromStream(builder, std::cin, &value, &errs);
+\endcode
+*/
+class JSON_API CharReaderBuilder : public CharReader::Factory {
+public:
+  // Note: We use a Json::Value so that we can add data-members to this class
+  // without a major version bump.
+  /** Configuration of this builder.
+    These are case-sensitive.
+    Available settings (case-sensitive):
+    - `"collectComments": false or true`
+      - true to collect comment and allow writing them
+        back during serialization, false to discard comments.
+        This parameter is ignored if allowComments is false.
+    - `"allowComments": false or true`
+      - true if comments are allowed.
+    - `"strictRoot": false or true`
+      - true if root must be either an array or an object value
+    - `"allowDroppedNullPlaceholders": false or true`
+      - true if dropped null placeholders are allowed. (See
+    StreamWriterBuilder.)
+    - `"allowNumericKeys": false or true`
+      - true if numeric object keys are allowed.
+    - `"allowSingleQuotes": false or true`
+      - true if '' are allowed for strings (both keys and values)
+    - `"stackLimit": integer`
+      - Exceeding stackLimit (recursive depth of `readValue()`) will
+        cause an exception.
+      - This is a security issue (seg-faults caused by deeply nested JSON),
+        so the default is low.
+    - `"failIfExtra": false or true`
+      - If true, `parse()` returns false when extra non-whitespace trails
+        the JSON value in the input string.
+    - `"rejectDupKeys": false or true`
+      - If true, `parse()` returns false when a key is duplicated within an
+    object.
+    - `"allowSpecialFloats": false or true`
+      - If true, special float values (NaNs and infinities) are allowed
+        and their values are lossfree restorable.
+
+    You can examine 'settings_` yourself
+    to see the defaults. You can also write and read them just like any
+    JSON Value.
+    \sa setDefaults()
+    */
+  Json::Value settings_;
+
+  CharReaderBuilder();
+  ~CharReaderBuilder() override;
+
+  CharReader* newCharReader() const override;
+
+  /** \return true if 'settings' are legal and consistent;
+   *   otherwise, indicate bad settings via 'invalid'.
+   */
+  bool validate(Json::Value* invalid) const;
+
+  /** A simple way to update a specific setting.
+   */
+  Value& operator[](const String& key);
+
+  /** Called by ctor, but you can use this to reset settings_.
+   * \pre 'settings' != NULL (but Json::null is fine)
+   * \remark Defaults:
+   * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults
+   */
+  static void setDefaults(Json::Value* settings);
+  /** Same as old Features::strictMode().
+   * \pre 'settings' != NULL (but Json::null is fine)
+   * \remark Defaults:
+   * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode
+   */
+  static void strictMode(Json::Value* settings);
+};
+
+/** Consume entire stream and use its begin/end.
+ * Someday we might have a real StreamReader, but for now this
+ * is convenient.
+ */
+bool JSON_API parseFromStream(CharReader::Factory const&,
+                              IStream&,
+                              Value* root,
+                              std::string* errs);
+
+/** \brief Read from 'sin' into 'root'.
+
+ Always keep comments from the input JSON.
+
+ This can be used to read a file into a particular sub-object.
+ For example:
+ \code
+ Json::Value root;
+ cin >> root["dir"]["file"];
+ cout << root;
+ \endcode
+ Result:
+ \verbatim
+ {
+ "dir": {
+     "file": {
+     // The input stream JSON would be nested here.
+     }
+ }
+ }
+ \endverbatim
+ \throw std::exception on parse error.
+ \see Json::operator<<()
+*/
+JSON_API IStream& operator>>(IStream&, Value&);
+
+} // namespace Json
+
+#pragma pack(pop)
+
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#endif // CPPTL_JSON_READER_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/reader.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/writer.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef JSON_WRITER_H_INCLUDED
+#define JSON_WRITER_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "value.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <ostream>
+#include <string>
+#include <vector>
+
+// Disable warning C4251: <data member>: <type> needs to have dll-interface to
+// be used by...
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4251)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#pragma pack(push, 8)
+
+namespace Json {
+
+class Value;
+
+/**
+
+Usage:
+\code
+  using namespace Json;
+  void writeToStdout(StreamWriter::Factory const& factory, Value const& value) {
+    std::unique_ptr<StreamWriter> const writer(
+      factory.newStreamWriter());
+    writer->write(value, &std::cout);
+    std::cout << std::endl;  // add lf and flush
+  }
+\endcode
+*/
+class JSON_API StreamWriter {
+protected:
+  OStream* sout_; // not owned; will not delete
+public:
+  StreamWriter();
+  virtual ~StreamWriter();
+  /** Write Value into document as configured in sub-class.
+      Do not take ownership of sout, but maintain a reference during function.
+      \pre sout != NULL
+      \return zero on success (For now, we always return zero, so check the
+     stream instead.) \throw std::exception possibly, depending on configuration
+   */
+  virtual int write(Value const& root, OStream* sout) = 0;
+
+  /** \brief A simple abstract factory.
+   */
+  class JSON_API Factory {
+  public:
+    virtual ~Factory();
+    /** \brief Allocate a CharReader via operator new().
+     * \throw std::exception if something goes wrong (e.g. invalid settings)
+     */
+    virtual StreamWriter* newStreamWriter() const = 0;
+  }; // Factory
+};   // StreamWriter
+
+/** \brief Write into stringstream, then return string, for convenience.
+ * A StreamWriter will be created from the factory, used, and then deleted.
+ */
+String JSON_API writeString(StreamWriter::Factory const& factory,
+                            Value const& root);
+
+/** \brief Build a StreamWriter implementation.
+
+Usage:
+\code
+  using namespace Json;
+  Value value = ...;
+  StreamWriterBuilder builder;
+  builder["commentStyle"] = "None";
+  builder["indentation"] = "   ";  // or whatever you like
+  std::unique_ptr<Json::StreamWriter> writer(
+      builder.newStreamWriter());
+  writer->write(value, &std::cout);
+  std::cout << std::endl;  // add lf and flush
+\endcode
+*/
+class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
+public:
+  // Note: We use a Json::Value so that we can add data-members to this class
+  // without a major version bump.
+  /** Configuration of this builder.
+    Available settings (case-sensitive):
+    - "commentStyle": "None" or "All"
+    - "indentation":  "<anything>".
+      - Setting this to an empty string also omits newline characters.
+    - "enableYAMLCompatibility": false or true
+      - slightly change the whitespace around colons
+    - "dropNullPlaceholders": false or true
+      - Drop the "null" string from the writer's output for nullValues.
+        Strictly speaking, this is not valid JSON. But when the output is being
+        fed to a browser's JavaScript, it makes for smaller output and the
+        browser can handle the output just fine.
+    - "useSpecialFloats": false or true
+      - If true, outputs non-finite floating point values in the following way:
+        NaN values as "NaN", positive infinity as "Infinity", and negative
+    infinity as "-Infinity".
+    - "precision": int
+      - Number of precision digits for formatting of real values.
+    - "precisionType": "significant"(default) or "decimal"
+      - Type of precision for formatting of real values.
+
+    You can examine 'settings_` yourself
+    to see the defaults. You can also write and read them just like any
+    JSON Value.
+    \sa setDefaults()
+    */
+  Json::Value settings_;
+
+  StreamWriterBuilder();
+  ~StreamWriterBuilder() override;
+
+  /**
+   * \throw std::exception if something goes wrong (e.g. invalid settings)
+   */
+  StreamWriter* newStreamWriter() const override;
+
+  /** \return true if 'settings' are legal and consistent;
+   *   otherwise, indicate bad settings via 'invalid'.
+   */
+  bool validate(Json::Value* invalid) const;
+  /** A simple way to update a specific setting.
+   */
+  Value& operator[](const String& key);
+
+  /** Called by ctor, but you can use this to reset settings_.
+   * \pre 'settings' != NULL (but Json::null is fine)
+   * \remark Defaults:
+   * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults
+   */
+  static void setDefaults(Json::Value* settings);
+};
+
+/** \brief Abstract class for writers.
+ * \deprecated Use StreamWriter. (And really, this is an implementation detail.)
+ */
+class JSONCPP_DEPRECATED("Use StreamWriter instead") JSON_API Writer {
+public:
+  virtual ~Writer();
+
+  virtual String write(const Value& root) = 0;
+};
+
+/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format
+ *without formatting (not human friendly).
+ *
+ * The JSON document is written in a single line. It is not intended for 'human'
+ *consumption,
+ * but may be useful to support feature such as RPC where bandwidth is limited.
+ * \sa Reader, Value
+ * \deprecated Use StreamWriterBuilder.
+ */
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4996) // Deriving from deprecated class
+#endif
+class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter
+    : public Writer {
+public:
+  FastWriter();
+  ~FastWriter() override = default;
+
+  void enableYAMLCompatibility();
+
+  /** \brief Drop the "null" string from the writer's output for nullValues.
+   * Strictly speaking, this is not valid JSON. But when the output is being
+   * fed to a browser's JavaScript, it makes for smaller output and the
+   * browser can handle the output just fine.
+   */
+  void dropNullPlaceholders();
+
+  void omitEndingLineFeed();
+
+public: // overridden from Writer
+  String write(const Value& root) override;
+
+private:
+  void writeValue(const Value& value);
+
+  String document_;
+  bool yamlCompatibilityEnabled_{false};
+  bool dropNullPlaceholders_{false};
+  bool omitEndingLineFeed_{false};
+};
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
+ *human friendly way.
+ *
+ * The rules for line break and indent are as follow:
+ * - Object value:
+ *     - if empty then print {} without indent and line break
+ *     - if not empty the print '{', line break & indent, print one value per
+ *line
+ *       and then unindent and line break and print '}'.
+ * - Array value:
+ *     - if empty then print [] without indent and line break
+ *     - if the array contains no object value, empty array or some other value
+ *types,
+ *       and all the values fit on one lines, then print the array on a single
+ *line.
+ *     - otherwise, it the values do not fit on one line, or the array contains
+ *       object or non empty array, then print one value per line.
+ *
+ * If the Value have comments then they are outputed according to their
+ *#CommentPlacement.
+ *
+ * \sa Reader, Value, Value::setComment()
+ * \deprecated Use StreamWriterBuilder.
+ */
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4996) // Deriving from deprecated class
+#endif
+class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API
+    StyledWriter : public Writer {
+public:
+  StyledWriter();
+  ~StyledWriter() override = default;
+
+public: // overridden from Writer
+  /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
+   * \param root Value to serialize.
+   * \return String containing the JSON document that represents the root value.
+   */
+  String write(const Value& root) override;
+
+private:
+  void writeValue(const Value& value);
+  void writeArrayValue(const Value& value);
+  bool isMultilineArray(const Value& value);
+  void pushValue(const String& value);
+  void writeIndent();
+  void writeWithIndent(const String& value);
+  void indent();
+  void unindent();
+  void writeCommentBeforeValue(const Value& root);
+  void writeCommentAfterValueOnSameLine(const Value& root);
+  static bool hasCommentForValue(const Value& value);
+  static String normalizeEOL(const String& text);
+
+  typedef std::vector<String> ChildValues;
+
+  ChildValues childValues_;
+  String document_;
+  String indentString_;
+  unsigned int rightMargin_{74};
+  unsigned int indentSize_{3};
+  bool addChildValues_{false};
+};
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
+ human friendly way,
+     to a stream rather than to a string.
+ *
+ * The rules for line break and indent are as follow:
+ * - Object value:
+ *     - if empty then print {} without indent and line break
+ *     - if not empty the print '{', line break & indent, print one value per
+ line
+ *       and then unindent and line break and print '}'.
+ * - Array value:
+ *     - if empty then print [] without indent and line break
+ *     - if the array contains no object value, empty array or some other value
+ types,
+ *       and all the values fit on one lines, then print the array on a single
+ line.
+ *     - otherwise, it the values do not fit on one line, or the array contains
+ *       object or non empty array, then print one value per line.
+ *
+ * If the Value have comments then they are outputed according to their
+ #CommentPlacement.
+ *
+ * \sa Reader, Value, Value::setComment()
+ * \deprecated Use StreamWriterBuilder.
+ */
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4996) // Deriving from deprecated class
+#endif
+class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API
+    StyledStreamWriter {
+public:
+  /**
+   * \param indentation Each level will be indented by this amount extra.
+   */
+  StyledStreamWriter(String indentation = "\t");
+  ~StyledStreamWriter() = default;
+
+public:
+  /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
+   * \param out Stream to write to. (Can be ostringstream, e.g.)
+   * \param root Value to serialize.
+   * \note There is no point in deriving from Writer, since write() should not
+   * return a value.
+   */
+  void write(OStream& out, const Value& root);
+
+private:
+  void writeValue(const Value& value);
+  void writeArrayValue(const Value& value);
+  bool isMultilineArray(const Value& value);
+  void pushValue(const String& value);
+  void writeIndent();
+  void writeWithIndent(const String& value);
+  void indent();
+  void unindent();
+  void writeCommentBeforeValue(const Value& root);
+  void writeCommentAfterValueOnSameLine(const Value& root);
+  static bool hasCommentForValue(const Value& value);
+  static String normalizeEOL(const String& text);
+
+  typedef std::vector<String> ChildValues;
+
+  ChildValues childValues_;
+  OStream* document_;
+  String indentString_;
+  unsigned int rightMargin_{74};
+  String indentation_;
+  bool addChildValues_ : 1;
+  bool indented_ : 1;
+};
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#if defined(JSON_HAS_INT64)
+String JSON_API valueToString(Int value);
+String JSON_API valueToString(UInt value);
+#endif // if defined(JSON_HAS_INT64)
+String JSON_API valueToString(LargestInt value);
+String JSON_API valueToString(LargestUInt value);
+String JSON_API
+valueToString(double value,
+              unsigned int precision = Value::defaultRealPrecision,
+              PrecisionType precisionType = PrecisionType::significantDigits);
+String JSON_API valueToString(bool value);
+String JSON_API valueToQuotedString(const char* value);
+
+/// \brief Output using the StyledStreamWriter.
+/// \see Json::operator>>()
+JSON_API OStream& operator<<(OStream&, const Value& root);
+
+} // namespace Json
+
+#pragma pack(pop)
+
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#pragma warning(pop)
+#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+
+#endif // JSON_WRITER_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/writer.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: include/json/assertions.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED
+#define CPPTL_JSON_ASSERTIONS_H_INCLUDED
+
+#include <cstdlib>
+#include <sstream>
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "config.h"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+/** It should not be possible for a maliciously designed file to
+ *  cause an abort() or seg-fault, so these macros are used only
+ *  for pre-condition violations and internal logic errors.
+ */
+#if JSON_USE_EXCEPTION
+
+// @todo <= add detail about condition in exception
+#define JSON_ASSERT(condition)                                                 \
+  {                                                                            \
+    if (!(condition)) {                                                        \
+      Json::throwLogicError("assert json failed");                             \
+    }                                                                          \
+  }
+
+#define JSON_FAIL_MESSAGE(message)                                             \
+  {                                                                            \
+    OStringStream oss;                                                         \
+    oss << message;                                                            \
+    Json::throwLogicError(oss.str());                                          \
+    abort();                                                                   \
+  }
+
+#else // JSON_USE_EXCEPTION
+
+#define JSON_ASSERT(condition) assert(condition)
+
+// The call to assert() will show the failure message in debug builds. In
+// release builds we abort, for a core-dump or debugger.
+#define JSON_FAIL_MESSAGE(message)                                             \
+  {                                                                            \
+    OStringStream oss;                                                         \
+    oss << message;                                                            \
+    assert(false && oss.str().c_str());                                        \
+    abort();                                                                   \
+  }
+
+#endif
+
+#define JSON_ASSERT_MESSAGE(condition, message)                                \
+  if (!(condition)) {                                                          \
+    JSON_FAIL_MESSAGE(message);                                                \
+  }
+
+#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: include/json/assertions.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#endif //ifndef JSON_AMALGAMATED_H_INCLUDED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/jsoncpp.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,5418 @@
+/// Json-cpp amalgamated source (http://jsoncpp.sourceforge.net/).
+/// It is intended to be used with #include "json/json.h"
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+/*
+The JsonCpp library's source code, including accompanying documentation, 
+tests and demonstration applications, are licensed under the following
+conditions...
+
+Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all 
+jurisdictions which recognize such a disclaimer. In such jurisdictions, 
+this software is released into the Public Domain.
+
+In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
+2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
+The JsonCpp Authors, and is released under the terms of the MIT License (see below).
+
+In jurisdictions which recognize Public Domain property, the user of this 
+software may choose to accept it either as 1) Public Domain, 2) under the 
+conditions of the MIT License (see below), or 3) under the terms of dual 
+Public Domain/MIT License conditions described here, as they choose.
+
+The MIT License is about as close to Public Domain as a license can get, and is
+described in clear, concise terms at:
+
+   http://en.wikipedia.org/wiki/MIT_License
+   
+The full text of the MIT License follows:
+
+========================================================================
+Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+========================================================================
+(END LICENSE TEXT)
+
+The MIT license is compatible with both the GPL and commercial
+software, affording one all of the rights of Public Domain with the
+minor nuisance of being required to keep the above copyright notice
+and license text in the source code. Note also that by accepting the
+Public Domain "license" you can re-license your copy using whatever
+license you like.
+
+*/
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: LICENSE
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+#include "json/json.h"
+
+#ifndef JSON_IS_AMALGAMATION
+#error "Compile with -I PATH_TO_JSON_DIRECTORY"
+#endif
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_tool.h
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include <json/config.h>
+#endif
+
+// Also support old flag NO_LOCALE_SUPPORT
+#ifdef NO_LOCALE_SUPPORT
+#define JSONCPP_NO_LOCALE_SUPPORT
+#endif
+
+#ifndef JSONCPP_NO_LOCALE_SUPPORT
+#include <clocale>
+#endif
+
+/* This header provides common string manipulation support, such as UTF-8,
+ * portable conversion from/to string...
+ *
+ * It is an internal header that must not be exposed.
+ */
+
+namespace Json {
+static inline char getDecimalPoint() {
+#ifdef JSONCPP_NO_LOCALE_SUPPORT
+  return '\0';
+#else
+  struct lconv* lc = localeconv();
+  return lc ? *(lc->decimal_point) : '\0';
+#endif
+}
+
+/// Converts a unicode code-point to UTF-8.
+static inline String codePointToUTF8(unsigned int cp) {
+  String result;
+
+  // based on description from http://en.wikipedia.org/wiki/UTF-8
+
+  if (cp <= 0x7f) {
+    result.resize(1);
+    result[0] = static_cast<char>(cp);
+  } else if (cp <= 0x7FF) {
+    result.resize(2);
+    result[1] = static_cast<char>(0x80 | (0x3f & cp));
+    result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
+  } else if (cp <= 0xFFFF) {
+    result.resize(3);
+    result[2] = static_cast<char>(0x80 | (0x3f & cp));
+    result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
+    result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));
+  } else if (cp <= 0x10FFFF) {
+    result.resize(4);
+    result[3] = static_cast<char>(0x80 | (0x3f & cp));
+    result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
+    result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
+    result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
+  }
+
+  return result;
+}
+
+enum {
+  /// Constant that specify the size of the buffer that must be passed to
+  /// uintToString.
+  uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1
+};
+
+// Defines a char buffer for use with uintToString().
+typedef char UIntToStringBuffer[uintToStringBufferSize];
+
+/** Converts an unsigned integer to string.
+ * @param value Unsigned integer to convert to string
+ * @param current Input/Output string buffer.
+ *        Must have at least uintToStringBufferSize chars free.
+ */
+static inline void uintToString(LargestUInt value, char*& current) {
+  *--current = 0;
+  do {
+    *--current = static_cast<char>(value % 10U + static_cast<unsigned>('0'));
+    value /= 10;
+  } while (value != 0);
+}
+
+/** Change ',' to '.' everywhere in buffer.
+ *
+ * We had a sophisticated way, but it did not work in WinCE.
+ * @see https://github.com/open-source-parsers/jsoncpp/pull/9
+ */
+template <typename Iter> Iter fixNumericLocale(Iter begin, Iter end) {
+  for (; begin != end; ++begin) {
+    if (*begin == ',') {
+      *begin = '.';
+    }
+  }
+  return begin;
+}
+
+template <typename Iter> void fixNumericLocaleInput(Iter begin, Iter end) {
+  char decimalPoint = getDecimalPoint();
+  if (decimalPoint == '\0' || decimalPoint == '.') {
+    return;
+  }
+  for (; begin != end; ++begin) {
+    if (*begin == '.') {
+      *begin = decimalPoint;
+    }
+  }
+}
+
+/**
+ * Return iterator that would be the new end of the range [begin,end), if we
+ * were to delete zeros in the end of string, but not the last zero before '.'.
+ */
+template <typename Iter> Iter fixZerosInTheEnd(Iter begin, Iter end) {
+  for (; begin != end; --end) {
+    if (*(end - 1) != '0') {
+      return end;
+    }
+    // Don't delete the last zero before the decimal point.
+    if (begin != (end - 1) && *(end - 2) == '.') {
+      return end;
+    }
+  }
+  return end;
+}
+
+} // namespace Json
+
+#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_tool.h
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_reader.cpp
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors
+// Copyright (C) 2016 InfoTeCS JSC. All rights reserved.
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "json_tool.h"
+#include <json/assertions.h>
+#include <json/reader.h>
+#include <json/value.h>
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <cassert>
+#include <cstring>
+#include <istream>
+#include <limits>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <utility>
+
+#include <cstdio>
+#if __cplusplus >= 201103L
+
+#if !defined(sscanf)
+#define sscanf std::sscanf
+#endif
+
+#endif //__cplusplus
+
+#if defined(_MSC_VER)
+#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
+#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
+#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
+#endif //_MSC_VER
+
+#if defined(_MSC_VER)
+// Disable warning about strdup being deprecated.
+#pragma warning(disable : 4996)
+#endif
+
+// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile
+// time to change the stack limit
+#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
+#define JSONCPP_DEPRECATED_STACK_LIMIT 1000
+#endif
+
+static size_t const stackLimit_g =
+    JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue()
+
+namespace Json {
+
+#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
+typedef std::unique_ptr<CharReader> CharReaderPtr;
+#else
+typedef std::unique_ptr<CharReader> CharReaderPtr;
+#endif
+
+// Implementation of class Features
+// ////////////////////////////////
+
+Features::Features() = default;
+
+Features Features::all() { return {}; }
+
+Features Features::strictMode() {
+  Features features;
+  features.allowComments_ = false;
+  features.strictRoot_ = true;
+  features.allowDroppedNullPlaceholders_ = false;
+  features.allowNumericKeys_ = false;
+  return features;
+}
+
+// Implementation of class Reader
+// ////////////////////////////////
+
+bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
+  for (; begin < end; ++begin)
+    if (*begin == '\n' || *begin == '\r')
+      return true;
+  return false;
+}
+
+// Class Reader
+// //////////////////////////////////////////////////////////////////
+
+Reader::Reader()
+    : errors_(), document_(), commentsBefore_(), features_(Features::all()) {}
+
+Reader::Reader(const Features& features)
+    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
+      lastValue_(), commentsBefore_(), features_(features), collectComments_() {
+}
+
+bool Reader::parse(const std::string& document,
+                   Value& root,
+                   bool collectComments) {
+  document_.assign(document.begin(), document.end());
+  const char* begin = document_.c_str();
+  const char* end = begin + document_.length();
+  return parse(begin, end, root, collectComments);
+}
+
+bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
+  // std::istream_iterator<char> begin(is);
+  // std::istream_iterator<char> end;
+  // Those would allow streamed input from a file, if parse() were a
+  // template function.
+
+  // Since String is reference-counted, this at least does not
+  // create an extra copy.
+  String doc;
+  std::getline(is, doc, (char)EOF);
+  return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
+}
+
+bool Reader::parse(const char* beginDoc,
+                   const char* endDoc,
+                   Value& root,
+                   bool collectComments) {
+  if (!features_.allowComments_) {
+    collectComments = false;
+  }
+
+  begin_ = beginDoc;
+  end_ = endDoc;
+  collectComments_ = collectComments;
+  current_ = begin_;
+  lastValueEnd_ = nullptr;
+  lastValue_ = nullptr;
+  commentsBefore_.clear();
+  errors_.clear();
+  while (!nodes_.empty())
+    nodes_.pop();
+  nodes_.push(&root);
+
+  bool successful = readValue();
+  Token token;
+  skipCommentTokens(token);
+  if (collectComments_ && !commentsBefore_.empty())
+    root.setComment(commentsBefore_, commentAfter);
+  if (features_.strictRoot_) {
+    if (!root.isArray() && !root.isObject()) {
+      // Set error location to start of doc, ideally should be first token found
+      // in doc
+      token.type_ = tokenError;
+      token.start_ = beginDoc;
+      token.end_ = endDoc;
+      addError(
+          "A valid JSON document must be either an array or an object value.",
+          token);
+      return false;
+    }
+  }
+  return successful;
+}
+
+bool Reader::readValue() {
+  // readValue() may call itself only if it calls readObject() or ReadArray().
+  // These methods execute nodes_.push() just before and nodes_.pop)() just
+  // after calling readValue(). parse() executes one nodes_.push(), so > instead
+  // of >=.
+  if (nodes_.size() > stackLimit_g)
+    throwRuntimeError("Exceeded stackLimit in readValue().");
+
+  Token token;
+  skipCommentTokens(token);
+  bool successful = true;
+
+  if (collectComments_ && !commentsBefore_.empty()) {
+    currentValue().setComment(commentsBefore_, commentBefore);
+    commentsBefore_.clear();
+  }
+
+  switch (token.type_) {
+  case tokenObjectBegin:
+    successful = readObject(token);
+    currentValue().setOffsetLimit(current_ - begin_);
+    break;
+  case tokenArrayBegin:
+    successful = readArray(token);
+    currentValue().setOffsetLimit(current_ - begin_);
+    break;
+  case tokenNumber:
+    successful = decodeNumber(token);
+    break;
+  case tokenString:
+    successful = decodeString(token);
+    break;
+  case tokenTrue: {
+    Value v(true);
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenFalse: {
+    Value v(false);
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenNull: {
+    Value v;
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenArraySeparator:
+  case tokenObjectEnd:
+  case tokenArrayEnd:
+    if (features_.allowDroppedNullPlaceholders_) {
+      // "Un-read" the current token and mark the current value as a null
+      // token.
+      current_--;
+      Value v;
+      currentValue().swapPayload(v);
+      currentValue().setOffsetStart(current_ - begin_ - 1);
+      currentValue().setOffsetLimit(current_ - begin_);
+      break;
+    } // Else, fall through...
+  default:
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+    return addError("Syntax error: value, object or array expected.", token);
+  }
+
+  if (collectComments_) {
+    lastValueEnd_ = current_;
+    lastValue_ = &currentValue();
+  }
+
+  return successful;
+}
+
+void Reader::skipCommentTokens(Token& token) {
+  if (features_.allowComments_) {
+    do {
+      readToken(token);
+    } while (token.type_ == tokenComment);
+  } else {
+    readToken(token);
+  }
+}
+
+bool Reader::readToken(Token& token) {
+  skipSpaces();
+  token.start_ = current_;
+  Char c = getNextChar();
+  bool ok = true;
+  switch (c) {
+  case '{':
+    token.type_ = tokenObjectBegin;
+    break;
+  case '}':
+    token.type_ = tokenObjectEnd;
+    break;
+  case '[':
+    token.type_ = tokenArrayBegin;
+    break;
+  case ']':
+    token.type_ = tokenArrayEnd;
+    break;
+  case '"':
+    token.type_ = tokenString;
+    ok = readString();
+    break;
+  case '/':
+    token.type_ = tokenComment;
+    ok = readComment();
+    break;
+  case '0':
+  case '1':
+  case '2':
+  case '3':
+  case '4':
+  case '5':
+  case '6':
+  case '7':
+  case '8':
+  case '9':
+  case '-':
+    token.type_ = tokenNumber;
+    readNumber();
+    break;
+  case 't':
+    token.type_ = tokenTrue;
+    ok = match("rue", 3);
+    break;
+  case 'f':
+    token.type_ = tokenFalse;
+    ok = match("alse", 4);
+    break;
+  case 'n':
+    token.type_ = tokenNull;
+    ok = match("ull", 3);
+    break;
+  case ',':
+    token.type_ = tokenArraySeparator;
+    break;
+  case ':':
+    token.type_ = tokenMemberSeparator;
+    break;
+  case 0:
+    token.type_ = tokenEndOfStream;
+    break;
+  default:
+    ok = false;
+    break;
+  }
+  if (!ok)
+    token.type_ = tokenError;
+  token.end_ = current_;
+  return true;
+}
+
+void Reader::skipSpaces() {
+  while (current_ != end_) {
+    Char c = *current_;
+    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
+      ++current_;
+    else
+      break;
+  }
+}
+
+bool Reader::match(Location pattern, int patternLength) {
+  if (end_ - current_ < patternLength)
+    return false;
+  int index = patternLength;
+  while (index--)
+    if (current_[index] != pattern[index])
+      return false;
+  current_ += patternLength;
+  return true;
+}
+
+bool Reader::readComment() {
+  Location commentBegin = current_ - 1;
+  Char c = getNextChar();
+  bool successful = false;
+  if (c == '*')
+    successful = readCStyleComment();
+  else if (c == '/')
+    successful = readCppStyleComment();
+  if (!successful)
+    return false;
+
+  if (collectComments_) {
+    CommentPlacement placement = commentBefore;
+    if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
+      if (c != '*' || !containsNewLine(commentBegin, current_))
+        placement = commentAfterOnSameLine;
+    }
+
+    addComment(commentBegin, current_, placement);
+  }
+  return true;
+}
+
+String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) {
+  String normalized;
+  normalized.reserve(static_cast<size_t>(end - begin));
+  Reader::Location current = begin;
+  while (current != end) {
+    char c = *current++;
+    if (c == '\r') {
+      if (current != end && *current == '\n')
+        // convert dos EOL
+        ++current;
+      // convert Mac EOL
+      normalized += '\n';
+    } else {
+      normalized += c;
+    }
+  }
+  return normalized;
+}
+
+void Reader::addComment(Location begin,
+                        Location end,
+                        CommentPlacement placement) {
+  assert(collectComments_);
+  const String& normalized = normalizeEOL(begin, end);
+  if (placement == commentAfterOnSameLine) {
+    assert(lastValue_ != nullptr);
+    lastValue_->setComment(normalized, placement);
+  } else {
+    commentsBefore_ += normalized;
+  }
+}
+
+bool Reader::readCStyleComment() {
+  while ((current_ + 1) < end_) {
+    Char c = getNextChar();
+    if (c == '*' && *current_ == '/')
+      break;
+  }
+  return getNextChar() == '/';
+}
+
+bool Reader::readCppStyleComment() {
+  while (current_ != end_) {
+    Char c = getNextChar();
+    if (c == '\n')
+      break;
+    if (c == '\r') {
+      // Consume DOS EOL. It will be normalized in addComment.
+      if (current_ != end_ && *current_ == '\n')
+        getNextChar();
+      // Break on Moc OS 9 EOL.
+      break;
+    }
+  }
+  return true;
+}
+
+void Reader::readNumber() {
+  const char* p = current_;
+  char c = '0'; // stopgap for already consumed character
+  // integral part
+  while (c >= '0' && c <= '9')
+    c = (current_ = p) < end_ ? *p++ : '\0';
+  // fractional part
+  if (c == '.') {
+    c = (current_ = p) < end_ ? *p++ : '\0';
+    while (c >= '0' && c <= '9')
+      c = (current_ = p) < end_ ? *p++ : '\0';
+  }
+  // exponential part
+  if (c == 'e' || c == 'E') {
+    c = (current_ = p) < end_ ? *p++ : '\0';
+    if (c == '+' || c == '-')
+      c = (current_ = p) < end_ ? *p++ : '\0';
+    while (c >= '0' && c <= '9')
+      c = (current_ = p) < end_ ? *p++ : '\0';
+  }
+}
+
+bool Reader::readString() {
+  Char c = '\0';
+  while (current_ != end_) {
+    c = getNextChar();
+    if (c == '\\')
+      getNextChar();
+    else if (c == '"')
+      break;
+  }
+  return c == '"';
+}
+
+bool Reader::readObject(Token& token) {
+  Token tokenName;
+  String name;
+  Value init(objectValue);
+  currentValue().swapPayload(init);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  while (readToken(tokenName)) {
+    bool initialTokenOk = true;
+    while (tokenName.type_ == tokenComment && initialTokenOk)
+      initialTokenOk = readToken(tokenName);
+    if (!initialTokenOk)
+      break;
+    if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
+      return true;
+    name.clear();
+    if (tokenName.type_ == tokenString) {
+      if (!decodeString(tokenName, name))
+        return recoverFromError(tokenObjectEnd);
+    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
+      Value numberName;
+      if (!decodeNumber(tokenName, numberName))
+        return recoverFromError(tokenObjectEnd);
+      name = String(numberName.asCString());
+    } else {
+      break;
+    }
+
+    Token colon;
+    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
+      return addErrorAndRecover("Missing ':' after object member name", colon,
+                                tokenObjectEnd);
+    }
+    Value& value = currentValue()[name];
+    nodes_.push(&value);
+    bool ok = readValue();
+    nodes_.pop();
+    if (!ok) // error already set
+      return recoverFromError(tokenObjectEnd);
+
+    Token comma;
+    if (!readToken(comma) ||
+        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
+         comma.type_ != tokenComment)) {
+      return addErrorAndRecover("Missing ',' or '}' in object declaration",
+                                comma, tokenObjectEnd);
+    }
+    bool finalizeTokenOk = true;
+    while (comma.type_ == tokenComment && finalizeTokenOk)
+      finalizeTokenOk = readToken(comma);
+    if (comma.type_ == tokenObjectEnd)
+      return true;
+  }
+  return addErrorAndRecover("Missing '}' or object member name", tokenName,
+                            tokenObjectEnd);
+}
+
+bool Reader::readArray(Token& token) {
+  Value init(arrayValue);
+  currentValue().swapPayload(init);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  skipSpaces();
+  if (current_ != end_ && *current_ == ']') // empty array
+  {
+    Token endArray;
+    readToken(endArray);
+    return true;
+  }
+  int index = 0;
+  for (;;) {
+    Value& value = currentValue()[index++];
+    nodes_.push(&value);
+    bool ok = readValue();
+    nodes_.pop();
+    if (!ok) // error already set
+      return recoverFromError(tokenArrayEnd);
+
+    Token currentToken;
+    // Accept Comment after last item in the array.
+    ok = readToken(currentToken);
+    while (currentToken.type_ == tokenComment && ok) {
+      ok = readToken(currentToken);
+    }
+    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
+                         currentToken.type_ != tokenArrayEnd);
+    if (!ok || badTokenType) {
+      return addErrorAndRecover("Missing ',' or ']' in array declaration",
+                                currentToken, tokenArrayEnd);
+    }
+    if (currentToken.type_ == tokenArrayEnd)
+      break;
+  }
+  return true;
+}
+
+bool Reader::decodeNumber(Token& token) {
+  Value decoded;
+  if (!decodeNumber(token, decoded))
+    return false;
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool Reader::decodeNumber(Token& token, Value& decoded) {
+  // Attempts to parse the number as an integer. If the number is
+  // larger than the maximum supported value of an integer then
+  // we decode the number as a double.
+  Location current = token.start_;
+  bool isNegative = *current == '-';
+  if (isNegative)
+    ++current;
+  // TODO: Help the compiler do the div and mod at compile time or get rid of
+  // them.
+  Value::LargestUInt maxIntegerValue =
+      isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1
+                 : Value::maxLargestUInt;
+  Value::LargestUInt threshold = maxIntegerValue / 10;
+  Value::LargestUInt value = 0;
+  while (current < token.end_) {
+    Char c = *current++;
+    if (c < '0' || c > '9')
+      return decodeDouble(token, decoded);
+    auto digit(static_cast<Value::UInt>(c - '0'));
+    if (value >= threshold) {
+      // We've hit or exceeded the max value divided by 10 (rounded down). If
+      // a) we've only just touched the limit, b) this is the last digit, and
+      // c) it's small enough to fit in that rounding delta, we're okay.
+      // Otherwise treat this number as a double to avoid overflow.
+      if (value > threshold || current != token.end_ ||
+          digit > maxIntegerValue % 10) {
+        return decodeDouble(token, decoded);
+      }
+    }
+    value = value * 10 + digit;
+  }
+  if (isNegative && value == maxIntegerValue)
+    decoded = Value::minLargestInt;
+  else if (isNegative)
+    decoded = -Value::LargestInt(value);
+  else if (value <= Value::LargestUInt(Value::maxInt))
+    decoded = Value::LargestInt(value);
+  else
+    decoded = value;
+  return true;
+}
+
+bool Reader::decodeDouble(Token& token) {
+  Value decoded;
+  if (!decodeDouble(token, decoded))
+    return false;
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool Reader::decodeDouble(Token& token, Value& decoded) {
+  double value = 0;
+  String buffer(token.start_, token.end_);
+  IStringStream is(buffer);
+  if (!(is >> value))
+    return addError(
+        "'" + String(token.start_, token.end_) + "' is not a number.", token);
+  decoded = value;
+  return true;
+}
+
+bool Reader::decodeString(Token& token) {
+  String decoded_string;
+  if (!decodeString(token, decoded_string))
+    return false;
+  Value decoded(decoded_string);
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool Reader::decodeString(Token& token, String& decoded) {
+  decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
+  Location current = token.start_ + 1; // skip '"'
+  Location end = token.end_ - 1;       // do not include '"'
+  while (current != end) {
+    Char c = *current++;
+    if (c == '"')
+      break;
+    else if (c == '\\') {
+      if (current == end)
+        return addError("Empty escape sequence in string", token, current);
+      Char escape = *current++;
+      switch (escape) {
+      case '"':
+        decoded += '"';
+        break;
+      case '/':
+        decoded += '/';
+        break;
+      case '\\':
+        decoded += '\\';
+        break;
+      case 'b':
+        decoded += '\b';
+        break;
+      case 'f':
+        decoded += '\f';
+        break;
+      case 'n':
+        decoded += '\n';
+        break;
+      case 'r':
+        decoded += '\r';
+        break;
+      case 't':
+        decoded += '\t';
+        break;
+      case 'u': {
+        unsigned int unicode;
+        if (!decodeUnicodeCodePoint(token, current, end, unicode))
+          return false;
+        decoded += codePointToUTF8(unicode);
+      } break;
+      default:
+        return addError("Bad escape sequence in string", token, current);
+      }
+    } else {
+      decoded += c;
+    }
+  }
+  return true;
+}
+
+bool Reader::decodeUnicodeCodePoint(Token& token,
+                                    Location& current,
+                                    Location end,
+                                    unsigned int& unicode) {
+
+  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
+    return false;
+  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
+    // surrogate pairs
+    if (end - current < 6)
+      return addError(
+          "additional six characters expected to parse unicode surrogate pair.",
+          token, current);
+    if (*(current++) == '\\' && *(current++) == 'u') {
+      unsigned int surrogatePair;
+      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
+        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
+      } else
+        return false;
+    } else
+      return addError("expecting another \\u token to begin the second half of "
+                      "a unicode surrogate pair",
+                      token, current);
+  }
+  return true;
+}
+
+bool Reader::decodeUnicodeEscapeSequence(Token& token,
+                                         Location& current,
+                                         Location end,
+                                         unsigned int& ret_unicode) {
+  if (end - current < 4)
+    return addError(
+        "Bad unicode escape sequence in string: four digits expected.", token,
+        current);
+  int unicode = 0;
+  for (int index = 0; index < 4; ++index) {
+    Char c = *current++;
+    unicode *= 16;
+    if (c >= '0' && c <= '9')
+      unicode += c - '0';
+    else if (c >= 'a' && c <= 'f')
+      unicode += c - 'a' + 10;
+    else if (c >= 'A' && c <= 'F')
+      unicode += c - 'A' + 10;
+    else
+      return addError(
+          "Bad unicode escape sequence in string: hexadecimal digit expected.",
+          token, current);
+  }
+  ret_unicode = static_cast<unsigned int>(unicode);
+  return true;
+}
+
+bool Reader::addError(const String& message, Token& token, Location extra) {
+  ErrorInfo info;
+  info.token_ = token;
+  info.message_ = message;
+  info.extra_ = extra;
+  errors_.push_back(info);
+  return false;
+}
+
+bool Reader::recoverFromError(TokenType skipUntilToken) {
+  size_t const errorCount = errors_.size();
+  Token skip;
+  for (;;) {
+    if (!readToken(skip))
+      errors_.resize(errorCount); // discard errors caused by recovery
+    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
+      break;
+  }
+  errors_.resize(errorCount);
+  return false;
+}
+
+bool Reader::addErrorAndRecover(const String& message,
+                                Token& token,
+                                TokenType skipUntilToken) {
+  addError(message, token);
+  return recoverFromError(skipUntilToken);
+}
+
+Value& Reader::currentValue() { return *(nodes_.top()); }
+
+Reader::Char Reader::getNextChar() {
+  if (current_ == end_)
+    return 0;
+  return *current_++;
+}
+
+void Reader::getLocationLineAndColumn(Location location,
+                                      int& line,
+                                      int& column) const {
+  Location current = begin_;
+  Location lastLineStart = current;
+  line = 0;
+  while (current < location && current != end_) {
+    Char c = *current++;
+    if (c == '\r') {
+      if (*current == '\n')
+        ++current;
+      lastLineStart = current;
+      ++line;
+    } else if (c == '\n') {
+      lastLineStart = current;
+      ++line;
+    }
+  }
+  // column & line start at 1
+  column = int(location - lastLineStart) + 1;
+  ++line;
+}
+
+String Reader::getLocationLineAndColumn(Location location) const {
+  int line, column;
+  getLocationLineAndColumn(location, line, column);
+  char buffer[18 + 16 + 16 + 1];
+  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+  return buffer;
+}
+
+// Deprecated. Preserved for backward compatibility
+String Reader::getFormatedErrorMessages() const {
+  return getFormattedErrorMessages();
+}
+
+String Reader::getFormattedErrorMessages() const {
+  String formattedMessage;
+  for (const auto& error : errors_) {
+    formattedMessage +=
+        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
+    formattedMessage += "  " + error.message_ + "\n";
+    if (error.extra_)
+      formattedMessage +=
+          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
+  }
+  return formattedMessage;
+}
+
+std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
+  std::vector<Reader::StructuredError> allErrors;
+  for (const auto& error : errors_) {
+    Reader::StructuredError structured;
+    structured.offset_start = error.token_.start_ - begin_;
+    structured.offset_limit = error.token_.end_ - begin_;
+    structured.message = error.message_;
+    allErrors.push_back(structured);
+  }
+  return allErrors;
+}
+
+bool Reader::pushError(const Value& value, const String& message) {
+  ptrdiff_t const length = end_ - begin_;
+  if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
+    return false;
+  Token token;
+  token.type_ = tokenError;
+  token.start_ = begin_ + value.getOffsetStart();
+  token.end_ = end_ + value.getOffsetLimit();
+  ErrorInfo info;
+  info.token_ = token;
+  info.message_ = message;
+  info.extra_ = nullptr;
+  errors_.push_back(info);
+  return true;
+}
+
+bool Reader::pushError(const Value& value,
+                       const String& message,
+                       const Value& extra) {
+  ptrdiff_t const length = end_ - begin_;
+  if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
+      extra.getOffsetLimit() > length)
+    return false;
+  Token token;
+  token.type_ = tokenError;
+  token.start_ = begin_ + value.getOffsetStart();
+  token.end_ = begin_ + value.getOffsetLimit();
+  ErrorInfo info;
+  info.token_ = token;
+  info.message_ = message;
+  info.extra_ = begin_ + extra.getOffsetStart();
+  errors_.push_back(info);
+  return true;
+}
+
+bool Reader::good() const { return errors_.empty(); }
+
+// exact copy of Features
+class OurFeatures {
+public:
+  static OurFeatures all();
+  bool allowComments_;
+  bool strictRoot_;
+  bool allowDroppedNullPlaceholders_;
+  bool allowNumericKeys_;
+  bool allowSingleQuotes_;
+  bool failIfExtra_;
+  bool rejectDupKeys_;
+  bool allowSpecialFloats_;
+  size_t stackLimit_;
+}; // OurFeatures
+
+// exact copy of Implementation of class Features
+// ////////////////////////////////
+
+OurFeatures OurFeatures::all() { return {}; }
+
+// Implementation of class Reader
+// ////////////////////////////////
+
+// exact copy of Reader, renamed to OurReader
+class OurReader {
+public:
+  typedef char Char;
+  typedef const Char* Location;
+  struct StructuredError {
+    ptrdiff_t offset_start;
+    ptrdiff_t offset_limit;
+    String message;
+  };
+
+  OurReader(OurFeatures const& features);
+  bool parse(const char* beginDoc,
+             const char* endDoc,
+             Value& root,
+             bool collectComments = true);
+  String getFormattedErrorMessages() const;
+  std::vector<StructuredError> getStructuredErrors() const;
+  bool pushError(const Value& value, const String& message);
+  bool pushError(const Value& value, const String& message, const Value& extra);
+  bool good() const;
+
+private:
+  OurReader(OurReader const&);      // no impl
+  void operator=(OurReader const&); // no impl
+
+  enum TokenType {
+    tokenEndOfStream = 0,
+    tokenObjectBegin,
+    tokenObjectEnd,
+    tokenArrayBegin,
+    tokenArrayEnd,
+    tokenString,
+    tokenNumber,
+    tokenTrue,
+    tokenFalse,
+    tokenNull,
+    tokenNaN,
+    tokenPosInf,
+    tokenNegInf,
+    tokenArraySeparator,
+    tokenMemberSeparator,
+    tokenComment,
+    tokenError
+  };
+
+  class Token {
+  public:
+    TokenType type_;
+    Location start_;
+    Location end_;
+  };
+
+  class ErrorInfo {
+  public:
+    Token token_;
+    String message_;
+    Location extra_;
+  };
+
+  typedef std::deque<ErrorInfo> Errors;
+
+  bool readToken(Token& token);
+  void skipSpaces();
+  bool match(Location pattern, int patternLength);
+  bool readComment();
+  bool readCStyleComment();
+  bool readCppStyleComment();
+  bool readString();
+  bool readStringSingleQuote();
+  bool readNumber(bool checkInf);
+  bool readValue();
+  bool readObject(Token& token);
+  bool readArray(Token& token);
+  bool decodeNumber(Token& token);
+  bool decodeNumber(Token& token, Value& decoded);
+  bool decodeString(Token& token);
+  bool decodeString(Token& token, String& decoded);
+  bool decodeDouble(Token& token);
+  bool decodeDouble(Token& token, Value& decoded);
+  bool decodeUnicodeCodePoint(Token& token,
+                              Location& current,
+                              Location end,
+                              unsigned int& unicode);
+  bool decodeUnicodeEscapeSequence(Token& token,
+                                   Location& current,
+                                   Location end,
+                                   unsigned int& unicode);
+  bool addError(const String& message, Token& token, Location extra = nullptr);
+  bool recoverFromError(TokenType skipUntilToken);
+  bool addErrorAndRecover(const String& message,
+                          Token& token,
+                          TokenType skipUntilToken);
+  void skipUntilSpace();
+  Value& currentValue();
+  Char getNextChar();
+  void
+  getLocationLineAndColumn(Location location, int& line, int& column) const;
+  String getLocationLineAndColumn(Location location) const;
+  void addComment(Location begin, Location end, CommentPlacement placement);
+  void skipCommentTokens(Token& token);
+
+  static String normalizeEOL(Location begin, Location end);
+  static bool containsNewLine(Location begin, Location end);
+
+  typedef std::stack<Value*> Nodes;
+  Nodes nodes_;
+  Errors errors_;
+  String document_;
+  Location begin_;
+  Location end_;
+  Location current_;
+  Location lastValueEnd_;
+  Value* lastValue_;
+  String commentsBefore_;
+
+  OurFeatures const features_;
+  bool collectComments_;
+}; // OurReader
+
+// complete copy of Read impl, for OurReader
+
+bool OurReader::containsNewLine(OurReader::Location begin,
+                                OurReader::Location end) {
+  for (; begin < end; ++begin)
+    if (*begin == '\n' || *begin == '\r')
+      return true;
+  return false;
+}
+
+OurReader::OurReader(OurFeatures const& features)
+    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
+      lastValue_(), commentsBefore_(), features_(features), collectComments_() {
+}
+
+bool OurReader::parse(const char* beginDoc,
+                      const char* endDoc,
+                      Value& root,
+                      bool collectComments) {
+  if (!features_.allowComments_) {
+    collectComments = false;
+  }
+
+  begin_ = beginDoc;
+  end_ = endDoc;
+  collectComments_ = collectComments;
+  current_ = begin_;
+  lastValueEnd_ = nullptr;
+  lastValue_ = nullptr;
+  commentsBefore_.clear();
+  errors_.clear();
+  while (!nodes_.empty())
+    nodes_.pop();
+  nodes_.push(&root);
+
+  bool successful = readValue();
+  Token token;
+  skipCommentTokens(token);
+  if (features_.failIfExtra_) {
+    if ((features_.strictRoot_ || token.type_ != tokenError) &&
+        token.type_ != tokenEndOfStream) {
+      addError("Extra non-whitespace after JSON value.", token);
+      return false;
+    }
+  }
+  if (collectComments_ && !commentsBefore_.empty())
+    root.setComment(commentsBefore_, commentAfter);
+  if (features_.strictRoot_) {
+    if (!root.isArray() && !root.isObject()) {
+      // Set error location to start of doc, ideally should be first token found
+      // in doc
+      token.type_ = tokenError;
+      token.start_ = beginDoc;
+      token.end_ = endDoc;
+      addError(
+          "A valid JSON document must be either an array or an object value.",
+          token);
+      return false;
+    }
+  }
+  return successful;
+}
+
+bool OurReader::readValue() {
+  //  To preserve the old behaviour we cast size_t to int.
+  if (nodes_.size() > features_.stackLimit_)
+    throwRuntimeError("Exceeded stackLimit in readValue().");
+  Token token;
+  skipCommentTokens(token);
+  bool successful = true;
+
+  if (collectComments_ && !commentsBefore_.empty()) {
+    currentValue().setComment(commentsBefore_, commentBefore);
+    commentsBefore_.clear();
+  }
+
+  switch (token.type_) {
+  case tokenObjectBegin:
+    successful = readObject(token);
+    currentValue().setOffsetLimit(current_ - begin_);
+    break;
+  case tokenArrayBegin:
+    successful = readArray(token);
+    currentValue().setOffsetLimit(current_ - begin_);
+    break;
+  case tokenNumber:
+    successful = decodeNumber(token);
+    break;
+  case tokenString:
+    successful = decodeString(token);
+    break;
+  case tokenTrue: {
+    Value v(true);
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenFalse: {
+    Value v(false);
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenNull: {
+    Value v;
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenNaN: {
+    Value v(std::numeric_limits<double>::quiet_NaN());
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenPosInf: {
+    Value v(std::numeric_limits<double>::infinity());
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenNegInf: {
+    Value v(-std::numeric_limits<double>::infinity());
+    currentValue().swapPayload(v);
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+  } break;
+  case tokenArraySeparator:
+  case tokenObjectEnd:
+  case tokenArrayEnd:
+    if (features_.allowDroppedNullPlaceholders_) {
+      // "Un-read" the current token and mark the current value as a null
+      // token.
+      current_--;
+      Value v;
+      currentValue().swapPayload(v);
+      currentValue().setOffsetStart(current_ - begin_ - 1);
+      currentValue().setOffsetLimit(current_ - begin_);
+      break;
+    } // else, fall through ...
+  default:
+    currentValue().setOffsetStart(token.start_ - begin_);
+    currentValue().setOffsetLimit(token.end_ - begin_);
+    return addError("Syntax error: value, object or array expected.", token);
+  }
+
+  if (collectComments_) {
+    lastValueEnd_ = current_;
+    lastValue_ = &currentValue();
+  }
+
+  return successful;
+}
+
+void OurReader::skipCommentTokens(Token& token) {
+  if (features_.allowComments_) {
+    do {
+      readToken(token);
+    } while (token.type_ == tokenComment);
+  } else {
+    readToken(token);
+  }
+}
+
+bool OurReader::readToken(Token& token) {
+  skipSpaces();
+  token.start_ = current_;
+  Char c = getNextChar();
+  bool ok = true;
+  switch (c) {
+  case '{':
+    token.type_ = tokenObjectBegin;
+    break;
+  case '}':
+    token.type_ = tokenObjectEnd;
+    break;
+  case '[':
+    token.type_ = tokenArrayBegin;
+    break;
+  case ']':
+    token.type_ = tokenArrayEnd;
+    break;
+  case '"':
+    token.type_ = tokenString;
+    ok = readString();
+    break;
+  case '\'':
+    if (features_.allowSingleQuotes_) {
+      token.type_ = tokenString;
+      ok = readStringSingleQuote();
+      break;
+    } // else fall through
+  case '/':
+    token.type_ = tokenComment;
+    ok = readComment();
+    break;
+  case '0':
+  case '1':
+  case '2':
+  case '3':
+  case '4':
+  case '5':
+  case '6':
+  case '7':
+  case '8':
+  case '9':
+    token.type_ = tokenNumber;
+    readNumber(false);
+    break;
+  case '-':
+    if (readNumber(true)) {
+      token.type_ = tokenNumber;
+    } else {
+      token.type_ = tokenNegInf;
+      ok = features_.allowSpecialFloats_ && match("nfinity", 7);
+    }
+    break;
+  case 't':
+    token.type_ = tokenTrue;
+    ok = match("rue", 3);
+    break;
+  case 'f':
+    token.type_ = tokenFalse;
+    ok = match("alse", 4);
+    break;
+  case 'n':
+    token.type_ = tokenNull;
+    ok = match("ull", 3);
+    break;
+  case 'N':
+    if (features_.allowSpecialFloats_) {
+      token.type_ = tokenNaN;
+      ok = match("aN", 2);
+    } else {
+      ok = false;
+    }
+    break;
+  case 'I':
+    if (features_.allowSpecialFloats_) {
+      token.type_ = tokenPosInf;
+      ok = match("nfinity", 7);
+    } else {
+      ok = false;
+    }
+    break;
+  case ',':
+    token.type_ = tokenArraySeparator;
+    break;
+  case ':':
+    token.type_ = tokenMemberSeparator;
+    break;
+  case 0:
+    token.type_ = tokenEndOfStream;
+    break;
+  default:
+    ok = false;
+    break;
+  }
+  if (!ok)
+    token.type_ = tokenError;
+  token.end_ = current_;
+  return true;
+}
+
+void OurReader::skipSpaces() {
+  while (current_ != end_) {
+    Char c = *current_;
+    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
+      ++current_;
+    else
+      break;
+  }
+}
+
+bool OurReader::match(Location pattern, int patternLength) {
+  if (end_ - current_ < patternLength)
+    return false;
+  int index = patternLength;
+  while (index--)
+    if (current_[index] != pattern[index])
+      return false;
+  current_ += patternLength;
+  return true;
+}
+
+bool OurReader::readComment() {
+  Location commentBegin = current_ - 1;
+  Char c = getNextChar();
+  bool successful = false;
+  if (c == '*')
+    successful = readCStyleComment();
+  else if (c == '/')
+    successful = readCppStyleComment();
+  if (!successful)
+    return false;
+
+  if (collectComments_) {
+    CommentPlacement placement = commentBefore;
+    if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
+      if (c != '*' || !containsNewLine(commentBegin, current_))
+        placement = commentAfterOnSameLine;
+    }
+
+    addComment(commentBegin, current_, placement);
+  }
+  return true;
+}
+
+String OurReader::normalizeEOL(OurReader::Location begin,
+                               OurReader::Location end) {
+  String normalized;
+  normalized.reserve(static_cast<size_t>(end - begin));
+  OurReader::Location current = begin;
+  while (current != end) {
+    char c = *current++;
+    if (c == '\r') {
+      if (current != end && *current == '\n')
+        // convert dos EOL
+        ++current;
+      // convert Mac EOL
+      normalized += '\n';
+    } else {
+      normalized += c;
+    }
+  }
+  return normalized;
+}
+
+void OurReader::addComment(Location begin,
+                           Location end,
+                           CommentPlacement placement) {
+  assert(collectComments_);
+  const String& normalized = normalizeEOL(begin, end);
+  if (placement == commentAfterOnSameLine) {
+    assert(lastValue_ != nullptr);
+    lastValue_->setComment(normalized, placement);
+  } else {
+    commentsBefore_ += normalized;
+  }
+}
+
+bool OurReader::readCStyleComment() {
+  while ((current_ + 1) < end_) {
+    Char c = getNextChar();
+    if (c == '*' && *current_ == '/')
+      break;
+  }
+  return getNextChar() == '/';
+}
+
+bool OurReader::readCppStyleComment() {
+  while (current_ != end_) {
+    Char c = getNextChar();
+    if (c == '\n')
+      break;
+    if (c == '\r') {
+      // Consume DOS EOL. It will be normalized in addComment.
+      if (current_ != end_ && *current_ == '\n')
+        getNextChar();
+      // Break on Moc OS 9 EOL.
+      break;
+    }
+  }
+  return true;
+}
+
+bool OurReader::readNumber(bool checkInf) {
+  const char* p = current_;
+  if (checkInf && p != end_ && *p == 'I') {
+    current_ = ++p;
+    return false;
+  }
+  char c = '0'; // stopgap for already consumed character
+  // integral part
+  while (c >= '0' && c <= '9')
+    c = (current_ = p) < end_ ? *p++ : '\0';
+  // fractional part
+  if (c == '.') {
+    c = (current_ = p) < end_ ? *p++ : '\0';
+    while (c >= '0' && c <= '9')
+      c = (current_ = p) < end_ ? *p++ : '\0';
+  }
+  // exponential part
+  if (c == 'e' || c == 'E') {
+    c = (current_ = p) < end_ ? *p++ : '\0';
+    if (c == '+' || c == '-')
+      c = (current_ = p) < end_ ? *p++ : '\0';
+    while (c >= '0' && c <= '9')
+      c = (current_ = p) < end_ ? *p++ : '\0';
+  }
+  return true;
+}
+bool OurReader::readString() {
+  Char c = 0;
+  while (current_ != end_) {
+    c = getNextChar();
+    if (c == '\\')
+      getNextChar();
+    else if (c == '"')
+      break;
+  }
+  return c == '"';
+}
+
+bool OurReader::readStringSingleQuote() {
+  Char c = 0;
+  while (current_ != end_) {
+    c = getNextChar();
+    if (c == '\\')
+      getNextChar();
+    else if (c == '\'')
+      break;
+  }
+  return c == '\'';
+}
+
+bool OurReader::readObject(Token& token) {
+  Token tokenName;
+  String name;
+  Value init(objectValue);
+  currentValue().swapPayload(init);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  while (readToken(tokenName)) {
+    bool initialTokenOk = true;
+    while (tokenName.type_ == tokenComment && initialTokenOk)
+      initialTokenOk = readToken(tokenName);
+    if (!initialTokenOk)
+      break;
+    if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
+      return true;
+    name.clear();
+    if (tokenName.type_ == tokenString) {
+      if (!decodeString(tokenName, name))
+        return recoverFromError(tokenObjectEnd);
+    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
+      Value numberName;
+      if (!decodeNumber(tokenName, numberName))
+        return recoverFromError(tokenObjectEnd);
+      name = numberName.asString();
+    } else {
+      break;
+    }
+
+    Token colon;
+    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
+      return addErrorAndRecover("Missing ':' after object member name", colon,
+                                tokenObjectEnd);
+    }
+    if (name.length() >= (1U << 30))
+      throwRuntimeError("keylength >= 2^30");
+    if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
+      String msg = "Duplicate key: '" + name + "'";
+      return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
+    }
+    Value& value = currentValue()[name];
+    nodes_.push(&value);
+    bool ok = readValue();
+    nodes_.pop();
+    if (!ok) // error already set
+      return recoverFromError(tokenObjectEnd);
+
+    Token comma;
+    if (!readToken(comma) ||
+        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
+         comma.type_ != tokenComment)) {
+      return addErrorAndRecover("Missing ',' or '}' in object declaration",
+                                comma, tokenObjectEnd);
+    }
+    bool finalizeTokenOk = true;
+    while (comma.type_ == tokenComment && finalizeTokenOk)
+      finalizeTokenOk = readToken(comma);
+    if (comma.type_ == tokenObjectEnd)
+      return true;
+  }
+  return addErrorAndRecover("Missing '}' or object member name", tokenName,
+                            tokenObjectEnd);
+}
+
+bool OurReader::readArray(Token& token) {
+  Value init(arrayValue);
+  currentValue().swapPayload(init);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  skipSpaces();
+  if (current_ != end_ && *current_ == ']') // empty array
+  {
+    Token endArray;
+    readToken(endArray);
+    return true;
+  }
+  int index = 0;
+  for (;;) {
+    Value& value = currentValue()[index++];
+    nodes_.push(&value);
+    bool ok = readValue();
+    nodes_.pop();
+    if (!ok) // error already set
+      return recoverFromError(tokenArrayEnd);
+
+    Token currentToken;
+    // Accept Comment after last item in the array.
+    ok = readToken(currentToken);
+    while (currentToken.type_ == tokenComment && ok) {
+      ok = readToken(currentToken);
+    }
+    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
+                         currentToken.type_ != tokenArrayEnd);
+    if (!ok || badTokenType) {
+      return addErrorAndRecover("Missing ',' or ']' in array declaration",
+                                currentToken, tokenArrayEnd);
+    }
+    if (currentToken.type_ == tokenArrayEnd)
+      break;
+  }
+  return true;
+}
+
+bool OurReader::decodeNumber(Token& token) {
+  Value decoded;
+  if (!decodeNumber(token, decoded))
+    return false;
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool OurReader::decodeNumber(Token& token, Value& decoded) {
+  // Attempts to parse the number as an integer. If the number is
+  // larger than the maximum supported value of an integer then
+  // we decode the number as a double.
+  Location current = token.start_;
+  bool isNegative = *current == '-';
+  if (isNegative)
+    ++current;
+  // TODO: Help the compiler do the div and mod at compile time or get rid of
+  // them.
+  Value::LargestUInt maxIntegerValue =
+      isNegative ? Value::LargestUInt(Value::minLargestInt)
+                 : Value::maxLargestUInt;
+  Value::LargestUInt threshold = maxIntegerValue / 10;
+  Value::LargestUInt value = 0;
+  while (current < token.end_) {
+    Char c = *current++;
+    if (c < '0' || c > '9')
+      return decodeDouble(token, decoded);
+    auto digit(static_cast<Value::UInt>(c - '0'));
+    if (value >= threshold) {
+      // We've hit or exceeded the max value divided by 10 (rounded down). If
+      // a) we've only just touched the limit, b) this is the last digit, and
+      // c) it's small enough to fit in that rounding delta, we're okay.
+      // Otherwise treat this number as a double to avoid overflow.
+      if (value > threshold || current != token.end_ ||
+          digit > maxIntegerValue % 10) {
+        return decodeDouble(token, decoded);
+      }
+    }
+    value = value * 10 + digit;
+  }
+  if (isNegative)
+    decoded = -Value::LargestInt(value);
+  else if (value <= Value::LargestUInt(Value::maxInt))
+    decoded = Value::LargestInt(value);
+  else
+    decoded = value;
+  return true;
+}
+
+bool OurReader::decodeDouble(Token& token) {
+  Value decoded;
+  if (!decodeDouble(token, decoded))
+    return false;
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool OurReader::decodeDouble(Token& token, Value& decoded) {
+  double value = 0;
+  const int bufferSize = 32;
+  int count;
+  ptrdiff_t const length = token.end_ - token.start_;
+
+  // Sanity check to avoid buffer overflow exploits.
+  if (length < 0) {
+    return addError("Unable to parse token length", token);
+  }
+  auto const ulength = static_cast<size_t>(length);
+
+  // Avoid using a string constant for the format control string given to
+  // sscanf, as this can cause hard to debug crashes on OS X. See here for more
+  // info:
+  //
+  //     http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
+  char format[] = "%lf";
+
+  if (length <= bufferSize) {
+    Char buffer[bufferSize + 1];
+    memcpy(buffer, token.start_, ulength);
+    buffer[length] = 0;
+    fixNumericLocaleInput(buffer, buffer + length);
+    count = sscanf(buffer, format, &value);
+  } else {
+    String buffer(token.start_, token.end_);
+    count = sscanf(buffer.c_str(), format, &value);
+  }
+
+  if (count != 1)
+    return addError(
+        "'" + String(token.start_, token.end_) + "' is not a number.", token);
+  decoded = value;
+  return true;
+}
+
+bool OurReader::decodeString(Token& token) {
+  String decoded_string;
+  if (!decodeString(token, decoded_string))
+    return false;
+  Value decoded(decoded_string);
+  currentValue().swapPayload(decoded);
+  currentValue().setOffsetStart(token.start_ - begin_);
+  currentValue().setOffsetLimit(token.end_ - begin_);
+  return true;
+}
+
+bool OurReader::decodeString(Token& token, String& decoded) {
+  decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
+  Location current = token.start_ + 1; // skip '"'
+  Location end = token.end_ - 1;       // do not include '"'
+  while (current != end) {
+    Char c = *current++;
+    if (c == '"')
+      break;
+    else if (c == '\\') {
+      if (current == end)
+        return addError("Empty escape sequence in string", token, current);
+      Char escape = *current++;
+      switch (escape) {
+      case '"':
+        decoded += '"';
+        break;
+      case '/':
+        decoded += '/';
+        break;
+      case '\\':
+        decoded += '\\';
+        break;
+      case 'b':
+        decoded += '\b';
+        break;
+      case 'f':
+        decoded += '\f';
+        break;
+      case 'n':
+        decoded += '\n';
+        break;
+      case 'r':
+        decoded += '\r';
+        break;
+      case 't':
+        decoded += '\t';
+        break;
+      case 'u': {
+        unsigned int unicode;
+        if (!decodeUnicodeCodePoint(token, current, end, unicode))
+          return false;
+        decoded += codePointToUTF8(unicode);
+      } break;
+      default:
+        return addError("Bad escape sequence in string", token, current);
+      }
+    } else {
+      decoded += c;
+    }
+  }
+  return true;
+}
+
+bool OurReader::decodeUnicodeCodePoint(Token& token,
+                                       Location& current,
+                                       Location end,
+                                       unsigned int& unicode) {
+
+  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
+    return false;
+  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
+    // surrogate pairs
+    if (end - current < 6)
+      return addError(
+          "additional six characters expected to parse unicode surrogate pair.",
+          token, current);
+    if (*(current++) == '\\' && *(current++) == 'u') {
+      unsigned int surrogatePair;
+      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
+        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
+      } else
+        return false;
+    } else
+      return addError("expecting another \\u token to begin the second half of "
+                      "a unicode surrogate pair",
+                      token, current);
+  }
+  return true;
+}
+
+bool OurReader::decodeUnicodeEscapeSequence(Token& token,
+                                            Location& current,
+                                            Location end,
+                                            unsigned int& ret_unicode) {
+  if (end - current < 4)
+    return addError(
+        "Bad unicode escape sequence in string: four digits expected.", token,
+        current);
+  int unicode = 0;
+  for (int index = 0; index < 4; ++index) {
+    Char c = *current++;
+    unicode *= 16;
+    if (c >= '0' && c <= '9')
+      unicode += c - '0';
+    else if (c >= 'a' && c <= 'f')
+      unicode += c - 'a' + 10;
+    else if (c >= 'A' && c <= 'F')
+      unicode += c - 'A' + 10;
+    else
+      return addError(
+          "Bad unicode escape sequence in string: hexadecimal digit expected.",
+          token, current);
+  }
+  ret_unicode = static_cast<unsigned int>(unicode);
+  return true;
+}
+
+bool OurReader::addError(const String& message, Token& token, Location extra) {
+  ErrorInfo info;
+  info.token_ = token;
+  info.message_ = message;
+  info.extra_ = extra;
+  errors_.push_back(info);
+  return false;
+}
+
+bool OurReader::recoverFromError(TokenType skipUntilToken) {
+  size_t errorCount = errors_.size();
+  Token skip;
+  for (;;) {
+    if (!readToken(skip))
+      errors_.resize(errorCount); // discard errors caused by recovery
+    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
+      break;
+  }
+  errors_.resize(errorCount);
+  return false;
+}
+
+bool OurReader::addErrorAndRecover(const String& message,
+                                   Token& token,
+                                   TokenType skipUntilToken) {
+  addError(message, token);
+  return recoverFromError(skipUntilToken);
+}
+
+Value& OurReader::currentValue() { return *(nodes_.top()); }
+
+OurReader::Char OurReader::getNextChar() {
+  if (current_ == end_)
+    return 0;
+  return *current_++;
+}
+
+void OurReader::getLocationLineAndColumn(Location location,
+                                         int& line,
+                                         int& column) const {
+  Location current = begin_;
+  Location lastLineStart = current;
+  line = 0;
+  while (current < location && current != end_) {
+    Char c = *current++;
+    if (c == '\r') {
+      if (*current == '\n')
+        ++current;
+      lastLineStart = current;
+      ++line;
+    } else if (c == '\n') {
+      lastLineStart = current;
+      ++line;
+    }
+  }
+  // column & line start at 1
+  column = int(location - lastLineStart) + 1;
+  ++line;
+}
+
+String OurReader::getLocationLineAndColumn(Location location) const {
+  int line, column;
+  getLocationLineAndColumn(location, line, column);
+  char buffer[18 + 16 + 16 + 1];
+  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+  return buffer;
+}
+
+String OurReader::getFormattedErrorMessages() const {
+  String formattedMessage;
+  for (const auto& error : errors_) {
+    formattedMessage +=
+        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
+    formattedMessage += "  " + error.message_ + "\n";
+    if (error.extra_)
+      formattedMessage +=
+          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
+  }
+  return formattedMessage;
+}
+
+std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
+  std::vector<OurReader::StructuredError> allErrors;
+  for (const auto& error : errors_) {
+    OurReader::StructuredError structured;
+    structured.offset_start = error.token_.start_ - begin_;
+    structured.offset_limit = error.token_.end_ - begin_;
+    structured.message = error.message_;
+    allErrors.push_back(structured);
+  }
+  return allErrors;
+}
+
+bool OurReader::pushError(const Value& value, const String& message) {
+  ptrdiff_t length = end_ - begin_;
+  if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
+    return false;
+  Token token;
+  token.type_ = tokenError;
+  token.start_ = begin_ + value.getOffsetStart();
+  token.end_ = end_ + value.getOffsetLimit();
+  ErrorInfo info;
+  info.token_ = token;
+  info.message_ = message;
+  info.extra_ = nullptr;
+  errors_.push_back(info);
+  return true;
+}
+
+bool OurReader::pushError(const Value& value,
+                          const String& message,
+                          const Value& extra) {
+  ptrdiff_t length = end_ - begin_;
+  if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
+      extra.getOffsetLimit() > length)
+    return false;
+  Token token;
+  token.type_ = tokenError;
+  token.start_ = begin_ + value.getOffsetStart();
+  token.end_ = begin_ + value.getOffsetLimit();
+  ErrorInfo info;
+  info.token_ = token;
+  info.message_ = message;
+  info.extra_ = begin_ + extra.getOffsetStart();
+  errors_.push_back(info);
+  return true;
+}
+
+bool OurReader::good() const { return errors_.empty(); }
+
+class OurCharReader : public CharReader {
+  bool const collectComments_;
+  OurReader reader_;
+
+public:
+  OurCharReader(bool collectComments, OurFeatures const& features)
+      : collectComments_(collectComments), reader_(features) {}
+  bool parse(char const* beginDoc,
+             char const* endDoc,
+             Value* root,
+             String* errs) override {
+    bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
+    if (errs) {
+      *errs = reader_.getFormattedErrorMessages();
+    }
+    return ok;
+  }
+};
+
+CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
+CharReaderBuilder::~CharReaderBuilder() = default;
+CharReader* CharReaderBuilder::newCharReader() const {
+  bool collectComments = settings_["collectComments"].asBool();
+  OurFeatures features = OurFeatures::all();
+  features.allowComments_ = settings_["allowComments"].asBool();
+  features.strictRoot_ = settings_["strictRoot"].asBool();
+  features.allowDroppedNullPlaceholders_ =
+      settings_["allowDroppedNullPlaceholders"].asBool();
+  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
+  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
+#if defined(JSON_HAS_INT64)
+  features.stackLimit_ = settings_["stackLimit"].asUInt64();
+#else
+  features.stackLimit_ = settings_["stackLimit"].asUInt();
+#endif
+  features.failIfExtra_ = settings_["failIfExtra"].asBool();
+  features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
+  features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
+  return new OurCharReader(collectComments, features);
+}
+static void getValidReaderKeys(std::set<String>* valid_keys) {
+  valid_keys->clear();
+  valid_keys->insert("collectComments");
+  valid_keys->insert("allowComments");
+  valid_keys->insert("strictRoot");
+  valid_keys->insert("allowDroppedNullPlaceholders");
+  valid_keys->insert("allowNumericKeys");
+  valid_keys->insert("allowSingleQuotes");
+  valid_keys->insert("stackLimit");
+  valid_keys->insert("failIfExtra");
+  valid_keys->insert("rejectDupKeys");
+  valid_keys->insert("allowSpecialFloats");
+}
+bool CharReaderBuilder::validate(Json::Value* invalid) const {
+  Json::Value my_invalid;
+  if (!invalid)
+    invalid = &my_invalid; // so we do not need to test for NULL
+  Json::Value& inv = *invalid;
+  std::set<String> valid_keys;
+  getValidReaderKeys(&valid_keys);
+  Value::Members keys = settings_.getMemberNames();
+  size_t n = keys.size();
+  for (size_t i = 0; i < n; ++i) {
+    String const& key = keys[i];
+    if (valid_keys.find(key) == valid_keys.end()) {
+      inv[key] = settings_[key];
+    }
+  }
+  return inv.empty();
+}
+Value& CharReaderBuilder::operator[](const String& key) {
+  return settings_[key];
+}
+// static
+void CharReaderBuilder::strictMode(Json::Value* settings) {
+  //! [CharReaderBuilderStrictMode]
+  (*settings)["allowComments"] = false;
+  (*settings)["strictRoot"] = true;
+  (*settings)["allowDroppedNullPlaceholders"] = false;
+  (*settings)["allowNumericKeys"] = false;
+  (*settings)["allowSingleQuotes"] = false;
+  (*settings)["stackLimit"] = 1000;
+  (*settings)["failIfExtra"] = true;
+  (*settings)["rejectDupKeys"] = true;
+  (*settings)["allowSpecialFloats"] = false;
+  //! [CharReaderBuilderStrictMode]
+}
+// static
+void CharReaderBuilder::setDefaults(Json::Value* settings) {
+  //! [CharReaderBuilderDefaults]
+  (*settings)["collectComments"] = true;
+  (*settings)["allowComments"] = true;
+  (*settings)["strictRoot"] = false;
+  (*settings)["allowDroppedNullPlaceholders"] = false;
+  (*settings)["allowNumericKeys"] = false;
+  (*settings)["allowSingleQuotes"] = false;
+  (*settings)["stackLimit"] = 1000;
+  (*settings)["failIfExtra"] = false;
+  (*settings)["rejectDupKeys"] = false;
+  (*settings)["allowSpecialFloats"] = false;
+  //! [CharReaderBuilderDefaults]
+}
+
+//////////////////////////////////
+// global functions
+
+bool parseFromStream(CharReader::Factory const& fact,
+                     IStream& sin,
+                     Value* root,
+                     String* errs) {
+  OStringStream ssin;
+  ssin << sin.rdbuf();
+  String doc = ssin.str();
+  char const* begin = doc.data();
+  char const* end = begin + doc.size();
+  // Note that we do not actually need a null-terminator.
+  CharReaderPtr const reader(fact.newCharReader());
+  return reader->parse(begin, end, root, errs);
+}
+
+IStream& operator>>(IStream& sin, Value& root) {
+  CharReaderBuilder b;
+  String errs;
+  bool ok = parseFromStream(b, sin, &root, &errs);
+  if (!ok) {
+    throwRuntimeError(errs);
+  }
+  return sin;
+}
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_reader.cpp
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_valueiterator.inl
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+// included by json_value.cpp
+
+namespace Json {
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueIteratorBase
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueIteratorBase::ValueIteratorBase() : current_() {}
+
+ValueIteratorBase::ValueIteratorBase(
+    const Value::ObjectValues::iterator& current)
+    : current_(current), isNull_(false) {}
+
+Value& ValueIteratorBase::deref() const { return current_->second; }
+
+void ValueIteratorBase::increment() { ++current_; }
+
+void ValueIteratorBase::decrement() { --current_; }
+
+ValueIteratorBase::difference_type
+ValueIteratorBase::computeDistance(const SelfType& other) const {
+#ifdef JSON_USE_CPPTL_SMALLMAP
+  return other.current_ - current_;
+#else
+  // Iterator for null value are initialized using the default
+  // constructor, which initialize current_ to the default
+  // std::map::iterator. As begin() and end() are two instance
+  // of the default std::map::iterator, they can not be compared.
+  // To allow this, we handle this comparison specifically.
+  if (isNull_ && other.isNull_) {
+    return 0;
+  }
+
+  // Usage of std::distance is not portable (does not compile with Sun Studio 12
+  // RogueWave STL,
+  // which is the one used by default).
+  // Using a portable hand-made version for non random iterator instead:
+  //   return difference_type( std::distance( current_, other.current_ ) );
+  difference_type myDistance = 0;
+  for (Value::ObjectValues::iterator it = current_; it != other.current_;
+       ++it) {
+    ++myDistance;
+  }
+  return myDistance;
+#endif
+}
+
+bool ValueIteratorBase::isEqual(const SelfType& other) const {
+  if (isNull_) {
+    return other.isNull_;
+  }
+  return current_ == other.current_;
+}
+
+void ValueIteratorBase::copy(const SelfType& other) {
+  current_ = other.current_;
+  isNull_ = other.isNull_;
+}
+
+Value ValueIteratorBase::key() const {
+  const Value::CZString czstring = (*current_).first;
+  if (czstring.data()) {
+    if (czstring.isStaticString())
+      return Value(StaticString(czstring.data()));
+    return Value(czstring.data(), czstring.data() + czstring.length());
+  }
+  return Value(czstring.index());
+}
+
+UInt ValueIteratorBase::index() const {
+  const Value::CZString czstring = (*current_).first;
+  if (!czstring.data())
+    return czstring.index();
+  return Value::UInt(-1);
+}
+
+String ValueIteratorBase::name() const {
+  char const* keey;
+  char const* end;
+  keey = memberName(&end);
+  if (!keey)
+    return String();
+  return String(keey, end);
+}
+
+char const* ValueIteratorBase::memberName() const {
+  const char* cname = (*current_).first.data();
+  return cname ? cname : "";
+}
+
+char const* ValueIteratorBase::memberName(char const** end) const {
+  const char* cname = (*current_).first.data();
+  if (!cname) {
+    *end = nullptr;
+    return nullptr;
+  }
+  *end = cname + (*current_).first.length();
+  return cname;
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueConstIterator
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueConstIterator::ValueConstIterator() = default;
+
+ValueConstIterator::ValueConstIterator(
+    const Value::ObjectValues::iterator& current)
+    : ValueIteratorBase(current) {}
+
+ValueConstIterator::ValueConstIterator(ValueIterator const& other)
+    : ValueIteratorBase(other) {}
+
+ValueConstIterator& ValueConstIterator::
+operator=(const ValueIteratorBase& other) {
+  copy(other);
+  return *this;
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class ValueIterator
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+ValueIterator::ValueIterator() = default;
+
+ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current)
+    : ValueIteratorBase(current) {}
+
+ValueIterator::ValueIterator(const ValueConstIterator& other)
+    : ValueIteratorBase(other) {
+  throwRuntimeError("ConstIterator to Iterator should never be allowed.");
+}
+
+ValueIterator::ValueIterator(const ValueIterator& other) = default;
+
+ValueIterator& ValueIterator::operator=(const SelfType& other) {
+  copy(other);
+  return *this;
+}
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_valueiterator.inl
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_value.cpp
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include <json/assertions.h>
+#include <json/value.h>
+#include <json/writer.h>
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <cassert>
+#include <cmath>
+#include <cstring>
+#include <sstream>
+#include <utility>
+#ifdef JSON_USE_CPPTL
+#include <cpptl/conststring.h>
+#endif
+#include <algorithm> // min()
+#include <cstddef>   // size_t
+
+// Provide implementation equivalent of std::snprintf for older _MSC compilers
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#include <stdarg.h>
+static int msvc_pre1900_c99_vsnprintf(char* outBuf,
+                                      size_t size,
+                                      const char* format,
+                                      va_list ap) {
+  int count = -1;
+  if (size != 0)
+    count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
+  if (count == -1)
+    count = _vscprintf(format, ap);
+  return count;
+}
+
+int JSON_API msvc_pre1900_c99_snprintf(char* outBuf,
+                                       size_t size,
+                                       const char* format,
+                                       ...) {
+  va_list ap;
+  va_start(ap, format);
+  const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap);
+  va_end(ap);
+  return count;
+}
+#endif
+
+// Disable warning C4702 : unreachable code
+#if defined(_MSC_VER)
+#pragma warning(disable : 4702)
+#endif
+
+#define JSON_ASSERT_UNREACHABLE assert(false)
+
+namespace Json {
+
+// This is a walkaround to avoid the static initialization of Value::null.
+// kNull must be word-aligned to avoid crashing on ARM.  We use an alignment of
+// 8 (instead of 4) as a bit of future-proofing.
+#if defined(__ARMEL__)
+#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
+#else
+#define ALIGNAS(byte_alignment)
+#endif
+// static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };
+// const unsigned char& kNullRef = kNull[0];
+// const Value& Value::null = reinterpret_cast<const Value&>(kNullRef);
+// const Value& Value::nullRef = null;
+
+// static
+Value const& Value::nullSingleton() {
+  static Value const nullStatic;
+  return nullStatic;
+}
+
+// for backwards compatibility, we'll leave these global references around, but
+// DO NOT use them in JSONCPP library code any more!
+Value const& Value::null = Value::nullSingleton();
+Value const& Value::nullRef = Value::nullSingleton();
+
+const Int Value::minInt = Int(~(UInt(-1) / 2));
+const Int Value::maxInt = Int(UInt(-1) / 2);
+const UInt Value::maxUInt = UInt(-1);
+#if defined(JSON_HAS_INT64)
+const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2));
+const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2);
+const UInt64 Value::maxUInt64 = UInt64(-1);
+// The constant is hard-coded because some compiler have trouble
+// converting Value::maxUInt64 to a double correctly (AIX/xlC).
+// Assumes that UInt64 is a 64 bits integer.
+static const double maxUInt64AsDouble = 18446744073709551615.0;
+#endif // defined(JSON_HAS_INT64)
+const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2));
+const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);
+const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
+
+const UInt Value::defaultRealPrecision = 17;
+
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+template <typename T, typename U>
+static inline bool InRange(double d, T min, U max) {
+  // The casts can lose precision, but we are looking only for
+  // an approximate range. Might fail on edge cases though. ~cdunn
+  // return d >= static_cast<double>(min) && d <= static_cast<double>(max);
+  return d >= min && d <= max;
+}
+#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+static inline double integerToDouble(Json::UInt64 value) {
+  return static_cast<double>(Int64(value / 2)) * 2.0 +
+         static_cast<double>(Int64(value & 1));
+}
+
+template <typename T> static inline double integerToDouble(T value) {
+  return static_cast<double>(value);
+}
+
+template <typename T, typename U>
+static inline bool InRange(double d, T min, U max) {
+  return d >= integerToDouble(min) && d <= integerToDouble(max);
+}
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+
+/** Duplicates the specified string value.
+ * @param value Pointer to the string to duplicate. Must be zero-terminated if
+ *              length is "unknown".
+ * @param length Length of the value. if equals to unknown, then it will be
+ *               computed using strlen(value).
+ * @return Pointer on the duplicate instance of string.
+ */
+static inline char* duplicateStringValue(const char* value, size_t length) {
+  // Avoid an integer overflow in the call to malloc below by limiting length
+  // to a sane value.
+  if (length >= static_cast<size_t>(Value::maxInt))
+    length = Value::maxInt - 1;
+
+  char* newString = static_cast<char*>(malloc(length + 1));
+  if (newString == nullptr) {
+    throwRuntimeError("in Json::Value::duplicateStringValue(): "
+                      "Failed to allocate string value buffer");
+  }
+  memcpy(newString, value, length);
+  newString[length] = 0;
+  return newString;
+}
+
+/* Record the length as a prefix.
+ */
+static inline char* duplicateAndPrefixStringValue(const char* value,
+                                                  unsigned int length) {
+  // Avoid an integer overflow in the call to malloc below by limiting length
+  // to a sane value.
+  JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) -
+                                    sizeof(unsigned) - 1U,
+                      "in Json::Value::duplicateAndPrefixStringValue(): "
+                      "length too big for prefixing");
+  unsigned actualLength = length + static_cast<unsigned>(sizeof(unsigned)) + 1U;
+  char* newString = static_cast<char*>(malloc(actualLength));
+  if (newString == nullptr) {
+    throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): "
+                      "Failed to allocate string value buffer");
+  }
+  *reinterpret_cast<unsigned*>(newString) = length;
+  memcpy(newString + sizeof(unsigned), value, length);
+  newString[actualLength - 1U] =
+      0; // to avoid buffer over-run accidents by users later
+  return newString;
+}
+inline static void decodePrefixedString(bool isPrefixed,
+                                        char const* prefixed,
+                                        unsigned* length,
+                                        char const** value) {
+  if (!isPrefixed) {
+    *length = static_cast<unsigned>(strlen(prefixed));
+    *value = prefixed;
+  } else {
+    *length = *reinterpret_cast<unsigned const*>(prefixed);
+    *value = prefixed + sizeof(unsigned);
+  }
+}
+/** Free the string duplicated by
+ * duplicateStringValue()/duplicateAndPrefixStringValue().
+ */
+#if JSONCPP_USING_SECURE_MEMORY
+static inline void releasePrefixedStringValue(char* value) {
+  unsigned length = 0;
+  char const* valueDecoded;
+  decodePrefixedString(true, value, &length, &valueDecoded);
+  size_t const size = sizeof(unsigned) + length + 1U;
+  memset(value, 0, size);
+  free(value);
+}
+static inline void releaseStringValue(char* value, unsigned length) {
+  // length==0 => we allocated the strings memory
+  size_t size = (length == 0) ? strlen(value) : length;
+  memset(value, 0, size);
+  free(value);
+}
+#else  // !JSONCPP_USING_SECURE_MEMORY
+static inline void releasePrefixedStringValue(char* value) { free(value); }
+static inline void releaseStringValue(char* value, unsigned) { free(value); }
+#endif // JSONCPP_USING_SECURE_MEMORY
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// ValueInternals...
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+#if !defined(JSON_IS_AMALGAMATION)
+
+#include "json_valueiterator.inl"
+#endif // if !defined(JSON_IS_AMALGAMATION)
+
+namespace Json {
+
+Exception::Exception(String msg) : msg_(std::move(msg)) {}
+Exception::~Exception() JSONCPP_NOEXCEPT {}
+char const* Exception::what() const JSONCPP_NOEXCEPT { return msg_.c_str(); }
+RuntimeError::RuntimeError(String const& msg) : Exception(msg) {}
+LogicError::LogicError(String const& msg) : Exception(msg) {}
+JSONCPP_NORETURN void throwRuntimeError(String const& msg) {
+  throw RuntimeError(msg);
+}
+JSONCPP_NORETURN void throwLogicError(String const& msg) {
+  throw LogicError(msg);
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class Value::CommentInfo
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+Value::CommentInfo::CommentInfo() = default;
+
+Value::CommentInfo::~CommentInfo() {
+  if (comment_)
+    releaseStringValue(comment_, 0u);
+}
+
+void Value::CommentInfo::setComment(const char* text, size_t len) {
+  if (comment_) {
+    releaseStringValue(comment_, 0u);
+    comment_ = nullptr;
+  }
+  JSON_ASSERT(text != nullptr);
+  JSON_ASSERT_MESSAGE(
+      text[0] == '\0' || text[0] == '/',
+      "in Json::Value::setComment(): Comments must start with /");
+  // It seems that /**/ style comments are acceptable as well.
+  comment_ = duplicateStringValue(text, len);
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class Value::CZString
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+// Notes: policy_ indicates if the string was allocated when
+// a string is stored.
+
+Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {}
+
+Value::CZString::CZString(char const* str,
+                          unsigned length,
+                          DuplicationPolicy allocate)
+    : cstr_(str) {
+  // allocate != duplicate
+  storage_.policy_ = allocate & 0x3;
+  storage_.length_ = length & 0x3FFFFFFF;
+}
+
+Value::CZString::CZString(const CZString& other) {
+  cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != nullptr
+               ? duplicateStringValue(other.cstr_, other.storage_.length_)
+               : other.cstr_);
+  storage_.policy_ =
+      static_cast<unsigned>(
+          other.cstr_
+              ? (static_cast<DuplicationPolicy>(other.storage_.policy_) ==
+                         noDuplication
+                     ? noDuplication
+                     : duplicate)
+              : static_cast<DuplicationPolicy>(other.storage_.policy_)) &
+      3U;
+  storage_.length_ = other.storage_.length_;
+}
+
+#if JSON_HAS_RVALUE_REFERENCES
+Value::CZString::CZString(CZString&& other)
+    : cstr_(other.cstr_), index_(other.index_) {
+  other.cstr_ = nullptr;
+}
+#endif
+
+Value::CZString::~CZString() {
+  if (cstr_ && storage_.policy_ == duplicate) {
+    releaseStringValue(const_cast<char*>(cstr_),
+                       storage_.length_ + 1u); // +1 for null terminating
+                                               // character for sake of
+                                               // completeness but not actually
+                                               // necessary
+  }
+}
+
+void Value::CZString::swap(CZString& other) {
+  std::swap(cstr_, other.cstr_);
+  std::swap(index_, other.index_);
+}
+
+Value::CZString& Value::CZString::operator=(const CZString& other) {
+  cstr_ = other.cstr_;
+  index_ = other.index_;
+  return *this;
+}
+
+#if JSON_HAS_RVALUE_REFERENCES
+Value::CZString& Value::CZString::operator=(CZString&& other) {
+  cstr_ = other.cstr_;
+  index_ = other.index_;
+  other.cstr_ = nullptr;
+  return *this;
+}
+#endif
+
+bool Value::CZString::operator<(const CZString& other) const {
+  if (!cstr_)
+    return index_ < other.index_;
+  // return strcmp(cstr_, other.cstr_) < 0;
+  // Assume both are strings.
+  unsigned this_len = this->storage_.length_;
+  unsigned other_len = other.storage_.length_;
+  unsigned min_len = std::min<unsigned>(this_len, other_len);
+  JSON_ASSERT(this->cstr_ && other.cstr_);
+  int comp = memcmp(this->cstr_, other.cstr_, min_len);
+  if (comp < 0)
+    return true;
+  if (comp > 0)
+    return false;
+  return (this_len < other_len);
+}
+
+bool Value::CZString::operator==(const CZString& other) const {
+  if (!cstr_)
+    return index_ == other.index_;
+  // return strcmp(cstr_, other.cstr_) == 0;
+  // Assume both are strings.
+  unsigned this_len = this->storage_.length_;
+  unsigned other_len = other.storage_.length_;
+  if (this_len != other_len)
+    return false;
+  JSON_ASSERT(this->cstr_ && other.cstr_);
+  int comp = memcmp(this->cstr_, other.cstr_, this_len);
+  return comp == 0;
+}
+
+ArrayIndex Value::CZString::index() const { return index_; }
+
+// const char* Value::CZString::c_str() const { return cstr_; }
+const char* Value::CZString::data() const { return cstr_; }
+unsigned Value::CZString::length() const { return storage_.length_; }
+bool Value::CZString::isStaticString() const {
+  return storage_.policy_ == noDuplication;
+}
+
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// class Value::Value
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+// //////////////////////////////////////////////////////////////////
+
+/*! \internal Default constructor initialization must be equivalent to:
+ * memset( this, 0, sizeof(Value) )
+ * This optimization is used in ValueInternalMap fast allocator.
+ */
+Value::Value(ValueType type) {
+  static char const emptyString[] = "";
+  initBasic(type);
+  switch (type) {
+  case nullValue:
+    break;
+  case intValue:
+  case uintValue:
+    value_.int_ = 0;
+    break;
+  case realValue:
+    value_.real_ = 0.0;
+    break;
+  case stringValue:
+    // allocated_ == false, so this is safe.
+    value_.string_ = const_cast<char*>(static_cast<char const*>(emptyString));
+    break;
+  case arrayValue:
+  case objectValue:
+    value_.map_ = new ObjectValues();
+    break;
+  case booleanValue:
+    value_.bool_ = false;
+    break;
+  default:
+    JSON_ASSERT_UNREACHABLE;
+  }
+}
+
+Value::Value(Int value) {
+  initBasic(intValue);
+  value_.int_ = value;
+}
+
+Value::Value(UInt value) {
+  initBasic(uintValue);
+  value_.uint_ = value;
+}
+#if defined(JSON_HAS_INT64)
+Value::Value(Int64 value) {
+  initBasic(intValue);
+  value_.int_ = value;
+}
+Value::Value(UInt64 value) {
+  initBasic(uintValue);
+  value_.uint_ = value;
+}
+#endif // defined(JSON_HAS_INT64)
+
+Value::Value(double value) {
+  initBasic(realValue);
+  value_.real_ = value;
+}
+
+Value::Value(const char* value) {
+  initBasic(stringValue, true);
+  JSON_ASSERT_MESSAGE(value != nullptr,
+                      "Null Value Passed to Value Constructor");
+  value_.string_ = duplicateAndPrefixStringValue(
+      value, static_cast<unsigned>(strlen(value)));
+}
+
+Value::Value(const char* begin, const char* end) {
+  initBasic(stringValue, true);
+  value_.string_ =
+      duplicateAndPrefixStringValue(begin, static_cast<unsigned>(end - begin));
+}
+
+Value::Value(const String& value) {
+  initBasic(stringValue, true);
+  value_.string_ = duplicateAndPrefixStringValue(
+      value.data(), static_cast<unsigned>(value.length()));
+}
+
+Value::Value(const StaticString& value) {
+  initBasic(stringValue);
+  value_.string_ = const_cast<char*>(value.c_str());
+}
+
+#ifdef JSON_USE_CPPTL
+Value::Value(const CppTL::ConstString& value) {
+  initBasic(stringValue, true);
+  value_.string_ = duplicateAndPrefixStringValue(
+      value, static_cast<unsigned>(value.length()));
+}
+#endif
+
+Value::Value(bool value) {
+  initBasic(booleanValue);
+  value_.bool_ = value;
+}
+
+Value::Value(const Value& other) {
+  dupPayload(other);
+  dupMeta(other);
+}
+
+Value::Value(Value&& other) {
+  initBasic(nullValue);
+  swap(other);
+}
+
+Value::~Value() {
+  releasePayload();
+  delete[] comments_;
+  value_.uint_ = 0;
+}
+
+Value& Value::operator=(const Value& other) {
+  Value(other).swap(*this);
+  return *this;
+}
+
+Value& Value::operator=(Value&& other) {
+  other.swap(*this);
+  return *this;
+}
+
+void Value::swapPayload(Value& other) {
+  std::swap(bits_, other.bits_);
+  std::swap(value_, other.value_);
+}
+
+void Value::copyPayload(const Value& other) {
+  releasePayload();
+  dupPayload(other);
+}
+
+void Value::swap(Value& other) {
+  swapPayload(other);
+  std::swap(comments_, other.comments_);
+  std::swap(start_, other.start_);
+  std::swap(limit_, other.limit_);
+}
+
+void Value::copy(const Value& other) {
+  copyPayload(other);
+  delete[] comments_;
+  dupMeta(other);
+}
+
+ValueType Value::type() const {
+  return static_cast<ValueType>(bits_.value_type_);
+}
+
+int Value::compare(const Value& other) const {
+  if (*this < other)
+    return -1;
+  if (*this > other)
+    return 1;
+  return 0;
+}
+
+bool Value::operator<(const Value& other) const {
+  int typeDelta = type() - other.type();
+  if (typeDelta)
+    return typeDelta < 0 ? true : false;
+  switch (type()) {
+  case nullValue:
+    return false;
+  case intValue:
+    return value_.int_ < other.value_.int_;
+  case uintValue:
+    return value_.uint_ < other.value_.uint_;
+  case realValue:
+    return value_.real_ < other.value_.real_;
+  case booleanValue:
+    return value_.bool_ < other.value_.bool_;
+  case stringValue: {
+    if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
+      if (other.value_.string_)
+        return true;
+      else
+        return false;
+    }
+    unsigned this_len;
+    unsigned other_len;
+    char const* this_str;
+    char const* other_str;
+    decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+                         &this_str);
+    decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
+                         &other_str);
+    unsigned min_len = std::min<unsigned>(this_len, other_len);
+    JSON_ASSERT(this_str && other_str);
+    int comp = memcmp(this_str, other_str, min_len);
+    if (comp < 0)
+      return true;
+    if (comp > 0)
+      return false;
+    return (this_len < other_len);
+  }
+  case arrayValue:
+  case objectValue: {
+    int delta = int(value_.map_->size() - other.value_.map_->size());
+    if (delta)
+      return delta < 0;
+    return (*value_.map_) < (*other.value_.map_);
+  }
+  default:
+    JSON_ASSERT_UNREACHABLE;
+  }
+  return false; // unreachable
+}
+
+bool Value::operator<=(const Value& other) const { return !(other < *this); }
+
+bool Value::operator>=(const Value& other) const { return !(*this < other); }
+
+bool Value::operator>(const Value& other) const { return other < *this; }
+
+bool Value::operator==(const Value& other) const {
+  if (type() != other.type())
+    return false;
+  switch (type()) {
+  case nullValue:
+    return true;
+  case intValue:
+    return value_.int_ == other.value_.int_;
+  case uintValue:
+    return value_.uint_ == other.value_.uint_;
+  case realValue:
+    return value_.real_ == other.value_.real_;
+  case booleanValue:
+    return value_.bool_ == other.value_.bool_;
+  case stringValue: {
+    if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
+      return (value_.string_ == other.value_.string_);
+    }
+    unsigned this_len;
+    unsigned other_len;
+    char const* this_str;
+    char const* other_str;
+    decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+                         &this_str);
+    decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
+                         &other_str);
+    if (this_len != other_len)
+      return false;
+    JSON_ASSERT(this_str && other_str);
+    int comp = memcmp(this_str, other_str, this_len);
+    return comp == 0;
+  }
+  case arrayValue:
+  case objectValue:
+    return value_.map_->size() == other.value_.map_->size() &&
+           (*value_.map_) == (*other.value_.map_);
+  default:
+    JSON_ASSERT_UNREACHABLE;
+  }
+  return false; // unreachable
+}
+
+bool Value::operator!=(const Value& other) const { return !(*this == other); }
+
+const char* Value::asCString() const {
+  JSON_ASSERT_MESSAGE(type() == stringValue,
+                      "in Json::Value::asCString(): requires stringValue");
+  if (value_.string_ == nullptr)
+    return nullptr;
+  unsigned this_len;
+  char const* this_str;
+  decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+                       &this_str);
+  return this_str;
+}
+
+#if JSONCPP_USING_SECURE_MEMORY
+unsigned Value::getCStringLength() const {
+  JSON_ASSERT_MESSAGE(type() == stringValue,
+                      "in Json::Value::asCString(): requires stringValue");
+  if (value_.string_ == 0)
+    return 0;
+  unsigned this_len;
+  char const* this_str;
+  decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+                       &this_str);
+  return this_len;
+}
+#endif
+
+bool Value::getString(char const** begin, char const** end) const {
+  if (type() != stringValue)
+    return false;
+  if (value_.string_ == nullptr)
+    return false;
+  unsigned length;
+  decodePrefixedString(this->isAllocated(), this->value_.string_, &length,
+                       begin);
+  *end = *begin + length;
+  return true;
+}
+
+String Value::asString() const {
+  switch (type()) {
+  case nullValue:
+    return "";
+  case stringValue: {
+    if (value_.string_ == nullptr)
+      return "";
+    unsigned this_len;
+    char const* this_str;
+    decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+                         &this_str);
+    return String(this_str, this_len);
+  }
+  case booleanValue:
+    return value_.bool_ ? "true" : "false";
+  case intValue:
+    return valueToString(value_.int_);
+  case uintValue:
+    return valueToString(value_.uint_);
+  case realValue:
+    return valueToString(value_.real_);
+  default:
+    JSON_FAIL_MESSAGE("Type is not convertible to string");
+  }
+}
+
+#ifdef JSON_USE_CPPTL
+CppTL::ConstString Value::asConstString() const {
+  unsigned len;
+  char const* str;
+  decodePrefixedString(isAllocated(), value_.string_, &len, &str);
+  return CppTL::ConstString(str, len);
+}
+#endif
+
+Value::Int Value::asInt() const {
+  switch (type()) {
+  case intValue:
+    JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range");
+    return Int(value_.int_);
+  case uintValue:
+    JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range");
+    return Int(value_.uint_);
+  case realValue:
+    JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt),
+                        "double out of Int range");
+    return Int(value_.real_);
+  case nullValue:
+    return 0;
+  case booleanValue:
+    return value_.bool_ ? 1 : 0;
+  default:
+    break;
+  }
+  JSON_FAIL_MESSAGE("Value is not convertible to Int.");
+}
+
+Value::UInt Value::asUInt() const {
+  switch (type()) {
+  case intValue:
+    JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range");
+    return UInt(value_.int_);
+  case uintValue:
+    JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range");
+    return UInt(value_.uint_);
+  case realValue:
+    JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt),
+                        "double out of UInt range");
+    return UInt(value_.real_);
+  case nullValue:
+    return 0;
+  case booleanValue:
+    return value_.bool_ ? 1 : 0;
+  default:
+    break;
+  }
+  JSON_FAIL_MESSAGE("Value is not convertible to UInt.");
+}
+
+#if defined(JSON_HAS_INT64)
+
+Value::Int64 Value::asInt64() const {
+  switch (type()) {
+  case intValue:
+    return Int64(value_.int_);
+  case uintValue:
+    JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range");
+    return Int64(value_.uint_);
+  case realValue:
+    JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64),
+                        "double out of Int64 range");
+    return Int64(value_.real_);
+  case nullValue:
+    return 0;
+  case booleanValue:
+    return value_.bool_ ? 1 : 0;
+  default:
+    break;
+  }
+  JSON_FAIL_MESSAGE("Value is not convertible to Int64.");
+}
+
+Value::UInt64 Value::asUInt64() const {
+  switch (type()) {
+  case intValue:
+    JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range");
+    return UInt64(value_.int_);
+  case uintValue:
+    return UInt64(value_.uint_);
+  case realValue:
+    JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64),
+                        "double out of UInt64 range");
+    return UInt64(value_.real_);
+  case nullValue:
+    return 0;
+  case booleanValue:
+    return value_.bool_ ? 1 : 0;
+  default:
+    break;
+  }
+  JSON_FAIL_MESSAGE("Value is not convertible to UInt64.");
+}
+#endif // if defined(JSON_HAS_INT64)
+
+LargestInt Value::asLargestInt() const {
+#if defined(JSON_NO_INT64)
+  return asInt();
+#else
+  return asInt64();
+#endif
+}
+
+LargestUInt Value::asLargestUInt() const {
+#if defined(JSON_NO_INT64)
+  return asUInt();
+#else
+  return asUInt64();
+#endif
+}
+
+double Value::asDouble() const {
+  switch (type()) {
+  case intValue:
+    return static_cast<double>(value_.int_);
+  case uintValue:
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+    return static_cast<double>(value_.uint_);
+#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+    return integerToDouble(value_.uint_);
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+  case realValue:
+    return value_.real_;
+  case nullValue:
+    return 0.0;
+  case booleanValue:
+    return value_.bool_ ? 1.0 : 0.0;
+  default:
+    break;
+  }
+  JSON_FAIL_MESSAGE("Value is not convertible to double.");
+}
+
+float Value::asFloat() const {
+  switch (type()) {
+  case intValue:
+    return static_cast<float>(value_.int_);
+  case uintValue:
+#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+    return static_cast<float>(value_.uint_);
+#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+    // This can fail (silently?) if the value is bigger than MAX_FLOAT.
+    return static_cast<float>(integerToDouble(value_.uint_));
+#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
+  case realValue:
+    return static_cast<float>(value_.real_);
+  case nullValue:
+    return 0.0;
+  case booleanValue:
+    return value_.bool_ ? 1.0f : 0.0f;
+  default:
+    break;
+  }
+  JSON_FAIL_MESSAGE("Value is not convertible to float.");
+}
+
+bool Value::asBool() const {
+  switch (type()) {
+  case booleanValue:
+    return value_.bool_;
+  case nullValue:
+    return false;
+  case intValue:
+    return value_.int_ ? true : false;
+  case uintValue:
+    return value_.uint_ ? true : false;
+  case realValue:
+    // This is kind of strange. Not recommended.
+    return (value_.real_ != 0.0) ? true : false;
+  default:
+    break;
+  }
+  JSON_FAIL_MESSAGE("Value is not convertible to bool.");
+}
+
+bool Value::isConvertibleTo(ValueType other) const {
+  switch (other) {
+  case nullValue:
+    return (isNumeric() && asDouble() == 0.0) ||
+           (type() == booleanValue && value_.bool_ == false) ||
+           (type() == stringValue && asString().empty()) ||
+           (type() == arrayValue && value_.map_->empty()) ||
+           (type() == objectValue && value_.map_->empty()) ||
+           type() == nullValue;
+  case intValue:
+    return isInt() ||
+           (type() == realValue && InRange(value_.real_, minInt, maxInt)) ||
+           type() == booleanValue || type() == nullValue;
+  case uintValue:
+    return isUInt() ||
+           (type() == realValue && InRange(value_.real_, 0, maxUInt)) ||
+           type() == booleanValue || type() == nullValue;
+  case realValue:
+    return isNumeric() || type() == booleanValue || type() == nullValue;
+  case booleanValue:
+    return isNumeric() || type() == booleanValue || type() == nullValue;
+  case stringValue:
+    return isNumeric() || type() == booleanValue || type() == stringValue ||
+           type() == nullValue;
+  case arrayValue:
+    return type() == arrayValue || type() == nullValue;
+  case objectValue:
+    return type() == objectValue || type() == nullValue;
+  }
+  JSON_ASSERT_UNREACHABLE;
+  return false;
+}
+
+/// Number of values in array or object
+ArrayIndex Value::size() const {
+  switch (type()) {
+  case nullValue:
+  case intValue:
+  case uintValue:
+  case realValue:
+  case booleanValue:
+  case stringValue:
+    return 0;
+  case arrayValue: // size of the array is highest index + 1
+    if (!value_.map_->empty()) {
+      ObjectValues::const_iterator itLast = value_.map_->end();
+      --itLast;
+      return (*itLast).first.index() + 1;
+    }
+    return 0;
+  case objectValue:
+    return ArrayIndex(value_.map_->size());
+  }
+  JSON_ASSERT_UNREACHABLE;
+  return 0; // unreachable;
+}
+
+bool Value::empty() const {
+  if (isNull() || isArray() || isObject())
+    return size() == 0u;
+  else
+    return false;
+}
+
+Value::operator bool() const { return !isNull(); }
+
+void Value::clear() {
+  JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue ||
+                          type() == objectValue,
+                      "in Json::Value::clear(): requires complex value");
+  start_ = 0;
+  limit_ = 0;
+  switch (type()) {
+  case arrayValue:
+  case objectValue:
+    value_.map_->clear();
+    break;
+  default:
+    break;
+  }
+}
+
+void Value::resize(ArrayIndex newSize) {
+  JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
+                      "in Json::Value::resize(): requires arrayValue");
+  if (type() == nullValue)
+    *this = Value(arrayValue);
+  ArrayIndex oldSize = size();
+  if (newSize == 0)
+    clear();
+  else if (newSize > oldSize)
+    this->operator[](newSize - 1);
+  else {
+    for (ArrayIndex index = newSize; index < oldSize; ++index) {
+      value_.map_->erase(index);
+    }
+    JSON_ASSERT(size() == newSize);
+  }
+}
+
+Value& Value::operator[](ArrayIndex index) {
+  JSON_ASSERT_MESSAGE(
+      type() == nullValue || type() == arrayValue,
+      "in Json::Value::operator[](ArrayIndex): requires arrayValue");
+  if (type() == nullValue)
+    *this = Value(arrayValue);
+  CZString key(index);
+  auto it = value_.map_->lower_bound(key);
+  if (it != value_.map_->end() && (*it).first == key)
+    return (*it).second;
+
+  ObjectValues::value_type defaultValue(key, nullSingleton());
+  it = value_.map_->insert(it, defaultValue);
+  return (*it).second;
+}
+
+Value& Value::operator[](int index) {
+  JSON_ASSERT_MESSAGE(
+      index >= 0,
+      "in Json::Value::operator[](int index): index cannot be negative");
+  return (*this)[ArrayIndex(index)];
+}
+
+const Value& Value::operator[](ArrayIndex index) const {
+  JSON_ASSERT_MESSAGE(
+      type() == nullValue || type() == arrayValue,
+      "in Json::Value::operator[](ArrayIndex)const: requires arrayValue");
+  if (type() == nullValue)
+    return nullSingleton();
+  CZString key(index);
+  ObjectValues::const_iterator it = value_.map_->find(key);
+  if (it == value_.map_->end())
+    return nullSingleton();
+  return (*it).second;
+}
+
+const Value& Value::operator[](int index) const {
+  JSON_ASSERT_MESSAGE(
+      index >= 0,
+      "in Json::Value::operator[](int index) const: index cannot be negative");
+  return (*this)[ArrayIndex(index)];
+}
+
+void Value::initBasic(ValueType type, bool allocated) {
+  setType(type);
+  setIsAllocated(allocated);
+  comments_ = nullptr;
+  start_ = 0;
+  limit_ = 0;
+}
+
+void Value::dupPayload(const Value& other) {
+  setType(other.type());
+  setIsAllocated(false);
+  switch (type()) {
+  case nullValue:
+  case intValue:
+  case uintValue:
+  case realValue:
+  case booleanValue:
+    value_ = other.value_;
+    break;
+  case stringValue:
+    if (other.value_.string_ && other.isAllocated()) {
+      unsigned len;
+      char const* str;
+      decodePrefixedString(other.isAllocated(), other.value_.string_, &len,
+                           &str);
+      value_.string_ = duplicateAndPrefixStringValue(str, len);
+      setIsAllocated(true);
+    } else {
+      value_.string_ = other.value_.string_;
+    }
+    break;
+  case arrayValue:
+  case objectValue:
+    value_.map_ = new ObjectValues(*other.value_.map_);
+    break;
+  default:
+    JSON_ASSERT_UNREACHABLE;
+  }
+}
+
+void Value::releasePayload() {
+  switch (type()) {
+  case nullValue:
+  case intValue:
+  case uintValue:
+  case realValue:
+  case booleanValue:
+    break;
+  case stringValue:
+    if (isAllocated())
+      releasePrefixedStringValue(value_.string_);
+    break;
+  case arrayValue:
+  case objectValue:
+    delete value_.map_;
+    break;
+  default:
+    JSON_ASSERT_UNREACHABLE;
+  }
+}
+
+void Value::dupMeta(const Value& other) {
+  if (other.comments_) {
+    comments_ = new CommentInfo[numberOfCommentPlacement];
+    for (int comment = 0; comment < numberOfCommentPlacement; ++comment) {
+      const CommentInfo& otherComment = other.comments_[comment];
+      if (otherComment.comment_)
+        comments_[comment].setComment(otherComment.comment_,
+                                      strlen(otherComment.comment_));
+    }
+  } else {
+    comments_ = nullptr;
+  }
+  start_ = other.start_;
+  limit_ = other.limit_;
+}
+
+// Access an object value by name, create a null member if it does not exist.
+// @pre Type of '*this' is object or null.
+// @param key is null-terminated.
+Value& Value::resolveReference(const char* key) {
+  JSON_ASSERT_MESSAGE(
+      type() == nullValue || type() == objectValue,
+      "in Json::Value::resolveReference(): requires objectValue");
+  if (type() == nullValue)
+    *this = Value(objectValue);
+  CZString actualKey(key, static_cast<unsigned>(strlen(key)),
+                     CZString::noDuplication); // NOTE!
+  auto it = value_.map_->lower_bound(actualKey);
+  if (it != value_.map_->end() && (*it).first == actualKey)
+    return (*it).second;
+
+  ObjectValues::value_type defaultValue(actualKey, nullSingleton());
+  it = value_.map_->insert(it, defaultValue);
+  Value& value = (*it).second;
+  return value;
+}
+
+// @param key is not null-terminated.
+Value& Value::resolveReference(char const* key, char const* end) {
+  JSON_ASSERT_MESSAGE(
+      type() == nullValue || type() == objectValue,
+      "in Json::Value::resolveReference(key, end): requires objectValue");
+  if (type() == nullValue)
+    *this = Value(objectValue);
+  CZString actualKey(key, static_cast<unsigned>(end - key),
+                     CZString::duplicateOnCopy);
+  auto it = value_.map_->lower_bound(actualKey);
+  if (it != value_.map_->end() && (*it).first == actualKey)
+    return (*it).second;
+
+  ObjectValues::value_type defaultValue(actualKey, nullSingleton());
+  it = value_.map_->insert(it, defaultValue);
+  Value& value = (*it).second;
+  return value;
+}
+
+Value Value::get(ArrayIndex index, const Value& defaultValue) const {
+  const Value* value = &((*this)[index]);
+  return value == &nullSingleton() ? defaultValue : *value;
+}
+
+bool Value::isValidIndex(ArrayIndex index) const { return index < size(); }
+
+Value const* Value::find(char const* begin, char const* end) const {
+  JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
+                      "in Json::Value::find(key, end, found): requires "
+                      "objectValue or nullValue");
+  if (type() == nullValue)
+    return nullptr;
+  CZString actualKey(begin, static_cast<unsigned>(end - begin),
+                     CZString::noDuplication);
+  ObjectValues::const_iterator it = value_.map_->find(actualKey);
+  if (it == value_.map_->end())
+    return nullptr;
+  return &(*it).second;
+}
+const Value& Value::operator[](const char* key) const {
+  Value const* found = find(key, key + strlen(key));
+  if (!found)
+    return nullSingleton();
+  return *found;
+}
+Value const& Value::operator[](const String& key) const {
+  Value const* found = find(key.data(), key.data() + key.length());
+  if (!found)
+    return nullSingleton();
+  return *found;
+}
+
+Value& Value::operator[](const char* key) {
+  return resolveReference(key, key + strlen(key));
+}
+
+Value& Value::operator[](const String& key) {
+  return resolveReference(key.data(), key.data() + key.length());
+}
+
+Value& Value::operator[](const StaticString& key) {
+  return resolveReference(key.c_str());
+}
+
+#ifdef JSON_USE_CPPTL
+Value& Value::operator[](const CppTL::ConstString& key) {
+  return resolveReference(key.c_str(), key.end_c_str());
+}
+Value const& Value::operator[](CppTL::ConstString const& key) const {
+  Value const* found = find(key.c_str(), key.end_c_str());
+  if (!found)
+    return nullSingleton();
+  return *found;
+}
+#endif
+
+Value& Value::append(const Value& value) { return (*this)[size()] = value; }
+
+#if JSON_HAS_RVALUE_REFERENCES
+Value& Value::append(Value&& value) {
+  return (*this)[size()] = std::move(value);
+}
+#endif
+
+Value Value::get(char const* begin,
+                 char const* end,
+                 Value const& defaultValue) const {
+  Value const* found = find(begin, end);
+  return !found ? defaultValue : *found;
+}
+Value Value::get(char const* key, Value const& defaultValue) const {
+  return get(key, key + strlen(key), defaultValue);
+}
+Value Value::get(String const& key, Value const& defaultValue) const {
+  return get(key.data(), key.data() + key.length(), defaultValue);
+}
+
+bool Value::removeMember(const char* begin, const char* end, Value* removed) {
+  if (type() != objectValue) {
+    return false;
+  }
+  CZString actualKey(begin, static_cast<unsigned>(end - begin),
+                     CZString::noDuplication);
+  auto it = value_.map_->find(actualKey);
+  if (it == value_.map_->end())
+    return false;
+  if (removed)
+#if JSON_HAS_RVALUE_REFERENCES
+    *removed = std::move(it->second);
+#else
+    *removed = it->second;
+#endif
+  value_.map_->erase(it);
+  return true;
+}
+bool Value::removeMember(const char* key, Value* removed) {
+  return removeMember(key, key + strlen(key), removed);
+}
+bool Value::removeMember(String const& key, Value* removed) {
+  return removeMember(key.data(), key.data() + key.length(), removed);
+}
+void Value::removeMember(const char* key) {
+  JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
+                      "in Json::Value::removeMember(): requires objectValue");
+  if (type() == nullValue)
+    return;
+
+  CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication);
+  value_.map_->erase(actualKey);
+}
+void Value::removeMember(const String& key) { removeMember(key.c_str()); }
+
+bool Value::removeIndex(ArrayIndex index, Value* removed) {
+  if (type() != arrayValue) {
+    return false;
+  }
+  CZString key(index);
+  auto it = value_.map_->find(key);
+  if (it == value_.map_->end()) {
+    return false;
+  }
+  if (removed)
+    *removed = it->second;
+  ArrayIndex oldSize = size();
+  // shift left all items left, into the place of the "removed"
+  for (ArrayIndex i = index; i < (oldSize - 1); ++i) {
+    CZString keey(i);
+    (*value_.map_)[keey] = (*this)[i + 1];
+  }
+  // erase the last one ("leftover")
+  CZString keyLast(oldSize - 1);
+  auto itLast = value_.map_->find(keyLast);
+  value_.map_->erase(itLast);
+  return true;
+}
+
+#ifdef JSON_USE_CPPTL
+Value Value::get(const CppTL::ConstString& key,
+                 const Value& defaultValue) const {
+  return get(key.c_str(), key.end_c_str(), defaultValue);
+}
+#endif
+
+bool Value::isMember(char const* begin, char const* end) const {
+  Value const* value = find(begin, end);
+  return nullptr != value;
+}
+bool Value::isMember(char const* key) const {
+  return isMember(key, key + strlen(key));
+}
+bool Value::isMember(String const& key) const {
+  return isMember(key.data(), key.data() + key.length());
+}
+
+#ifdef JSON_USE_CPPTL
+bool Value::isMember(const CppTL::ConstString& key) const {
+  return isMember(key.c_str(), key.end_c_str());
+}
+#endif
+
+Value::Members Value::getMemberNames() const {
+  JSON_ASSERT_MESSAGE(
+      type() == nullValue || type() == objectValue,
+      "in Json::Value::getMemberNames(), value must be objectValue");
+  if (type() == nullValue)
+    return Value::Members();
+  Members members;
+  members.reserve(value_.map_->size());
+  ObjectValues::const_iterator it = value_.map_->begin();
+  ObjectValues::const_iterator itEnd = value_.map_->end();
+  for (; it != itEnd; ++it) {
+    members.push_back(String((*it).first.data(), (*it).first.length()));
+  }
+  return members;
+}
+//
+//# ifdef JSON_USE_CPPTL
+// EnumMemberNames
+// Value::enumMemberNames() const
+//{
+//   if ( type() == objectValue )
+//   {
+//      return CppTL::Enum::any(  CppTL::Enum::transform(
+//         CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ),
+//         MemberNamesTransform() ) );
+//   }
+//   return EnumMemberNames();
+//}
+//
+//
+// EnumValues
+// Value::enumValues() const
+//{
+//   if ( type() == objectValue  ||  type() == arrayValue )
+//      return CppTL::Enum::anyValues( *(value_.map_),
+//                                     CppTL::Type<const Value &>() );
+//   return EnumValues();
+//}
+//
+//# endif
+
+static bool IsIntegral(double d) {
+  double integral_part;
+  return modf(d, &integral_part) == 0.0;
+}
+
+bool Value::isNull() const { return type() == nullValue; }
+
+bool Value::isBool() const { return type() == booleanValue; }
+
+bool Value::isInt() const {
+  switch (type()) {
+  case intValue:
+#if defined(JSON_HAS_INT64)
+    return value_.int_ >= minInt && value_.int_ <= maxInt;
+#else
+    return true;
+#endif
+  case uintValue:
+    return value_.uint_ <= UInt(maxInt);
+  case realValue:
+    return value_.real_ >= minInt && value_.real_ <= maxInt &&
+           IsIntegral(value_.real_);
+  default:
+    break;
+  }
+  return false;
+}
+
+bool Value::isUInt() const {
+  switch (type()) {
+  case intValue:
+#if defined(JSON_HAS_INT64)
+    return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt);
+#else
+    return value_.int_ >= 0;
+#endif
+  case uintValue:
+#if defined(JSON_HAS_INT64)
+    return value_.uint_ <= maxUInt;
+#else
+    return true;
+#endif
+  case realValue:
+    return value_.real_ >= 0 && value_.real_ <= maxUInt &&
+           IsIntegral(value_.real_);
+  default:
+    break;
+  }
+  return false;
+}
+
+bool Value::isInt64() const {
+#if defined(JSON_HAS_INT64)
+  switch (type()) {
+  case intValue:
+    return true;
+  case uintValue:
+    return value_.uint_ <= UInt64(maxInt64);
+  case realValue:
+    // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a
+    // double, so double(maxInt64) will be rounded up to 2^63. Therefore we
+    // require the value to be strictly less than the limit.
+    return value_.real_ >= double(minInt64) &&
+           value_.real_ < double(maxInt64) && IsIntegral(value_.real_);
+  default:
+    break;
+  }
+#endif // JSON_HAS_INT64
+  return false;
+}
+
+bool Value::isUInt64() const {
+#if defined(JSON_HAS_INT64)
+  switch (type()) {
+  case intValue:
+    return value_.int_ >= 0;
+  case uintValue:
+    return true;
+  case realValue:
+    // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
+    // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
+    // require the value to be strictly less than the limit.
+    return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble &&
+           IsIntegral(value_.real_);
+  default:
+    break;
+  }
+#endif // JSON_HAS_INT64
+  return false;
+}
+
+bool Value::isIntegral() const {
+  switch (type()) {
+  case intValue:
+  case uintValue:
+    return true;
+  case realValue:
+#if defined(JSON_HAS_INT64)
+    // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
+    // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
+    // require the value to be strictly less than the limit.
+    return value_.real_ >= double(minInt64) &&
+           value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_);
+#else
+    return value_.real_ >= minInt && value_.real_ <= maxUInt &&
+           IsIntegral(value_.real_);
+#endif // JSON_HAS_INT64
+  default:
+    break;
+  }
+  return false;
+}
+
+bool Value::isDouble() const {
+  return type() == intValue || type() == uintValue || type() == realValue;
+}
+
+bool Value::isNumeric() const { return isDouble(); }
+
+bool Value::isString() const { return type() == stringValue; }
+
+bool Value::isArray() const { return type() == arrayValue; }
+
+bool Value::isObject() const { return type() == objectValue; }
+
+void Value::setComment(const char* comment,
+                       size_t len,
+                       CommentPlacement placement) {
+  if (!comments_)
+    comments_ = new CommentInfo[numberOfCommentPlacement];
+  if ((len > 0) && (comment[len - 1] == '\n')) {
+    // Always discard trailing newline, to aid indentation.
+    len -= 1;
+  }
+  comments_[placement].setComment(comment, len);
+}
+
+void Value::setComment(const char* comment, CommentPlacement placement) {
+  setComment(comment, strlen(comment), placement);
+}
+
+void Value::setComment(const String& comment, CommentPlacement placement) {
+  setComment(comment.c_str(), comment.length(), placement);
+}
+
+bool Value::hasComment(CommentPlacement placement) const {
+  return comments_ != nullptr && comments_[placement].comment_ != nullptr;
+}
+
+String Value::getComment(CommentPlacement placement) const {
+  if (hasComment(placement))
+    return comments_[placement].comment_;
+  return "";
+}
+
+void Value::setOffsetStart(ptrdiff_t start) { start_ = start; }
+
+void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; }
+
+ptrdiff_t Value::getOffsetStart() const { return start_; }
+
+ptrdiff_t Value::getOffsetLimit() const { return limit_; }
+
+String Value::toStyledString() const {
+  StreamWriterBuilder builder;
+
+  String out = this->hasComment(commentBefore) ? "\n" : "";
+  out += Json::writeString(builder, *this);
+  out += '\n';
+
+  return out;
+}
+
+Value::const_iterator Value::begin() const {
+  switch (type()) {
+  case arrayValue:
+  case objectValue:
+    if (value_.map_)
+      return const_iterator(value_.map_->begin());
+    break;
+  default:
+    break;
+  }
+  return {};
+}
+
+Value::const_iterator Value::end() const {
+  switch (type()) {
+  case arrayValue:
+  case objectValue:
+    if (value_.map_)
+      return const_iterator(value_.map_->end());
+    break;
+  default:
+    break;
+  }
+  return {};
+}
+
+Value::iterator Value::begin() {
+  switch (type()) {
+  case arrayValue:
+  case objectValue:
+    if (value_.map_)
+      return iterator(value_.map_->begin());
+    break;
+  default:
+    break;
+  }
+  return iterator();
+}
+
+Value::iterator Value::end() {
+  switch (type()) {
+  case arrayValue:
+  case objectValue:
+    if (value_.map_)
+      return iterator(value_.map_->end());
+    break;
+  default:
+    break;
+  }
+  return iterator();
+}
+
+// class PathArgument
+// //////////////////////////////////////////////////////////////////
+
+PathArgument::PathArgument() : key_() {}
+
+PathArgument::PathArgument(ArrayIndex index)
+    : key_(), index_(index), kind_(kindIndex) {}
+
+PathArgument::PathArgument(const char* key)
+    : key_(key), index_(), kind_(kindKey) {}
+
+PathArgument::PathArgument(const String& key)
+    : key_(key.c_str()), index_(), kind_(kindKey) {}
+
+// class Path
+// //////////////////////////////////////////////////////////////////
+
+Path::Path(const String& path,
+           const PathArgument& a1,
+           const PathArgument& a2,
+           const PathArgument& a3,
+           const PathArgument& a4,
+           const PathArgument& a5) {
+  InArgs in;
+  in.reserve(5);
+  in.push_back(&a1);
+  in.push_back(&a2);
+  in.push_back(&a3);
+  in.push_back(&a4);
+  in.push_back(&a5);
+  makePath(path, in);
+}
+
+void Path::makePath(const String& path, const InArgs& in) {
+  const char* current = path.c_str();
+  const char* end = current + path.length();
+  auto itInArg = in.begin();
+  while (current != end) {
+    if (*current == '[') {
+      ++current;
+      if (*current == '%')
+        addPathInArg(path, in, itInArg, PathArgument::kindIndex);
+      else {
+        ArrayIndex index = 0;
+        for (; current != end && *current >= '0' && *current <= '9'; ++current)
+          index = index * 10 + ArrayIndex(*current - '0');
+        args_.push_back(index);
+      }
+      if (current == end || *++current != ']')
+        invalidPath(path, int(current - path.c_str()));
+    } else if (*current == '%') {
+      addPathInArg(path, in, itInArg, PathArgument::kindKey);
+      ++current;
+    } else if (*current == '.' || *current == ']') {
+      ++current;
+    } else {
+      const char* beginName = current;
+      while (current != end && !strchr("[.", *current))
+        ++current;
+      args_.push_back(String(beginName, current));
+    }
+  }
+}
+
+void Path::addPathInArg(const String& /*path*/,
+                        const InArgs& in,
+                        InArgs::const_iterator& itInArg,
+                        PathArgument::Kind kind) {
+  if (itInArg == in.end()) {
+    // Error: missing argument %d
+  } else if ((*itInArg)->kind_ != kind) {
+    // Error: bad argument type
+  } else {
+    args_.push_back(**itInArg++);
+  }
+}
+
+void Path::invalidPath(const String& /*path*/, int /*location*/) {
+  // Error: invalid path.
+}
+
+const Value& Path::resolve(const Value& root) const {
+  const Value* node = &root;
+  for (const auto& arg : args_) {
+    if (arg.kind_ == PathArgument::kindIndex) {
+      if (!node->isArray() || !node->isValidIndex(arg.index_)) {
+        // Error: unable to resolve path (array value expected at position...
+        return Value::null;
+      }
+      node = &((*node)[arg.index_]);
+    } else if (arg.kind_ == PathArgument::kindKey) {
+      if (!node->isObject()) {
+        // Error: unable to resolve path (object value expected at position...)
+        return Value::null;
+      }
+      node = &((*node)[arg.key_]);
+      if (node == &Value::nullSingleton()) {
+        // Error: unable to resolve path (object has no member named '' at
+        // position...)
+        return Value::null;
+      }
+    }
+  }
+  return *node;
+}
+
+Value Path::resolve(const Value& root, const Value& defaultValue) const {
+  const Value* node = &root;
+  for (const auto& arg : args_) {
+    if (arg.kind_ == PathArgument::kindIndex) {
+      if (!node->isArray() || !node->isValidIndex(arg.index_))
+        return defaultValue;
+      node = &((*node)[arg.index_]);
+    } else if (arg.kind_ == PathArgument::kindKey) {
+      if (!node->isObject())
+        return defaultValue;
+      node = &((*node)[arg.key_]);
+      if (node == &Value::nullSingleton())
+        return defaultValue;
+    }
+  }
+  return *node;
+}
+
+Value& Path::make(Value& root) const {
+  Value* node = &root;
+  for (const auto& arg : args_) {
+    if (arg.kind_ == PathArgument::kindIndex) {
+      if (!node->isArray()) {
+        // Error: node is not an array at position ...
+      }
+      node = &((*node)[arg.index_]);
+    } else if (arg.kind_ == PathArgument::kindKey) {
+      if (!node->isObject()) {
+        // Error: node is not an object at position...
+      }
+      node = &((*node)[arg.key_]);
+    }
+  }
+  return *node;
+}
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_value.cpp
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+// //////////////////////////////////////////////////////////////////////
+// Beginning of content of file: src/lib_json/json_writer.cpp
+// //////////////////////////////////////////////////////////////////////
+
+// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
+// Distributed under MIT license, or public domain if desired and
+// recognized in your jurisdiction.
+// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
+
+#if !defined(JSON_IS_AMALGAMATION)
+#include "json_tool.h"
+#include <json/writer.h>
+#endif // if !defined(JSON_IS_AMALGAMATION)
+#include <cassert>
+#include <cstring>
+#include <iomanip>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <utility>
+
+#if __cplusplus >= 201103L
+#include <cmath>
+#include <cstdio>
+
+#if !defined(isnan)
+#define isnan std::isnan
+#endif
+
+#if !defined(isfinite)
+#define isfinite std::isfinite
+#endif
+
+#else
+#include <cmath>
+#include <cstdio>
+
+#if defined(_MSC_VER)
+#if !defined(isnan)
+#include <float.h>
+#define isnan _isnan
+#endif
+
+#if !defined(isfinite)
+#include <float.h>
+#define isfinite _finite
+#endif
+
+#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
+#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
+#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
+
+#endif //_MSC_VER
+
+#if defined(__sun) && defined(__SVR4) // Solaris
+#if !defined(isfinite)
+#include <ieeefp.h>
+#define isfinite finite
+#endif
+#endif
+
+#if defined(__hpux)
+#if !defined(isfinite)
+#if defined(__ia64) && !defined(finite)
+#define isfinite(x)                                                            \
+  ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x)))
+#endif
+#endif
+#endif
+
+#if !defined(isnan)
+// IEEE standard states that NaN values will not compare to themselves
+#define isnan(x) (x != x)
+#endif
+
+#if !defined(__APPLE__)
+#if !defined(isfinite)
+#define isfinite finite
+#endif
+#endif
+#endif
+
+#if defined(_MSC_VER)
+// Disable warning about strdup being deprecated.
+#pragma warning(disable : 4996)
+#endif
+
+namespace Json {
+
+#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
+typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
+#else
+typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
+#endif
+
+String valueToString(LargestInt value) {
+  UIntToStringBuffer buffer;
+  char* current = buffer + sizeof(buffer);
+  if (value == Value::minLargestInt) {
+    uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
+    *--current = '-';
+  } else if (value < 0) {
+    uintToString(LargestUInt(-value), current);
+    *--current = '-';
+  } else {
+    uintToString(LargestUInt(value), current);
+  }
+  assert(current >= buffer);
+  return current;
+}
+
+String valueToString(LargestUInt value) {
+  UIntToStringBuffer buffer;
+  char* current = buffer + sizeof(buffer);
+  uintToString(value, current);
+  assert(current >= buffer);
+  return current;
+}
+
+#if defined(JSON_HAS_INT64)
+
+String valueToString(Int value) { return valueToString(LargestInt(value)); }
+
+String valueToString(UInt value) { return valueToString(LargestUInt(value)); }
+
+#endif // # if defined(JSON_HAS_INT64)
+
+namespace {
+String valueToString(double value,
+                     bool useSpecialFloats,
+                     unsigned int precision,
+                     PrecisionType precisionType) {
+  // Print into the buffer. We need not request the alternative representation
+  // that always has a decimal point because JSON doesn't distinguish the
+  // concepts of reals and integers.
+  if (!isfinite(value)) {
+    static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"},
+                                           {"null", "-1e+9999", "1e+9999"}};
+    return reps[useSpecialFloats ? 0 : 1]
+               [isnan(value) ? 0 : (value < 0) ? 1 : 2];
+  }
+
+  String buffer(size_t(36), '\0');
+  while (true) {
+    int len = jsoncpp_snprintf(
+        &*buffer.begin(), buffer.size(),
+        (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f",
+        precision, value);
+    assert(len >= 0);
+    auto wouldPrint = static_cast<size_t>(len);
+    if (wouldPrint >= buffer.size()) {
+      buffer.resize(wouldPrint + 1);
+      continue;
+    }
+    buffer.resize(wouldPrint);
+    break;
+  }
+
+  buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());
+
+  // strip the zero padding from the right
+  if (precisionType == PrecisionType::decimalPlaces) {
+    buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end()), buffer.end());
+  }
+
+  // try to ensure we preserve the fact that this was given to us as a double on
+  // input
+  if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {
+    buffer += ".0";
+  }
+  return buffer;
+}
+} // namespace
+
+String valueToString(double value,
+                     unsigned int precision,
+                     PrecisionType precisionType) {
+  return valueToString(value, false, precision, precisionType);
+}
+
+String valueToString(bool value) { return value ? "true" : "false"; }
+
+static bool isAnyCharRequiredQuoting(char const* s, size_t n) {
+  assert(s || !n);
+
+  char const* const end = s + n;
+  for (char const* cur = s; cur < end; ++cur) {
+    if (*cur == '\\' || *cur == '\"' || *cur < ' ' ||
+        static_cast<unsigned char>(*cur) < 0x80)
+      return true;
+  }
+  return false;
+}
+
+static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
+  const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;
+
+  unsigned int firstByte = static_cast<unsigned char>(*s);
+
+  if (firstByte < 0x80)
+    return firstByte;
+
+  if (firstByte < 0xE0) {
+    if (e - s < 2)
+      return REPLACEMENT_CHARACTER;
+
+    unsigned int calculated =
+        ((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F);
+    s += 1;
+    // oversized encoded characters are invalid
+    return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;
+  }
+
+  if (firstByte < 0xF0) {
+    if (e - s < 3)
+      return REPLACEMENT_CHARACTER;
+
+    unsigned int calculated = ((firstByte & 0x0F) << 12) |
+                              ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) |
+                              (static_cast<unsigned int>(s[2]) & 0x3F);
+    s += 2;
+    // surrogates aren't valid codepoints itself
+    // shouldn't be UTF-8 encoded
+    if (calculated >= 0xD800 && calculated <= 0xDFFF)
+      return REPLACEMENT_CHARACTER;
+    // oversized encoded characters are invalid
+    return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;
+  }
+
+  if (firstByte < 0xF8) {
+    if (e - s < 4)
+      return REPLACEMENT_CHARACTER;
+
+    unsigned int calculated = ((firstByte & 0x07) << 18) |
+                              ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) |
+                              ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) |
+                              (static_cast<unsigned int>(s[3]) & 0x3F);
+    s += 3;
+    // oversized encoded characters are invalid
+    return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated;
+  }
+
+  return REPLACEMENT_CHARACTER;
+}
+
+static const char hex2[] = "000102030405060708090a0b0c0d0e0f"
+                           "101112131415161718191a1b1c1d1e1f"
+                           "202122232425262728292a2b2c2d2e2f"
+                           "303132333435363738393a3b3c3d3e3f"
+                           "404142434445464748494a4b4c4d4e4f"
+                           "505152535455565758595a5b5c5d5e5f"
+                           "606162636465666768696a6b6c6d6e6f"
+                           "707172737475767778797a7b7c7d7e7f"
+                           "808182838485868788898a8b8c8d8e8f"
+                           "909192939495969798999a9b9c9d9e9f"
+                           "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
+                           "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+                           "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+                           "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+                           "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+                           "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
+
+static String toHex16Bit(unsigned int x) {
+  const unsigned int hi = (x >> 8) & 0xff;
+  const unsigned int lo = x & 0xff;
+  String result(4, ' ');
+  result[0] = hex2[2 * hi];
+  result[1] = hex2[2 * hi + 1];
+  result[2] = hex2[2 * lo];
+  result[3] = hex2[2 * lo + 1];
+  return result;
+}
+
+static String valueToQuotedStringN(const char* value, unsigned length) {
+  if (value == nullptr)
+    return "";
+
+  if (!isAnyCharRequiredQuoting(value, length))
+    return String("\"") + value + "\"";
+  // We have to walk value and escape any special characters.
+  // Appending to String is not efficient, but this should be rare.
+  // (Note: forward slashes are *not* rare, but I am not escaping them.)
+  String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL
+  String result;
+  result.reserve(maxsize); // to avoid lots of mallocs
+  result += "\"";
+  char const* end = value + length;
+  for (const char* c = value; c != end; ++c) {
+    switch (*c) {
+    case '\"':
+      result += "\\\"";
+      break;
+    case '\\':
+      result += "\\\\";
+      break;
+    case '\b':
+      result += "\\b";
+      break;
+    case '\f':
+      result += "\\f";
+      break;
+    case '\n':
+      result += "\\n";
+      break;
+    case '\r':
+      result += "\\r";
+      break;
+    case '\t':
+      result += "\\t";
+      break;
+    // case '/':
+    // Even though \/ is considered a legal escape in JSON, a bare
+    // slash is also legal, so I see no reason to escape it.
+    // (I hope I am not misunderstanding something.)
+    // blep notes: actually escaping \/ may be useful in javascript to avoid </
+    // sequence.
+    // Should add a flag to allow this compatibility mode and prevent this
+    // sequence from occurring.
+    default: {
+      unsigned int cp = utf8ToCodepoint(c, end);
+      // don't escape non-control characters
+      // (short escape sequence are applied above)
+      if (cp < 0x80 && cp >= 0x20)
+        result += static_cast<char>(cp);
+      else if (cp < 0x10000) { // codepoint is in Basic Multilingual Plane
+        result += "\\u";
+        result += toHex16Bit(cp);
+      } else { // codepoint is not in Basic Multilingual Plane
+               // convert to surrogate pair first
+        cp -= 0x10000;
+        result += "\\u";
+        result += toHex16Bit((cp >> 10) + 0xD800);
+        result += "\\u";
+        result += toHex16Bit((cp & 0x3FF) + 0xDC00);
+      }
+    } break;
+    }
+  }
+  result += "\"";
+  return result;
+}
+
+String valueToQuotedString(const char* value) {
+  return valueToQuotedStringN(value, static_cast<unsigned int>(strlen(value)));
+}
+
+// Class Writer
+// //////////////////////////////////////////////////////////////////
+Writer::~Writer() = default;
+
+// Class FastWriter
+// //////////////////////////////////////////////////////////////////
+
+FastWriter::FastWriter()
+
+    = default;
+
+void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
+
+void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
+
+void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
+
+String FastWriter::write(const Value& root) {
+  document_.clear();
+  writeValue(root);
+  if (!omitEndingLineFeed_)
+    document_ += '\n';
+  return document_;
+}
+
+void FastWriter::writeValue(const Value& value) {
+  switch (value.type()) {
+  case nullValue:
+    if (!dropNullPlaceholders_)
+      document_ += "null";
+    break;
+  case intValue:
+    document_ += valueToString(value.asLargestInt());
+    break;
+  case uintValue:
+    document_ += valueToString(value.asLargestUInt());
+    break;
+  case realValue:
+    document_ += valueToString(value.asDouble());
+    break;
+  case stringValue: {
+    // Is NULL possible for value.string_? No.
+    char const* str;
+    char const* end;
+    bool ok = value.getString(&str, &end);
+    if (ok)
+      document_ += valueToQuotedStringN(str, static_cast<unsigned>(end - str));
+    break;
+  }
+  case booleanValue:
+    document_ += valueToString(value.asBool());
+    break;
+  case arrayValue: {
+    document_ += '[';
+    ArrayIndex size = value.size();
+    for (ArrayIndex index = 0; index < size; ++index) {
+      if (index > 0)
+        document_ += ',';
+      writeValue(value[index]);
+    }
+    document_ += ']';
+  } break;
+  case objectValue: {
+    Value::Members members(value.getMemberNames());
+    document_ += '{';
+    for (auto it = members.begin(); it != members.end(); ++it) {
+      const String& name = *it;
+      if (it != members.begin())
+        document_ += ',';
+      document_ += valueToQuotedStringN(name.data(),
+                                        static_cast<unsigned>(name.length()));
+      document_ += yamlCompatibilityEnabled_ ? ": " : ":";
+      writeValue(value[name]);
+    }
+    document_ += '}';
+  } break;
+  }
+}
+
+// Class StyledWriter
+// //////////////////////////////////////////////////////////////////
+
+StyledWriter::StyledWriter() = default;
+
+String StyledWriter::write(const Value& root) {
+  document_.clear();
+  addChildValues_ = false;
+  indentString_.clear();
+  writeCommentBeforeValue(root);
+  writeValue(root);
+  writeCommentAfterValueOnSameLine(root);
+  document_ += '\n';
+  return document_;
+}
+
+void StyledWriter::writeValue(const Value& value) {
+  switch (value.type()) {
+  case nullValue:
+    pushValue("null");
+    break;
+  case intValue:
+    pushValue(valueToString(value.asLargestInt()));
+    break;
+  case uintValue:
+    pushValue(valueToString(value.asLargestUInt()));
+    break;
+  case realValue:
+    pushValue(valueToString(value.asDouble()));
+    break;
+  case stringValue: {
+    // Is NULL possible for value.string_? No.
+    char const* str;
+    char const* end;
+    bool ok = value.getString(&str, &end);
+    if (ok)
+      pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
+    else
+      pushValue("");
+    break;
+  }
+  case booleanValue:
+    pushValue(valueToString(value.asBool()));
+    break;
+  case arrayValue:
+    writeArrayValue(value);
+    break;
+  case objectValue: {
+    Value::Members members(value.getMemberNames());
+    if (members.empty())
+      pushValue("{}");
+    else {
+      writeWithIndent("{");
+      indent();
+      auto it = members.begin();
+      for (;;) {
+        const String& name = *it;
+        const Value& childValue = value[name];
+        writeCommentBeforeValue(childValue);
+        writeWithIndent(valueToQuotedString(name.c_str()));
+        document_ += " : ";
+        writeValue(childValue);
+        if (++it == members.end()) {
+          writeCommentAfterValueOnSameLine(childValue);
+          break;
+        }
+        document_ += ',';
+        writeCommentAfterValueOnSameLine(childValue);
+      }
+      unindent();
+      writeWithIndent("}");
+    }
+  } break;
+  }
+}
+
+void StyledWriter::writeArrayValue(const Value& value) {
+  unsigned size = value.size();
+  if (size == 0)
+    pushValue("[]");
+  else {
+    bool isArrayMultiLine = isMultilineArray(value);
+    if (isArrayMultiLine) {
+      writeWithIndent("[");
+      indent();
+      bool hasChildValue = !childValues_.empty();
+      unsigned index = 0;
+      for (;;) {
+        const Value& childValue = value[index];
+        writeCommentBeforeValue(childValue);
+        if (hasChildValue)
+          writeWithIndent(childValues_[index]);
+        else {
+          writeIndent();
+          writeValue(childValue);
+        }
+        if (++index == size) {
+          writeCommentAfterValueOnSameLine(childValue);
+          break;
+        }
+        document_ += ',';
+        writeCommentAfterValueOnSameLine(childValue);
+      }
+      unindent();
+      writeWithIndent("]");
+    } else // output on a single line
+    {
+      assert(childValues_.size() == size);
+      document_ += "[ ";
+      for (unsigned index = 0; index < size; ++index) {
+        if (index > 0)
+          document_ += ", ";
+        document_ += childValues_[index];
+      }
+      document_ += " ]";
+    }
+  }
+}
+
+bool StyledWriter::isMultilineArray(const Value& value) {
+  ArrayIndex const size = value.size();
+  bool isMultiLine = size * 3 >= rightMargin_;
+  childValues_.clear();
+  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
+    const Value& childValue = value[index];
+    isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
+                   !childValue.empty());
+  }
+  if (!isMultiLine) // check if line length > max line length
+  {
+    childValues_.reserve(size);
+    addChildValues_ = true;
+    ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
+    for (ArrayIndex index = 0; index < size; ++index) {
+      if (hasCommentForValue(value[index])) {
+        isMultiLine = true;
+      }
+      writeValue(value[index]);
+      lineLength += static_cast<ArrayIndex>(childValues_[index].length());
+    }
+    addChildValues_ = false;
+    isMultiLine = isMultiLine || lineLength >= rightMargin_;
+  }
+  return isMultiLine;
+}
+
+void StyledWriter::pushValue(const String& value) {
+  if (addChildValues_)
+    childValues_.push_back(value);
+  else
+    document_ += value;
+}
+
+void StyledWriter::writeIndent() {
+  if (!document_.empty()) {
+    char last = document_[document_.length() - 1];
+    if (last == ' ') // already indented
+      return;
+    if (last != '\n') // Comments may add new-line
+      document_ += '\n';
+  }
+  document_ += indentString_;
+}
+
+void StyledWriter::writeWithIndent(const String& value) {
+  writeIndent();
+  document_ += value;
+}
+
+void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); }
+
+void StyledWriter::unindent() {
+  assert(indentString_.size() >= indentSize_);
+  indentString_.resize(indentString_.size() - indentSize_);
+}
+
+void StyledWriter::writeCommentBeforeValue(const Value& root) {
+  if (!root.hasComment(commentBefore))
+    return;
+
+  document_ += '\n';
+  writeIndent();
+  const String& comment = root.getComment(commentBefore);
+  String::const_iterator iter = comment.begin();
+  while (iter != comment.end()) {
+    document_ += *iter;
+    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
+      writeIndent();
+    ++iter;
+  }
+
+  // Comments are stripped of trailing newlines, so add one here
+  document_ += '\n';
+}
+
+void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
+  if (root.hasComment(commentAfterOnSameLine))
+    document_ += " " + root.getComment(commentAfterOnSameLine);
+
+  if (root.hasComment(commentAfter)) {
+    document_ += '\n';
+    document_ += root.getComment(commentAfter);
+    document_ += '\n';
+  }
+}
+
+bool StyledWriter::hasCommentForValue(const Value& value) {
+  return value.hasComment(commentBefore) ||
+         value.hasComment(commentAfterOnSameLine) ||
+         value.hasComment(commentAfter);
+}
+
+// Class StyledStreamWriter
+// //////////////////////////////////////////////////////////////////
+
+StyledStreamWriter::StyledStreamWriter(String indentation)
+    : document_(nullptr), indentation_(std::move(indentation)),
+      addChildValues_(), indented_(false) {}
+
+void StyledStreamWriter::write(OStream& out, const Value& root) {
+  document_ = &out;
+  addChildValues_ = false;
+  indentString_.clear();
+  indented_ = true;
+  writeCommentBeforeValue(root);
+  if (!indented_)
+    writeIndent();
+  indented_ = true;
+  writeValue(root);
+  writeCommentAfterValueOnSameLine(root);
+  *document_ << "\n";
+  document_ = nullptr; // Forget the stream, for safety.
+}
+
+void StyledStreamWriter::writeValue(const Value& value) {
+  switch (value.type()) {
+  case nullValue:
+    pushValue("null");
+    break;
+  case intValue:
+    pushValue(valueToString(value.asLargestInt()));
+    break;
+  case uintValue:
+    pushValue(valueToString(value.asLargestUInt()));
+    break;
+  case realValue:
+    pushValue(valueToString(value.asDouble()));
+    break;
+  case stringValue: {
+    // Is NULL possible for value.string_? No.
+    char const* str;
+    char const* end;
+    bool ok = value.getString(&str, &end);
+    if (ok)
+      pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
+    else
+      pushValue("");
+    break;
+  }
+  case booleanValue:
+    pushValue(valueToString(value.asBool()));
+    break;
+  case arrayValue:
+    writeArrayValue(value);
+    break;
+  case objectValue: {
+    Value::Members members(value.getMemberNames());
+    if (members.empty())
+      pushValue("{}");
+    else {
+      writeWithIndent("{");
+      indent();
+      auto it = members.begin();
+      for (;;) {
+        const String& name = *it;
+        const Value& childValue = value[name];
+        writeCommentBeforeValue(childValue);
+        writeWithIndent(valueToQuotedString(name.c_str()));
+        *document_ << " : ";
+        writeValue(childValue);
+        if (++it == members.end()) {
+          writeCommentAfterValueOnSameLine(childValue);
+          break;
+        }
+        *document_ << ",";
+        writeCommentAfterValueOnSameLine(childValue);
+      }
+      unindent();
+      writeWithIndent("}");
+    }
+  } break;
+  }
+}
+
+void StyledStreamWriter::writeArrayValue(const Value& value) {
+  unsigned size = value.size();
+  if (size == 0)
+    pushValue("[]");
+  else {
+    bool isArrayMultiLine = isMultilineArray(value);
+    if (isArrayMultiLine) {
+      writeWithIndent("[");
+      indent();
+      bool hasChildValue = !childValues_.empty();
+      unsigned index = 0;
+      for (;;) {
+        const Value& childValue = value[index];
+        writeCommentBeforeValue(childValue);
+        if (hasChildValue)
+          writeWithIndent(childValues_[index]);
+        else {
+          if (!indented_)
+            writeIndent();
+          indented_ = true;
+          writeValue(childValue);
+          indented_ = false;
+        }
+        if (++index == size) {
+          writeCommentAfterValueOnSameLine(childValue);
+          break;
+        }
+        *document_ << ",";
+        writeCommentAfterValueOnSameLine(childValue);
+      }
+      unindent();
+      writeWithIndent("]");
+    } else // output on a single line
+    {
+      assert(childValues_.size() == size);
+      *document_ << "[ ";
+      for (unsigned index = 0; index < size; ++index) {
+        if (index > 0)
+          *document_ << ", ";
+        *document_ << childValues_[index];
+      }
+      *document_ << " ]";
+    }
+  }
+}
+
+bool StyledStreamWriter::isMultilineArray(const Value& value) {
+  ArrayIndex const size = value.size();
+  bool isMultiLine = size * 3 >= rightMargin_;
+  childValues_.clear();
+  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
+    const Value& childValue = value[index];
+    isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
+                   !childValue.empty());
+  }
+  if (!isMultiLine) // check if line length > max line length
+  {
+    childValues_.reserve(size);
+    addChildValues_ = true;
+    ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
+    for (ArrayIndex index = 0; index < size; ++index) {
+      if (hasCommentForValue(value[index])) {
+        isMultiLine = true;
+      }
+      writeValue(value[index]);
+      lineLength += static_cast<ArrayIndex>(childValues_[index].length());
+    }
+    addChildValues_ = false;
+    isMultiLine = isMultiLine || lineLength >= rightMargin_;
+  }
+  return isMultiLine;
+}
+
+void StyledStreamWriter::pushValue(const String& value) {
+  if (addChildValues_)
+    childValues_.push_back(value);
+  else
+    *document_ << value;
+}
+
+void StyledStreamWriter::writeIndent() {
+  // blep intended this to look at the so-far-written string
+  // to determine whether we are already indented, but
+  // with a stream we cannot do that. So we rely on some saved state.
+  // The caller checks indented_.
+  *document_ << '\n' << indentString_;
+}
+
+void StyledStreamWriter::writeWithIndent(const String& value) {
+  if (!indented_)
+    writeIndent();
+  *document_ << value;
+  indented_ = false;
+}
+
+void StyledStreamWriter::indent() { indentString_ += indentation_; }
+
+void StyledStreamWriter::unindent() {
+  assert(indentString_.size() >= indentation_.size());
+  indentString_.resize(indentString_.size() - indentation_.size());
+}
+
+void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
+  if (!root.hasComment(commentBefore))
+    return;
+
+  if (!indented_)
+    writeIndent();
+  const String& comment = root.getComment(commentBefore);
+  String::const_iterator iter = comment.begin();
+  while (iter != comment.end()) {
+    *document_ << *iter;
+    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
+      // writeIndent();  // would include newline
+      *document_ << indentString_;
+    ++iter;
+  }
+  indented_ = false;
+}
+
+void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
+  if (root.hasComment(commentAfterOnSameLine))
+    *document_ << ' ' << root.getComment(commentAfterOnSameLine);
+
+  if (root.hasComment(commentAfter)) {
+    writeIndent();
+    *document_ << root.getComment(commentAfter);
+  }
+  indented_ = false;
+}
+
+bool StyledStreamWriter::hasCommentForValue(const Value& value) {
+  return value.hasComment(commentBefore) ||
+         value.hasComment(commentAfterOnSameLine) ||
+         value.hasComment(commentAfter);
+}
+
+//////////////////////////
+// BuiltStyledStreamWriter
+
+/// Scoped enums are not available until C++11.
+struct CommentStyle {
+  /// Decide whether to write comments.
+  enum Enum {
+    None, ///< Drop all comments.
+    Most, ///< Recover odd behavior of previous versions (not implemented yet).
+    All   ///< Keep all comments.
+  };
+};
+
+struct BuiltStyledStreamWriter : public StreamWriter {
+  BuiltStyledStreamWriter(String indentation,
+                          CommentStyle::Enum cs,
+                          String colonSymbol,
+                          String nullSymbol,
+                          String endingLineFeedSymbol,
+                          bool useSpecialFloats,
+                          unsigned int precision,
+                          PrecisionType precisionType);
+  int write(Value const& root, OStream* sout) override;
+
+private:
+  void writeValue(Value const& value);
+  void writeArrayValue(Value const& value);
+  bool isMultilineArray(Value const& value);
+  void pushValue(String const& value);
+  void writeIndent();
+  void writeWithIndent(String const& value);
+  void indent();
+  void unindent();
+  void writeCommentBeforeValue(Value const& root);
+  void writeCommentAfterValueOnSameLine(Value const& root);
+  static bool hasCommentForValue(const Value& value);
+
+  typedef std::vector<String> ChildValues;
+
+  ChildValues childValues_;
+  String indentString_;
+  unsigned int rightMargin_;
+  String indentation_;
+  CommentStyle::Enum cs_;
+  String colonSymbol_;
+  String nullSymbol_;
+  String endingLineFeedSymbol_;
+  bool addChildValues_ : 1;
+  bool indented_ : 1;
+  bool useSpecialFloats_ : 1;
+  unsigned int precision_;
+  PrecisionType precisionType_;
+};
+BuiltStyledStreamWriter::BuiltStyledStreamWriter(String indentation,
+                                                 CommentStyle::Enum cs,
+                                                 String colonSymbol,
+                                                 String nullSymbol,
+                                                 String endingLineFeedSymbol,
+                                                 bool useSpecialFloats,
+                                                 unsigned int precision,
+                                                 PrecisionType precisionType)
+    : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),
+      colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),
+      endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),
+      addChildValues_(false), indented_(false),
+      useSpecialFloats_(useSpecialFloats), precision_(precision),
+      precisionType_(precisionType) {}
+int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {
+  sout_ = sout;
+  addChildValues_ = false;
+  indented_ = true;
+  indentString_.clear();
+  writeCommentBeforeValue(root);
+  if (!indented_)
+    writeIndent();
+  indented_ = true;
+  writeValue(root);
+  writeCommentAfterValueOnSameLine(root);
+  *sout_ << endingLineFeedSymbol_;
+  sout_ = nullptr;
+  return 0;
+}
+void BuiltStyledStreamWriter::writeValue(Value const& value) {
+  switch (value.type()) {
+  case nullValue:
+    pushValue(nullSymbol_);
+    break;
+  case intValue:
+    pushValue(valueToString(value.asLargestInt()));
+    break;
+  case uintValue:
+    pushValue(valueToString(value.asLargestUInt()));
+    break;
+  case realValue:
+    pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_,
+                            precisionType_));
+    break;
+  case stringValue: {
+    // Is NULL is possible for value.string_? No.
+    char const* str;
+    char const* end;
+    bool ok = value.getString(&str, &end);
+    if (ok)
+      pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
+    else
+      pushValue("");
+    break;
+  }
+  case booleanValue:
+    pushValue(valueToString(value.asBool()));
+    break;
+  case arrayValue:
+    writeArrayValue(value);
+    break;
+  case objectValue: {
+    Value::Members members(value.getMemberNames());
+    if (members.empty())
+      pushValue("{}");
+    else {
+      writeWithIndent("{");
+      indent();
+      auto it = members.begin();
+      for (;;) {
+        String const& name = *it;
+        Value const& childValue = value[name];
+        writeCommentBeforeValue(childValue);
+        writeWithIndent(valueToQuotedStringN(
+            name.data(), static_cast<unsigned>(name.length())));
+        *sout_ << colonSymbol_;
+        writeValue(childValue);
+        if (++it == members.end()) {
+          writeCommentAfterValueOnSameLine(childValue);
+          break;
+        }
+        *sout_ << ",";
+        writeCommentAfterValueOnSameLine(childValue);
+      }
+      unindent();
+      writeWithIndent("}");
+    }
+  } break;
+  }
+}
+
+void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
+  unsigned size = value.size();
+  if (size == 0)
+    pushValue("[]");
+  else {
+    bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value);
+    if (isMultiLine) {
+      writeWithIndent("[");
+      indent();
+      bool hasChildValue = !childValues_.empty();
+      unsigned index = 0;
+      for (;;) {
+        Value const& childValue = value[index];
+        writeCommentBeforeValue(childValue);
+        if (hasChildValue)
+          writeWithIndent(childValues_[index]);
+        else {
+          if (!indented_)
+            writeIndent();
+          indented_ = true;
+          writeValue(childValue);
+          indented_ = false;
+        }
+        if (++index == size) {
+          writeCommentAfterValueOnSameLine(childValue);
+          break;
+        }
+        *sout_ << ",";
+        writeCommentAfterValueOnSameLine(childValue);
+      }
+      unindent();
+      writeWithIndent("]");
+    } else // output on a single line
+    {
+      assert(childValues_.size() == size);
+      *sout_ << "[";
+      if (!indentation_.empty())
+        *sout_ << " ";
+      for (unsigned index = 0; index < size; ++index) {
+        if (index > 0)
+          *sout_ << ((!indentation_.empty()) ? ", " : ",");
+        *sout_ << childValues_[index];
+      }
+      if (!indentation_.empty())
+        *sout_ << " ";
+      *sout_ << "]";
+    }
+  }
+}
+
+bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) {
+  ArrayIndex const size = value.size();
+  bool isMultiLine = size * 3 >= rightMargin_;
+  childValues_.clear();
+  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
+    Value const& childValue = value[index];
+    isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
+                   !childValue.empty());
+  }
+  if (!isMultiLine) // check if line length > max line length
+  {
+    childValues_.reserve(size);
+    addChildValues_ = true;
+    ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
+    for (ArrayIndex index = 0; index < size; ++index) {
+      if (hasCommentForValue(value[index])) {
+        isMultiLine = true;
+      }
+      writeValue(value[index]);
+      lineLength += static_cast<ArrayIndex>(childValues_[index].length());
+    }
+    addChildValues_ = false;
+    isMultiLine = isMultiLine || lineLength >= rightMargin_;
+  }
+  return isMultiLine;
+}
+
+void BuiltStyledStreamWriter::pushValue(String const& value) {
+  if (addChildValues_)
+    childValues_.push_back(value);
+  else
+    *sout_ << value;
+}
+
+void BuiltStyledStreamWriter::writeIndent() {
+  // blep intended this to look at the so-far-written string
+  // to determine whether we are already indented, but
+  // with a stream we cannot do that. So we rely on some saved state.
+  // The caller checks indented_.
+
+  if (!indentation_.empty()) {
+    // In this case, drop newlines too.
+    *sout_ << '\n' << indentString_;
+  }
+}
+
+void BuiltStyledStreamWriter::writeWithIndent(String const& value) {
+  if (!indented_)
+    writeIndent();
+  *sout_ << value;
+  indented_ = false;
+}
+
+void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
+
+void BuiltStyledStreamWriter::unindent() {
+  assert(indentString_.size() >= indentation_.size());
+  indentString_.resize(indentString_.size() - indentation_.size());
+}
+
+void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
+  if (cs_ == CommentStyle::None)
+    return;
+  if (!root.hasComment(commentBefore))
+    return;
+
+  if (!indented_)
+    writeIndent();
+  const String& comment = root.getComment(commentBefore);
+  String::const_iterator iter = comment.begin();
+  while (iter != comment.end()) {
+    *sout_ << *iter;
+    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
+      // writeIndent();  // would write extra newline
+      *sout_ << indentString_;
+    ++iter;
+  }
+  indented_ = false;
+}
+
+void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(
+    Value const& root) {
+  if (cs_ == CommentStyle::None)
+    return;
+  if (root.hasComment(commentAfterOnSameLine))
+    *sout_ << " " + root.getComment(commentAfterOnSameLine);
+
+  if (root.hasComment(commentAfter)) {
+    writeIndent();
+    *sout_ << root.getComment(commentAfter);
+  }
+}
+
+// static
+bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
+  return value.hasComment(commentBefore) ||
+         value.hasComment(commentAfterOnSameLine) ||
+         value.hasComment(commentAfter);
+}
+
+///////////////
+// StreamWriter
+
+StreamWriter::StreamWriter() : sout_(nullptr) {}
+StreamWriter::~StreamWriter() = default;
+StreamWriter::Factory::~Factory() = default;
+StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); }
+StreamWriterBuilder::~StreamWriterBuilder() = default;
+StreamWriter* StreamWriterBuilder::newStreamWriter() const {
+  String indentation = settings_["indentation"].asString();
+  String cs_str = settings_["commentStyle"].asString();
+  String pt_str = settings_["precisionType"].asString();
+  bool eyc = settings_["enableYAMLCompatibility"].asBool();
+  bool dnp = settings_["dropNullPlaceholders"].asBool();
+  bool usf = settings_["useSpecialFloats"].asBool();
+  unsigned int pre = settings_["precision"].asUInt();
+  CommentStyle::Enum cs = CommentStyle::All;
+  if (cs_str == "All") {
+    cs = CommentStyle::All;
+  } else if (cs_str == "None") {
+    cs = CommentStyle::None;
+  } else {
+    throwRuntimeError("commentStyle must be 'All' or 'None'");
+  }
+  PrecisionType precisionType(significantDigits);
+  if (pt_str == "significant") {
+    precisionType = PrecisionType::significantDigits;
+  } else if (pt_str == "decimal") {
+    precisionType = PrecisionType::decimalPlaces;
+  } else {
+    throwRuntimeError("precisionType must be 'significant' or 'decimal'");
+  }
+  String colonSymbol = " : ";
+  if (eyc) {
+    colonSymbol = ": ";
+  } else if (indentation.empty()) {
+    colonSymbol = ":";
+  }
+  String nullSymbol = "null";
+  if (dnp) {
+    nullSymbol.clear();
+  }
+  if (pre > 17)
+    pre = 17;
+  String endingLineFeedSymbol;
+  return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,
+                                     endingLineFeedSymbol, usf, pre,
+                                     precisionType);
+}
+static void getValidWriterKeys(std::set<String>* valid_keys) {
+  valid_keys->clear();
+  valid_keys->insert("indentation");
+  valid_keys->insert("commentStyle");
+  valid_keys->insert("enableYAMLCompatibility");
+  valid_keys->insert("dropNullPlaceholders");
+  valid_keys->insert("useSpecialFloats");
+  valid_keys->insert("precision");
+  valid_keys->insert("precisionType");
+}
+bool StreamWriterBuilder::validate(Json::Value* invalid) const {
+  Json::Value my_invalid;
+  if (!invalid)
+    invalid = &my_invalid; // so we do not need to test for NULL
+  Json::Value& inv = *invalid;
+  std::set<String> valid_keys;
+  getValidWriterKeys(&valid_keys);
+  Value::Members keys = settings_.getMemberNames();
+  size_t n = keys.size();
+  for (size_t i = 0; i < n; ++i) {
+    String const& key = keys[i];
+    if (valid_keys.find(key) == valid_keys.end()) {
+      inv[key] = settings_[key];
+    }
+  }
+  return inv.empty();
+}
+Value& StreamWriterBuilder::operator[](const String& key) {
+  return settings_[key];
+}
+// static
+void StreamWriterBuilder::setDefaults(Json::Value* settings) {
+  //! [StreamWriterBuilderDefaults]
+  (*settings)["commentStyle"] = "All";
+  (*settings)["indentation"] = "\t";
+  (*settings)["enableYAMLCompatibility"] = false;
+  (*settings)["dropNullPlaceholders"] = false;
+  (*settings)["useSpecialFloats"] = false;
+  (*settings)["precision"] = 17;
+  (*settings)["precisionType"] = "significant";
+  //! [StreamWriterBuilderDefaults]
+}
+
+String writeString(StreamWriter::Factory const& factory, Value const& root) {
+  OStringStream sout;
+  StreamWriterPtr const writer(factory.newStreamWriter());
+  writer->write(root, &sout);
+  return sout.str();
+}
+
+OStream& operator<<(OStream& sout, Value const& root) {
+  StreamWriterBuilder builder;
+  StreamWriterPtr const writer(builder.newStreamWriter());
+  writer->write(root, &sout);
+  return sout;
+}
+
+} // namespace Json
+
+// //////////////////////////////////////////////////////////////////////
+// End of content of file: src/lib_json/json_writer.cpp
+// //////////////////////////////////////////////////////////////////////
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/main.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,186 @@
+#include <iostream>
+#include <sstream>
+#include <emscripten/emscripten.h>
+#include "TestStoneCodeGen_generated.hpp"
+
+using std::stringstream;
+
+int main()
+{
+    std::cout << "Hello world from testWasmIntegrated! (this is sent from C++)" << std::endl;
+    try
+    {
+    const char* jsonData = R"bgo({"definition":
+    {
+      "val" : [ "berk", 42 ],
+      "zozo" : { "23": "zloutch", "lalala": 42}
+    }
+    })bgo";
+    std::string strValue(jsonData);
+    
+    Json::Value readValue;
+
+    Json::CharReaderBuilder builder;
+    Json::CharReader* reader = builder.newCharReader();
+
+    StoneSmartPtr<Json::CharReader> ptr(reader);
+
+    std::string errors;
+
+    bool ok = reader->parse(
+      strValue.c_str(),
+      strValue.c_str() + strValue.size(),
+      &readValue,
+      &errors
+    );
+    if (!ok)
+    {
+      std::stringstream ss;
+      ss << "Jsoncpp parsing error: " << errors;
+      throw std::runtime_error(ss.str());
+    }
+    std::cout << "Json parsing OK" << std::endl;
+    std::cout << readValue  << std::endl;
+    }
+    catch(std::exception& e)
+    {
+      std::cout << "Json parsing THROW" << std::endl;
+      std::cout << "e.what() = " << e.what() << std::endl;
+    }
+}
+
+extern "C" void SendMessageFromCppJS(const char* message);
+extern "C" void SendFreeTextFromCppJS(const char* message);
+
+#define HANDLE_MESSAGE(Type,value) \
+  stringstream ss; \
+  ss << "Received an instance of:\n" #Type "\n. Here's the dump:\n"; \
+  TestStoneCodeGen::StoneDumpValue(ss, value, 0); \
+  SendFreeTextFromCppJS(ss.str().c_str()); \
+  return true;
+
+#define ECHO_MESSAGE(Type,value) \
+  stringstream ss; \
+  ss << "Received an instance of:\n" #Type "\n. Here's the dump:\n"; \
+  TestStoneCodeGen::StoneDumpValue(ss, value, 0); \
+  SendFreeTextFromCppJS(ss.str().c_str()); \
+  std::string serializedInCpp = StoneSerialize(value); \
+  SendMessageFromCppJS(serializedInCpp.c_str()); \
+  return true;
+
+class MyHandler : public TestStoneCodeGen::IHandler
+{
+  public:
+    virtual bool Handle(const TestStoneCodeGen::A& value) override
+    {
+      HANDLE_MESSAGE(TestStoneCodeGen::A,value)
+    }
+    virtual bool Handle(const TestStoneCodeGen::B& value) override
+    {
+      HANDLE_MESSAGE(TestStoneCodeGen::B,value)
+    }
+
+    virtual bool Handle(const TestStoneCodeGen::Message1& value) override
+    {
+      HANDLE_MESSAGE(TestStoneCodeGen::Message1,value)
+    }
+
+    virtual bool Handle(const TestStoneCodeGen::Message2& value) override
+    {
+      HANDLE_MESSAGE(TestStoneCodeGen::Message2,value)
+    }
+
+    virtual bool Handle(const TestStoneCodeGen::C& value) override
+    {
+      HANDLE_MESSAGE(TestStoneCodeGen::C,value)
+    }
+};
+
+class MyEchoHandler : public TestStoneCodeGen::IHandler
+{
+  public:
+    virtual bool Handle(const TestStoneCodeGen::A& value) override
+    {
+      ECHO_MESSAGE(TestStoneCodeGen::A,value)
+    }
+    virtual bool Handle(const TestStoneCodeGen::B& value) override
+    {
+      ECHO_MESSAGE(TestStoneCodeGen::B,value)
+    }
+
+    virtual bool Handle(const TestStoneCodeGen::Message1& value) override
+    {
+      ECHO_MESSAGE(TestStoneCodeGen::Message1,value)
+    }
+
+    virtual bool Handle(const TestStoneCodeGen::Message2& value) override
+    {
+      ECHO_MESSAGE(TestStoneCodeGen::Message2,value)
+    }
+
+    virtual bool Handle(const TestStoneCodeGen::C& value) override
+    {
+      ECHO_MESSAGE(TestStoneCodeGen::C,value)
+    }
+};
+
+extern "C" void EMSCRIPTEN_KEEPALIVE SendMessageToCpp(const char* message)
+{
+    MyHandler handler;
+    try
+    {
+      bool handled = TestStoneCodeGen::StoneDispatchToHandler(message,&handler);
+      if(!handled)
+      {
+        SendFreeTextFromCppJS("This message is valid JSON, but was not handled!");  
+      }
+    }
+    catch(std::exception& e)
+    {
+      stringstream ss;
+      ss << "Error while parsing message: " << e.what() << "\n";
+      SendFreeTextFromCppJS(ss.str().c_str());  
+    }
+}
+
+extern "C" void EMSCRIPTEN_KEEPALIVE SendMessageToCppForEcho(const char* message)
+{
+    MyEchoHandler echoHandler;
+    try
+    {
+      bool handled = TestStoneCodeGen::StoneDispatchToHandler(message,&echoHandler);
+      if(!handled)
+      {
+        SendFreeTextFromCppJS("This message is valid JSON, but was not handled by the echo handler!");  
+      }
+    }
+    catch(std::exception& e)
+    {
+      stringstream ss;
+      ss << "Error while parsing message: " << e.what() << "\n";
+      SendFreeTextFromCppJS(ss.str().c_str());  
+    }
+}
+
+void EMSCRIPTEN_KEEPALIVE StartWasmApplication(const char* baseUri)
+{
+    printf("Hello! (this is sent from C++)\n");
+
+//     // recreate a command line from uri arguments and parse it
+//     boost::program_options::variables_map parameters;
+//     boost::program_options::options_description options;
+//     application->DeclareStartupOptions(options);
+//     startupParametersBuilder.GetStartupParameters(parameters, options);
+
+//     context.reset(new OrthancStone::StoneApplicationContext(broker));
+//     context->SetOrthancBaseUrl(baseUri);
+//     printf("Base URL to Orthanc API: [%s]\n", baseUri);
+//     context->SetWebService(OrthancStone::WasmWebService::GetInstance());
+//     context->SetDelayedCallExecutor(OrthancStone::WasmDelayedCallExecutor::GetInstance());
+//     application->Initialize(context.get(), statusBar_, parameters);
+//     application->InitializeWasm();
+
+// //    viewport->SetSize(width_, height_);
+//     printf("StartWasmApplication - completed\n");
+    SendFreeTextFromCppJS("Hello world from C++!");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/serve.py	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# tested on python 3.4 ,python of lower version  has different module organization.
+# from https://gist.github.com/HaiyangXu/ec88cbdce3cdbac7b8d5
+import http.server
+from http.server import HTTPServer, BaseHTTPRequestHandler
+import socketserver
+
+PORT = 8080
+
+Handler = http.server.SimpleHTTPRequestHandler
+
+Handler.extensions_map = {
+  '.manifest': 'text/cache-manifest',
+  '.html': 'text/html',
+  '.png': 'image/png',
+  '.jpg': 'image/jpg',
+  '.svg': 'image/svg+xml',
+  '.wasm': 'application/wasm',
+  '.css': 'text/css',
+  '.js': 'application/x-javascript',
+  '': 'application/octet-stream',  # Default
+}
+
+httpd = socketserver.TCPServer(("", PORT), Handler)
+
+print("serving at port", PORT)
+httpd.serve_forever()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/styles.css	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,66 @@
+.TestWasm-grid-container {
+    display: grid;
+    grid-template-columns: 0.55fr 0.55fr 0.55fr 0.55fr 0.6fr 1.1fr 1.1fr;
+    grid-template-rows: 1.1fr 0.9fr 0.2fr 0.3fr 0.1fr 0.3fr 0.1fr;
+    grid-template-areas: 
+        "SerializedInput SerializedInput SerializedInput SerializedInput ButtonContainer CppOutput CppOutput" 
+        "SerializedInput SerializedInput SerializedInput SerializedInput ButtonContainer CppOutput CppOutput" 
+        ". . . . . . ." 
+        "Test1 Test2 Test3 Test4 . . ." 
+        ". . . . . . ." 
+        "Test5 Test6 Test7 Test8 . . ."
+        "TestTsCppTs . . . . . ." 
+        ". . . . . . ." 
+        ;
+    height: 480px;
+  }
+
+  .TestWasm-ButtonContainer {
+    display: grid;
+    grid-template-columns: 0.2fr 0.8fr 0.2fr;
+    grid-template-rows: 0.2fr 0.5fr 0.2fr 0.5fr 0.2fr 0.5fr 0.2fr;
+    grid-template-areas: 
+        ". . ."
+        ". TriggerButton ." 
+        ". . ."
+        ". ClearButton ." 
+        ". . ."
+        ". ShowSchemaButton ." 
+        ". . ."
+        ;
+  }
+
+  .TestWasm-TriggerButton { grid-area: TriggerButton; }
+  
+  .TestWasm-ClearButton { grid-area: ClearButton; }
+  
+  .TestWasm-ShowSchemaButton { grid-area: ShowSchemaButton; }
+
+
+.TestWasm-SerializedInput { grid-area: SerializedInput; }
+
+.TestWasm-CppOutput { grid-area: CppOutput; }
+
+.TestWasm-ButtonContainer { grid-area: ButtonContainer; }
+
+.TestWasm-Test1 { grid-area: Test1; }
+
+.TestWasm-Test2 { grid-area: Test2; }
+
+.TestWasm-Test3 { grid-area: Test3; }
+
+.TestWasm-Test4 { grid-area: Test4; }
+
+.TestWasm-Test5 { grid-area: Test5; }
+
+.TestWasm-Test6 { grid-area: Test6; }
+
+.TestWasm-Test7 { grid-area: Test7; }
+
+.TestWasm-Test8 { grid-area: Test8; }
+
+.TestWasm-ts-cpp-ts { grid-area: TestTsCppTs; }
+
+.TestWasm-button {
+    width:80px;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.html	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,82 @@
+<!doctype html>
+
+<html lang="us">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <title>Javascript to WASM message passing</title>
+    <link href="styles.css" rel="stylesheet" />
+    <!-- <link href="styles2.css" rel="stylesheet" /> -->
+    <!-- 
+    <link href="testwasm_bootstrap.css" rel="stylesheet" /> 
+    -->
+    <body>
+    <div class="TestWasm-grid-container">
+        <textarea id="TestWasm-SerializedInput" class="TestWasm-SerializedInput">Serialized data should be put here.</textarea>
+        <textarea id="TestWasm-CppOutput" class="TestWasm-CppOutput">Free text and messages from C++ will appear here.</textarea>
+        <div class="TestWasm-ButtonContainer">
+          <div class="TestWasm-TriggerButton">
+            <button class="TestWasm-button" tool-selector="Trigger">Send message</button>
+          </div>
+          <div class="TestWasm-ClearButton">
+            <button class="TestWasm-button" tool-selector="Clear">Clear</button>
+          </div>
+          <div class="TestWasm-ShowSchemaButton">
+            <button class="TestWasm-button" tool-selector="ShowSchema">Show schema</button>
+          </div>
+           <!-- <button class="TestWasm-button TestWasm-hvcenter" tool-selector="Trigger">Send message</button>
+            <button class="TestWasm-button TestWasm-hvcenter" tool-selector="Clear">Clear</button>
+            <button class="TestWasm-button TestWasm-hvcenter" tool-selector="Show schema">Show schema</button> -->
+        </div>
+        
+        <div class="TestWasm-Test1">
+          <button class="TestWasm-button" tool-selector="Test CppHandler message2">Test CppHandler message2</button>
+        </div>
+        <div class="TestWasm-Test2">
+          <button class="TestWasm-button" tool-selector="Test 2">Test 2</button>
+        </div>
+        <div class="TestWasm-Test3">
+          <button class="TestWasm-button" tool-selector="Test 3">Test 3</button>
+        </div>
+        <div class="TestWasm-Test4">
+          <button class="TestWasm-button" tool-selector="Test 4">Test 4</button>
+        </div>
+        <div class="TestWasm-Test5">
+          <button class="TestWasm-button" tool-selector="Test 5">Test 5</button>
+        </div>
+        <div class="TestWasm-Test6">
+          <button class="TestWasm-button" tool-selector="Test 6">Test 6</button>
+        </div>
+        <div class="TestWasm-Test7">
+          <button class="TestWasm-button" tool-selector="Test 7">Test 7</button>
+        </div>
+        <div class="TestWasm-Test8">
+          <button class="TestWasm-button" tool-selector="Test 8">Test 8</button>
+        </div>
+        <div class="TestWasm-ts-cpp-ts">
+          <button class="TestWasm-button" tool-selector="Test-ts-cpp-ts">Test ts-cpp-ts</button>
+        </div>
+  
+        <!-- <button class="TestWasm-button" class="TestWasm-Test1" tool-selector="Test 1">Test 1</button>
+        <button class="TestWasm-button" class="TestWasm-Test2" tool-selector="Test 2">Test 2</button>
+        <button class="TestWasm-button" class="TestWasm-Test3" tool-selector="Test 3">Test 3</button>
+        <button class="TestWasm-button" class="TestWasm-Test4" tool-selector="Test 4">Test 4</button> -->
+
+
+
+        <!-- <div class="Trigger"></div>
+        <div class="Test1"></div>
+        <div class="Test2"></div>
+        <div class="Test3"></div>
+        <div class="Test4"></div> -->
+      </div>
+
+  <!-- <div id="toolbox" style="height: 50px">
+    <button tool-selector="line-measure" class="tool-selector">line</button>
+  </div> -->
+  <script type="text/javascript" src="testWasmIntegratedCpp.js"></script>
+  <script type="text/javascript" src="testWasmIntegratedApp.js"></script>
+</body>
+
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.ts	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,210 @@
+var SendMessageToCpp: Function = null;
+export var TestWasmIntegratedModule : any;
+
+import * as TestStoneCodeGen from './build-wasm/TestStoneCodeGen_generated'
+
+/*
++--------------------------------------------------+
+|  install emscripten handlers                     |
++--------------------------------------------------+
+*/
+
+// (<any> window).Module = {
+//   preRun: [ 
+//     function() {
+//     console.log('Loading the Stone Framework using WebAssembly');
+//     }
+//   ],
+//   postRun: [ 
+//     function()  {
+//     // This function is called by ".js" wrapper once the ".wasm"
+//     // WebAssembly module has been loaded and compiled by the
+//     // browser
+//     console.log('WebAssembly is ready');
+//     // window.SendMessageToCpp = (<any> window).Module.cwrap('SendMessageToCpp', 'string', ['string']);
+//     // window.SendFreeTextToCpp = (<any> window).Module.cwrap('SendFreeTextToCpp', 'string', ['string']);
+//     }
+//   ],
+//   print: function(text : string) {
+//     console.log(text);
+//   },
+//   printErr: function(text : string) {
+//     console.error(text);
+//   },
+//   totalDependencies: 0
+// };
+
+/*
++--------------------------------------------------+
+|  install handlers                                |
++--------------------------------------------------+
+*/
+document.querySelectorAll(".TestWasm-button").forEach((e) => {
+  (e as HTMLButtonElement).addEventListener("click", () => {
+    ButtonClick(e.attributes["tool-selector"].value);
+  });
+});
+
+/*
++--------------------------------------------------+
+|  define stock messages                           |
++--------------------------------------------------+
+*/
+let schemaText: string = null;
+fetch("testTestStoneCodeGen.yaml").then(function(res) {return res.text();}).then(function(text) {schemaText = text;});
+
+let stockSerializedMessages = new Map<string,string>();
+stockSerializedMessages["Test CppHandler message2"] = null;
+fetch("cppHandler_test_Message2.json").then(function(res) {return res.text();}).then(function(text) {stockSerializedMessages["Test CppHandler message2"] = text;});
+
+stockSerializedMessages["Test 2"] = ` {
+  "type" : "TestStoneCodeGen.Message1",
+  "value" : {
+    "memberInt32" : -987,
+    "memberString" : "Salomé",
+    "memberEnumMonth" : "March",
+    "memberBool" : true,
+    "memberFloat32" : 0.1,
+    "memberFloat64" : -0.2,
+    "extraMember" : "don't care"
+  }
+}`;
+stockSerializedMessages["Test 3"] = "Test 3 stock message sdfsfsdfsdf";
+stockSerializedMessages["Test 4"] = "Test 4 stock message 355345345";
+stockSerializedMessages["Test 5"] = "Test 5 stock message 34535";
+stockSerializedMessages["Test 6"] = "Test 6 stock message xcvcxvx";
+stockSerializedMessages["Test 7"] = "Test 7 stock message fgwqewqdgg";
+stockSerializedMessages["Test 8"] = "Test 8 stock message fgfsdfsdgg";
+
+/*
++--------------------------------------------------+
+|  define handler                                  |
++--------------------------------------------------+
+*/
+
+function setSerializedInputValue(text: string) {
+  let e : HTMLTextAreaElement = document.getElementById('TestWasm-SerializedInput') as HTMLTextAreaElement;
+  e.value = text;
+}
+
+function getSerializedInputValue(): string {
+  let e : HTMLTextAreaElement = document.getElementById('TestWasm-SerializedInput') as HTMLTextAreaElement;
+  return e.value;
+}
+
+function setCppOutputValue(text: string) {
+  let e : HTMLTextAreaElement = document.getElementById('TestWasm-CppOutput') as HTMLTextAreaElement;
+  e.value = text;
+}
+
+function getCppOutputValue(): string {
+  let e : HTMLTextAreaElement = document.getElementById('TestWasm-CppOutput') as HTMLTextAreaElement;
+  return e.value;
+}
+
+function SendFreeTextFromCpp(txt: string):string
+{
+  setCppOutputValue(getCppOutputValue() + "\n" + txt);
+  return "";
+}
+(<any> window).SendFreeTextFromCpp = SendFreeTextFromCpp;
+
+var referenceMessages = Array<any>();
+
+function testTsCppTs() {
+  var r = new TestStoneCodeGen.Message2();
+  r.memberEnumMovieType = TestStoneCodeGen.MovieType.RomCom;
+  r.memberStringWithDefault = "overriden";
+  r.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives] = 0.5;
+  r.memberString = "reference-messsage2-test1";
+
+  referenceMessages[r.memberString] = r;
+  var strMsg2 = r.StoneSerialize();
+  let SendMessageToCppForEchoLocal = (<any> window).Module.cwrap('SendMessageToCppForEcho', 'string', ['string']);
+  SendMessageToCppForEchoLocal(strMsg2);
+}
+
+class MyEchoHandler implements TestStoneCodeGen.IHandler
+{
+  public HandleMessage2(value:  TestStoneCodeGen.Message2): boolean
+  {
+    if (value.memberString in referenceMessages) {
+      let r = referenceMessages[value.memberString];
+      let equals = (value.memberStringWithDefault == r.memberStringWithDefault);
+      if (TestStoneCodeGen.CrispType.CreamAndChives in r.memberMapEnumFloat) {
+        equals == equals && r.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives] == value.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives];
+      }
+      // TODO continue comparison
+
+      if (equals) {
+        console.log("objects are equals after round trip");
+        return true;
+      }
+    }
+    console.log("problem after round trip");
+    return true;
+  }
+}
+
+function SendMessageFromCpp(txt: string):string
+{
+  setCppOutputValue(getCppOutputValue() + "\n" + txt);
+  TestStoneCodeGen.StoneDispatchToHandler(txt, new MyEchoHandler());
+  return "";
+}
+(<any> window).SendMessageFromCpp = SendMessageFromCpp;
+
+
+
+function ButtonClick(buttonName: string) {
+  if (buttonName.startsWith('Test ')) {
+    setSerializedInputValue(stockSerializedMessages[buttonName]);
+  }
+  else if (buttonName == "Test-ts-cpp-ts") {
+    testTsCppTs();
+  }
+  else if(buttonName == 'Trigger')
+  {
+    let serializedInputValue:string = getSerializedInputValue();
+
+    let SendMessageToCppLocal = (<any> window).Module.cwrap('SendMessageToCpp', 'string', ['string']);
+    SendMessageToCppLocal(serializedInputValue);
+  }
+  else if(buttonName == 'Clear')
+  {
+    setCppOutputValue("");
+  }
+  else if(buttonName == 'ShowSchema')
+  {
+    setCppOutputValue(schemaText);
+  }
+  else
+  {
+    throw new Error("Internal error!");
+  }
+}
+
+
+
+// this method is called "from the C++ code" when the StoneApplication is updated.
+// it can be used to update the UI of the application
+function UpdateWebApplicationWithString(statusUpdateMessageString: string) {
+  console.log("updating web application (string): ", statusUpdateMessageString);
+  let statusUpdateMessage = JSON.parse(statusUpdateMessageString);
+
+  if ("event" in statusUpdateMessage)
+  {
+    let eventName = statusUpdateMessage["event"];
+    if (eventName == "appStatusUpdated")
+    {
+      //ui.onAppStatusUpdated(statusUpdateMessage["data"]);
+    }
+  }
+}
+
+
+function UpdateWebApplicationWithSerializedMessage(statusUpdateMessageString: string) {
+  console.log("updating web application (serialized message): ", statusUpdateMessageString);
+  console.log("<not supported!>");
+}
+ 
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/test_data/test2.yaml	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,17 @@
+enum EnumMonth0:
+  - January
+  - February
+  - Month
+
+struct Message1:
+  a: int32
+  b: string
+  c: EnumMonth0
+  d: bool
+
+struct Message2:
+  toto: string
+  tata: vector<Message1>
+  tutu: vector<string>
+  titi: map<string, string>
+  lulu: map<string, Message1>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Resources/CodeGeneration/test_data/testTestStoneCodeGen.yaml	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,64 @@
+#
+#        1         2         3         4         5         6         7         8
+# 345678901234567890123456789012345678901234567890123456789012345678901234567890
+#
+rootName: TestStoneCodeGen
+
+struct B:
+  __handler: cpp
+
+  someAs: vector<A>
+  someInts: vector<int32>
+
+struct C:
+  __handler: cpp
+
+  someBs: vector<B>
+  ddd:    vector<string>
+
+struct A:
+  __handler: cpp
+
+  someStrings: vector<string>
+  someInts2: vector<int32>
+  movies: vector<MovieType>
+
+struct Message1:
+  __handler: cpp
+
+  memberInt32: int32
+  memberString: string
+  memberEnumMonth: EnumMonth0
+  memberBool: bool
+  memberFloat32: float32
+  memberFloat64: float64
+
+struct Message2:
+  __handler: [cpp, ts]
+
+  memberString: string
+  memberStringWithDefault: string = "my-default-value"
+  memberVectorOfMessage1: vector<Message1>
+  memberVectorOfString: vector<string>
+  memberMapStringString: map<string, string>
+  memberMapStringStruct: map<string, Message1>
+  memberMapEnumFloat: map<CrispType, float32>
+  memberEnumMovieType: MovieType
+  memberJson: json
+
+enum MovieType:
+  - RomCom
+  - Horror
+  - ScienceFiction
+  - Vegetables
+
+enum CrispType:
+  - SaltAndPepper
+  - CreamAndChives
+  - Paprika
+  - Barbecue
+
+enum EnumMonth0:
+  - January
+  - February
+  - March
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/MultiPlatform/BasicScene/BasicScene.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,275 @@
+/**
+ * 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/>.
+ **/
+
+#include "BasicScene.h"
+
+// From Stone
+#include "Framework/Scene2D/Scene2D.h"
+#include "Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "Framework/Scene2D/PolylineSceneLayer.h"
+#include "Framework/Scene2D/TextSceneLayer.h"
+
+#include "Framework/Scene2D/PanSceneTracker.h"
+#include "Framework/Scene2D/ZoomSceneTracker.h"
+#include "Framework/Scene2D/RotateSceneTracker.h"
+
+#include "Framework/Scene2D/CairoCompositor.h"
+
+// From Orthanc framework
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/PngWriter.h>
+
+using namespace OrthancStone;
+
+const unsigned int BASIC_SCENE_FONT_SIZE = 32;
+const int BASIC_SCENE_LAYER_POSITION = 150;
+
+void PrepareScene(Scene2D& scene)
+{
+  //Scene2D& scene(*controller->GetScene());
+  // Texture of 2x2 size
+  {
+    Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false);
+
+    uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0));
+    p[0] = 255;
+    p[1] = 0;
+    p[2] = 0;
+
+    p[3] = 0;
+    p[4] = 255;
+    p[5] = 0;
+
+    p = reinterpret_cast<uint8_t*>(i.GetRow(1));
+    p[0] = 0;
+    p[1] = 0;
+    p[2] = 255;
+
+    p[3] = 255;
+    p[4] = 0;
+    p[5] = 0;
+
+    scene.SetLayer(12, new ColorTextureSceneLayer(i));
+
+    std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
+    l->SetOrigin(-3, 2);
+    l->SetPixelSpacing(1.5, 1);
+    l->SetAngle(20.0 / 180.0 * 3.14);
+    scene.SetLayer(14, l.release());
+  }
+
+  // Texture of 1x1 size
+  {
+    Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false);
+
+    uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0));
+    p[0] = 255;
+    p[1] = 0;
+    p[2] = 0;
+
+    std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
+    l->SetOrigin(-2, 1);
+    l->SetAngle(20.0 / 180.0 * 3.14);
+    scene.SetLayer(13, l.release());
+  }
+
+  // Some lines
+  {
+    std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer);
+
+    layer->SetThickness(1);
+
+    PolylineSceneLayer::Chain chain;
+    chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5));
+    chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5));
+    chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5));
+    chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5));
+    layer->AddChain(chain, true, 255, 0, 0);
+
+    chain.clear();
+    chain.push_back(ScenePoint2D(-5, -5));
+    chain.push_back(ScenePoint2D(5, -5));
+    chain.push_back(ScenePoint2D(5, 5));
+    chain.push_back(ScenePoint2D(-5, 5));
+    layer->AddChain(chain, true, 0, 255, 0);
+
+    double dy = 1.01;
+    chain.clear();
+    chain.push_back(ScenePoint2D(-4, -4));
+    chain.push_back(ScenePoint2D(4, -4 + dy));
+    chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy));
+    chain.push_back(ScenePoint2D(4, 2));
+    layer->AddChain(chain, false, 0, 0, 255);
+
+    //    layer->SetColor(0,255, 255);
+    scene.SetLayer(50, layer.release());
+  }
+
+  // Some text
+  {
+    std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+    layer->SetText("Hello");
+    scene.SetLayer(100, layer.release());
+  }
+}
+
+#if ORTHANC_SANDBOXED == 0
+void TakeScreenshot(const std::string& target,
+                    const OrthancStone::Scene2D& scene,
+                    unsigned int canvasWidth,
+                    unsigned int canvasHeight)
+{
+  using namespace OrthancStone;
+  // Take a screenshot, then save it as PNG file
+  CairoCompositor compositor(scene, canvasWidth, canvasHeight);
+  compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1);
+  compositor.Refresh();
+
+  Orthanc::ImageAccessor canvas;
+  compositor.GetCanvas().GetReadOnlyAccessor(canvas);
+
+  Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
+  Orthanc::ImageProcessing::Convert(png, canvas);
+
+  Orthanc::PngWriter writer;
+  writer.WriteToFile(target, png);
+}
+#endif
+
+void ShowCursorInfo(Scene2D& scene, const PointerEvent& pointerEvent)
+{
+  ScenePoint2D p = pointerEvent.GetMainPosition().Apply(scene.GetCanvasToSceneTransform());
+
+  char buf[64];
+  sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY());
+
+  if (scene.HasLayer(BASIC_SCENE_LAYER_POSITION))
+  {
+    TextSceneLayer& layer =
+        dynamic_cast<TextSceneLayer&>(scene.GetLayer(BASIC_SCENE_LAYER_POSITION));
+    layer.SetText(buf);
+    layer.SetPosition(p.GetX(), p.GetY());
+  }
+  else
+  {
+    std::unique_ptr<TextSceneLayer>
+        layer(new TextSceneLayer);
+    layer->SetColor(0, 255, 0);
+    layer->SetText(buf);
+    layer->SetBorder(20);
+    layer->SetAnchor(BitmapAnchor_BottomCenter);
+    layer->SetPosition(p.GetX(), p.GetY());
+    scene.SetLayer(BASIC_SCENE_LAYER_POSITION, layer.release());
+  }
+}
+
+
+
+bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent)
+{
+  if (currentTracker_.get() != NULL)
+  {
+    switch (event.type)
+    {
+    case GUIADAPTER_EVENT_MOUSEUP:
+    {
+      currentTracker_->PointerUp(pointerEvent);
+      if (!currentTracker_->IsAlive())
+      {
+        currentTracker_.reset();
+      }
+    };break;
+    case GUIADAPTER_EVENT_MOUSEMOVE:
+    {
+      currentTracker_->PointerMove(pointerEvent);
+    };break;
+    default:
+      return false;
+    }
+    return true;
+  }
+  else if (event.type == GUIADAPTER_EVENT_MOUSEDOWN)
+  {
+    if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT)
+    {
+      currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent));
+    }
+    else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE)
+    {
+      currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent));
+    }
+    else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT)
+    {
+      currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, viewportController_->GetViewport().GetCanvasHeight()));
+    }
+  }
+  else if (event.type == GUIADAPTER_EVENT_MOUSEMOVE)
+  {
+    if (showCursorInfo_)
+    {
+      Scene2D& scene(viewportController_->GetScene());
+      ShowCursorInfo(scene, pointerEvent);
+    }
+    return true;
+  }
+  return false;
+}
+
+bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent)
+{
+  if (guiEvent.type == GUIADAPTER_EVENT_KEYDOWN)
+  {
+    switch (guiEvent.sym[0])
+    {
+    case 's':
+    {
+      //viewportController_->FitContent(viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight());
+      viewportController_->FitContent();
+      return true;
+    };
+#if ORTHANC_SANDBOXED == 0
+    case 'c':
+    {
+      Scene2D& scene(viewportController_->GetScene());
+      TakeScreenshot("screenshot.png", scene, viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight());
+      return true;
+    }
+#endif
+    case 'd':
+    {
+      showCursorInfo_ = !showCursorInfo_;
+      if (!showCursorInfo_)
+      {
+        Scene2D& scene(viewportController_->GetScene());
+        scene.DeleteLayer(BASIC_SCENE_LAYER_POSITION);
+      }
+
+      return true;
+    }
+    }
+  }
+  return false;
+}
+
+bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent)
+{
+  return false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/MultiPlatform/BasicScene/BasicScene.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,55 @@
+/**
+ * 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 <boost/shared_ptr.hpp>
+#include "Framework/Scene2DViewport/ViewportController.h"
+#include "Framework/Scene2D/Scene2D.h"
+
+extern const unsigned int BASIC_SCENE_FONT_SIZE;
+extern const int BASIC_SCENE_LAYER_POSITION;
+
+extern void PrepareScene(OrthancStone::Scene2D& scene);
+extern void TakeScreenshot(const std::string& target,
+                           const OrthancStone::Scene2D& scene,
+                           unsigned int canvasWidth,
+                           unsigned int canvasHeight);
+
+
+#include "Applications/Generic/Scene2DInteractor.h"
+#include "Framework/Scene2DViewport/IFlexiblePointerTracker.h"
+
+
+class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor
+{
+  boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>  currentTracker_;
+  bool                                                      showCursorInfo_;
+public:
+  BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) :
+    Scene2DInteractor(viewportController),
+    showCursorInfo_(false)
+  {}
+
+  virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override;
+  virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent) override;
+  virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent) override;
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/MultiPlatform/BasicScene/mainQt.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,103 @@
+/**
+ * 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/>.
+ **/
+
+#define GLEW_STATIC 1
+// From Stone
+#include "../../Framework/OpenGL/OpenGLIncludes.h"
+#include "../../Applications/Sdl/SdlWindow.h"
+#include "../../Framework/Scene2D/CairoCompositor.h"
+#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Scene2D/PanSceneTracker.h"
+#include "../../Framework/Scene2D/RotateSceneTracker.h"
+#include "../../Framework/Scene2D/Scene2D.h"
+#include "../../Framework/Scene2D/ZoomSceneTracker.h"
+#include "../../Framework/Scene2DViewport/ViewportController.h"
+#include "../../Framework/Scene2DViewport/UndoStack.h"
+
+#include "../../Framework/StoneInitialization.h"
+#include "../../Framework/Messages/MessageBroker.h"
+
+// From Orthanc framework
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/PngWriter.h>
+
+#include <boost/make_shared.hpp>
+#include <boost/ref.hpp>
+#include "EmbeddedResources.h"
+
+#include <stdio.h>
+#include <QDebug>
+#include <QWindow>
+
+#include "BasicScene.h"
+
+
+using namespace OrthancStone;
+
+
+
+static void GLAPIENTRY OpenGLMessageCallback(GLenum source,
+                                             GLenum type,
+                                             GLuint id,
+                                             GLenum severity,
+                                             GLsizei length,
+                                             const GLchar* message,
+                                             const void* userParam )
+{
+  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
+  {
+    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+            ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
+            type, severity, message );
+  }
+}
+
+extern void InitGL();
+
+#include <QApplication>
+#include "BasicSceneWindow.h"
+
+int main(int argc, char* argv[])
+{
+  QApplication a(argc, argv);
+
+  OrthancStone::Samples::BasicSceneWindow window;
+  window.show();
+  window.GetOpenGlWidget().Init();
+
+  MessageBroker broker;
+  boost::shared_ptr<UndoStack> undoStack(new UndoStack);
+  boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), window.GetOpenGlWidget());
+  PrepareScene(controller->GetScene());
+
+  window.GetOpenGlWidget().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
+                     BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1);
+
+  boost::shared_ptr<OrthancStone::Scene2DInteractor> interactor(new BasicScene2DInteractor(controller));
+  window.GetOpenGlWidget().SetInteractor(interactor);
+
+  controller->FitContent();
+
+  return a.exec();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/MultiPlatform/BasicScene/mainSdl.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,199 @@
+/**
+ * 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/>.
+ **/
+
+
+// From Stone
+#include "Framework/Viewport/SdlViewport.h"
+#include "Framework/Scene2D/OpenGLCompositor.h"
+#include "Framework/Scene2DViewport/UndoStack.h"
+#include "Framework/StoneInitialization.h"
+#include "Framework/Messages/MessageBroker.h"
+
+// From Orthanc framework
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+#include <boost/make_shared.hpp>
+#include <boost/ref.hpp>
+
+#include <SDL.h>
+#include <stdio.h>
+
+
+#include "BasicScene.h"
+
+using namespace OrthancStone;
+
+boost::shared_ptr<BasicScene2DInteractor> interactor;
+
+void HandleApplicationEvent(boost::shared_ptr<OrthancStone::ViewportController> controller,
+                            const SDL_Event& event)
+{
+  using namespace OrthancStone;
+  Scene2D& scene(controller->GetScene());
+  if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEMOTION)
+  {
+    // TODO: this code is copy/pasted from GuiAdapter::Run() -> find the right place
+    int scancodeCount = 0;
+    const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
+    bool ctrlPressed(false);
+    bool shiftPressed(false);
+    bool altPressed(false);
+
+    if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL])
+      ctrlPressed = true;
+    if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL])
+      ctrlPressed = true;
+    if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT])
+      shiftPressed = true;
+    if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT])
+      shiftPressed = true;
+    if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT])
+      altPressed = true;
+
+    GuiAdapterMouseEvent guiEvent;
+    ConvertFromPlatform(guiEvent, ctrlPressed, shiftPressed, altPressed, event);
+    PointerEvent pointerEvent;
+    pointerEvent.AddPosition(controller->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
+
+    interactor->OnMouseEvent(guiEvent, pointerEvent);
+    return;
+  }
+  else if ((event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) && event.key.repeat == 0  /* Ignore key bounce */)
+  {
+    GuiAdapterKeyboardEvent guiEvent;
+    ConvertFromPlatform(guiEvent, event);
+
+    interactor->OnKeyboardEvent(guiEvent);
+  }
+
+}
+
+
+static void GLAPIENTRY
+OpenGLMessageCallback(GLenum source,
+                      GLenum type,
+                      GLuint id,
+                      GLenum severity,
+                      GLsizei length,
+                      const GLchar* message,
+                      const void* userParam )
+{
+  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
+  {
+    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+            ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
+            type, severity, message );
+  }
+}
+
+
+void Run(boost::shared_ptr<OrthancStone::ViewportController> controller)
+{
+  SdlViewport& sdlViewport = dynamic_cast<SdlViewport&>(controller->GetViewport());
+
+  glEnable(GL_DEBUG_OUTPUT);
+  glDebugMessageCallback(OpenGLMessageCallback, 0);
+
+  controller->GetViewport().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
+                     BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1);
+
+  controller->GetViewport().Refresh();
+  controller->FitContent();
+
+
+  bool stop = false;
+  while (!stop)
+  {
+    controller->GetViewport().Refresh();
+
+    SDL_Event event;
+    while (!stop &&
+           SDL_PollEvent(&event))
+    {
+      if (event.type == SDL_QUIT)
+      {
+        stop = true;
+        break;
+      }
+      else if (event.type == SDL_WINDOWEVENT &&
+               event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
+      {
+        sdlViewport.UpdateSize(event.window.data1, event.window.data2);
+      }
+      else if (event.type == SDL_KEYDOWN &&
+               event.key.repeat == 0 /* Ignore key bounce */)
+      {
+        switch (event.key.keysym.sym)
+        {
+          case SDLK_f:
+            sdlViewport.GetWindow().ToggleMaximize();
+            break;
+              
+          case SDLK_q:
+            stop = true;
+            break;
+
+          default:
+            break;
+        }
+      }
+      
+      HandleApplicationEvent(controller, event);
+    }
+
+    SDL_Delay(1);
+  }
+  interactor.reset();
+}
+
+
+
+
+/**
+ * IMPORTANT: The full arguments to "main()" are needed for SDL on
+ * Windows. Otherwise, one gets the linking error "undefined reference
+ * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
+ **/
+int main(int argc, char* argv[])
+{
+  using namespace OrthancStone;
+  StoneInitialize();
+  Orthanc::Logging::EnableInfoLevel(true);
+
+  try
+  {
+    SdlOpenGLViewport viewport("Hello", 1024, 768);
+    MessageBroker broker;
+    boost::shared_ptr<UndoStack> undoStack(new UndoStack);
+    boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), boost::ref(viewport));
+    interactor.reset(new BasicScene2DInteractor(controller));
+    PrepareScene(controller->GetScene());
+    Run(controller);
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "EXCEPTION: " << e.What();
+  }
+
+  StoneFinalize();
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Qt/BasicSceneWindow.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,53 @@
+/**
+ * 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/>.
+ **/
+#include "../../Framework/OpenGL/OpenGLIncludes.h"
+#include "BasicSceneWindow.h"
+
+/**
+ * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as
+ * this makes CMake unable to detect when the UI file changes.
+ **/
+#include <ui_BasicSceneWindow.h>
+#include "../../Applications/Samples/SampleApplicationBase.h"
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+
+    BasicSceneWindow::BasicSceneWindow(
+      QWidget *parent) :
+      ui_(new Ui::BasicSceneWindow)
+    {
+      ui_->setupUi(this);
+    }
+
+    BasicSceneWindow::~BasicSceneWindow()
+    {
+      delete ui_;
+    }
+
+    QStoneOpenGlWidget& BasicSceneWindow::GetOpenGlWidget()
+    {
+      return *(ui_->centralWidget);
+    }
+
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Qt/BasicSceneWindow.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,53 @@
+/**
+ * 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 <QMainWindow>
+#include <QStoneOpenGlWidget.h>
+// #include "../../Qt/QCairoWidget.h"
+// #include "../../Qt/QStoneMainWindow.h"
+
+namespace Ui 
+{
+  class BasicSceneWindow;
+}
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+
+    //class SampleSingleCanvasApplicationBase;
+
+    class BasicSceneWindow : public QMainWindow
+    {
+      Q_OBJECT
+
+    private:
+      Ui::BasicSceneWindow*   ui_;
+      //SampleSingleCanvasApplicationBase&  stoneSampleApplication_;
+
+    public:
+      explicit BasicSceneWindow(QWidget *parent = 0);
+      ~BasicSceneWindow();
+
+      QStoneOpenGlWidget& GetOpenGlWidget();
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Qt/BasicSceneWindow.ui	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BasicSceneWindow</class>
+ <widget class="QMainWindow" name="BasicSceneWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>903</width>
+    <height>634</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>500</width>
+    <height>300</height>
+   </size>
+  </property>
+  <property name="baseSize">
+   <size>
+    <width>500</width>
+    <height>300</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Stone of Orthanc</string>
+  </property>
+  <property name="layoutDirection">
+   <enum>Qt::LeftToRight</enum>
+  </property>
+  <widget class="QWidget" name="mainWidget">
+   <property name="sizePolicy">
+    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+     <horstretch>0</horstretch>
+     <verstretch>0</verstretch>
+    </sizepolicy>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::LeftToRight</enum>
+   </property>
+   <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0">
+    <property name="sizeConstraint">
+     <enum>QLayout::SetDefaultConstraint</enum>
+    </property>
+    <item>
+     <widget class="OrthancStone::QStoneOpenGlWidget" name="centralWidget" native="true">
+      <property name="minimumSize">
+       <size>
+        <width>0</width>
+        <height>500</height>
+       </size>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>903</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuTest">
+    <property name="title">
+     <string>Test</string>
+    </property>
+   </widget>
+   <addaction name="menuTest"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QStoneOpenGlWidget</class>
+   <extends>QWidget</extends>
+   <header location="global">QStoneOpenGlWidget.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Qt/CMakeLists.txt	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,83 @@
+cmake_minimum_required(VERSION 2.8.3)
+
+#####################################################################
+## Configuration of the Orthanc framework
+#####################################################################
+
+# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it
+# must be the first inclusion
+include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/Version.cmake)
+
+if (ORTHANC_STONE_VERSION STREQUAL "mainline")
+  set(ORTHANC_FRAMEWORK_VERSION "mainline")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
+else()
+  set(ORTHANC_FRAMEWORK_VERSION "1.5.7")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
+endif()
+
+set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")")
+set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"")
+set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
+
+
+#####################################################################
+## Configuration of the Stone framework
+#####################################################################
+
+include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
+
+DownloadPackage(
+  "a24b8136b8f3bb93f166baf97d9328de"
+  "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip"
+  "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83")
+
+set(ORTHANC_STONE_APPLICATION_RESOURCES
+  UBUNTU_FONT  ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf
+  )
+
+SET(ENABLE_GOOGLE_TEST OFF)
+SET(ENABLE_LOCALE ON)
+SET(ENABLE_QT ON)
+SET(ENABLE_SDL OFF)
+SET(ENABLE_WEB_CLIENT ON)
+SET(ORTHANC_SANDBOXED OFF)
+LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
+
+include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake)
+
+add_definitions(
+  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
+  )
+#####################################################################
+## Build the samples
+#####################################################################
+
+add_library(OrthancStone STATIC
+  ${ORTHANC_STONE_SOURCES}
+  )
+
+list(APPEND BASIC_SCENE_APPLICATIONS_SOURCES
+  BasicSceneWindow.cpp
+  )
+
+ORTHANC_QT_WRAP_UI(BASIC_SCENE_APPLICATIONS_SOURCES
+  BasicSceneWindow.ui
+  )
+
+ORTHANC_QT_WRAP_CPP(BASIC_SCENE_APPLICATIONS_SOURCES
+  BasicSceneWindow.h
+  QStoneOpenGlWidget.h
+  )
+
+add_executable(MpBasicScene
+  ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.h
+  ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.cpp
+  ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/mainQt.cpp
+  QStoneOpenGlWidget.cpp
+  ${BASIC_SCENE_APPLICATIONS_SOURCES}
+  )
+
+target_include_directories(MpBasicScene PUBLIC ${CMAKE_SOURCE_DIR} ${ORTHANC_STONE_ROOT})
+target_link_libraries(MpBasicScene OrthancStone)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Qt/QStoneOpenGlWidget.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,171 @@
+#include "../../Framework/OpenGL/OpenGLIncludes.h"
+#include "QStoneOpenGlWidget.h"
+
+#include <QMouseEvent>
+
+using namespace OrthancStone;
+
+void QStoneOpenGlWidget::initializeGL()
+{
+  glewInit();
+}
+
+void QStoneOpenGlWidget::MakeCurrent()
+{
+  this->makeCurrent();
+}
+
+void QStoneOpenGlWidget::resizeGL(int w, int h)
+{
+
+}
+
+void QStoneOpenGlWidget::paintGL()
+{
+  if (compositor_)
+  {
+    compositor_->Refresh();
+  }
+  doneCurrent();
+}
+
+void ConvertFromPlatform(
+    OrthancStone::GuiAdapterMouseEvent& guiEvent,
+    PointerEvent& pointerEvent,
+    const QMouseEvent& qtEvent,
+    const IViewport& viewport)
+{
+  guiEvent.targetX = qtEvent.x();
+  guiEvent.targetY = qtEvent.y();
+  pointerEvent.AddPosition(viewport.GetPixelCenterCoordinates(guiEvent.targetX, guiEvent.targetY));
+
+  switch (qtEvent.button())
+  {
+  case Qt::LeftButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT; break;
+  case Qt::MiddleButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_MIDDLE; break;
+  case Qt::RightButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_RIGHT; break;
+  default:
+    guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT;
+  }
+
+  if (qtEvent.modifiers().testFlag(Qt::ShiftModifier))
+  {
+    guiEvent.shiftKey = true;
+  }
+  if (qtEvent.modifiers().testFlag(Qt::ControlModifier))
+  {
+    guiEvent.ctrlKey = true;
+  }
+  if (qtEvent.modifiers().testFlag(Qt::AltModifier))
+  {
+    guiEvent.altKey = true;
+  }
+}
+
+void QStoneOpenGlWidget::mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType)
+{
+  OrthancStone::GuiAdapterMouseEvent guiEvent;
+  PointerEvent pointerEvent;
+  ConvertFromPlatform(guiEvent, pointerEvent, *qtEvent, *this);
+  guiEvent.type = guiEventType;
+
+  if (sceneInteractor_.get() != NULL && compositor_.get() != NULL)
+  {
+    sceneInteractor_->OnMouseEvent(guiEvent, pointerEvent);
+  }
+
+  // force redraw of the OpenGL widget
+  update();
+}
+
+void QStoneOpenGlWidget::mousePressEvent(QMouseEvent* qtEvent)
+{
+  mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEDOWN);
+}
+
+void QStoneOpenGlWidget::mouseMoveEvent(QMouseEvent* qtEvent)
+{
+  mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEMOVE);
+}
+
+void QStoneOpenGlWidget::mouseReleaseEvent(QMouseEvent* qtEvent)
+{
+  mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEUP);
+}
+
+void ConvertFromPlatform(
+    OrthancStone::GuiAdapterKeyboardEvent& guiEvent,
+    const QKeyEvent& qtEvent)
+{
+  if (qtEvent.text().length() > 0)
+  {
+    guiEvent.sym[0] = qtEvent.text()[0].cell();
+  }
+  else
+  {
+    guiEvent.sym[0] = 0;
+  }
+  guiEvent.sym[1] = 0;
+
+  if (qtEvent.modifiers().testFlag(Qt::ShiftModifier))
+  {
+    guiEvent.shiftKey = true;
+  }
+  if (qtEvent.modifiers().testFlag(Qt::ControlModifier))
+  {
+    guiEvent.ctrlKey = true;
+  }
+  if (qtEvent.modifiers().testFlag(Qt::AltModifier))
+  {
+    guiEvent.altKey = true;
+  }
+
+}
+
+
+bool QStoneOpenGlWidget::keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType)
+{
+  bool handled = false;
+  OrthancStone::GuiAdapterKeyboardEvent guiEvent;
+  ConvertFromPlatform(guiEvent, *qtEvent);
+  guiEvent.type = guiEventType;
+
+  if (sceneInteractor_.get() != NULL && compositor_.get() != NULL)
+  {
+    handled = sceneInteractor_->OnKeyboardEvent(guiEvent);
+
+    if (handled)
+    {
+      // force redraw of the OpenGL widget
+      update();
+    }
+  }
+  return handled;
+}
+
+void QStoneOpenGlWidget::keyPressEvent(QKeyEvent *qtEvent)
+{
+  bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYDOWN);
+  if (!handled)
+  {
+    QOpenGLWidget::keyPressEvent(qtEvent);
+  }
+}
+
+void QStoneOpenGlWidget::keyReleaseEvent(QKeyEvent *qtEvent)
+{
+  bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYUP);
+  if (!handled)
+  {
+    QOpenGLWidget::keyPressEvent(qtEvent);
+  }
+}
+
+void QStoneOpenGlWidget::wheelEvent(QWheelEvent *qtEvent)
+{
+  OrthancStone::GuiAdapterWheelEvent guiEvent;
+  throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+
+  // force redraw of the OpenGL widget
+  update();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Qt/QStoneOpenGlWidget.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,90 @@
+#pragma once
+#include "../../Framework/OpenGL/OpenGLIncludes.h"
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions>
+#include <QOpenGLContext>
+
+#include <boost/shared_ptr.hpp>
+#include "../../Framework/OpenGL/IOpenGLContext.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Viewport/ViewportBase.h"
+#include "../../Applications/Generic/Scene2DInteractor.h"
+
+namespace OrthancStone
+{
+  class QStoneOpenGlWidget :
+      public QOpenGLWidget,
+      public OpenGL::IOpenGLContext,
+      public ViewportBase
+  {
+    std::unique_ptr<OrthancStone::OpenGLCompositor> compositor_;
+    boost::shared_ptr<Scene2DInteractor> sceneInteractor_;
+    QOpenGLContext                        openGlContext_;
+
+  public:
+    QStoneOpenGlWidget(QWidget *parent) :
+      QOpenGLWidget(parent),
+      ViewportBase("QtStoneOpenGlWidget")  // TODO: we shall be able to define a name but construction time is too early !
+    {
+      setFocusPolicy(Qt::StrongFocus);  // to enable keyPressEvent
+      setMouseTracking(true);           // to enable mouseMoveEvent event when no button is pressed
+    }
+
+    void Init()
+    {
+      QSurfaceFormat requestedFormat;
+      requestedFormat.setVersion( 2, 0 );
+      openGlContext_.setFormat( requestedFormat );
+      openGlContext_.create();
+      openGlContext_.makeCurrent(context()->surface());
+
+      compositor_.reset(new OpenGLCompositor(*this, GetScene()));
+    }
+
+  protected:
+
+    //**** QWidget overrides
+    void initializeGL() override;
+    void resizeGL(int w, int h) override;
+    void paintGL() override;
+
+    void mousePressEvent(QMouseEvent* event) override;
+    void mouseMoveEvent(QMouseEvent* event) override;
+    void mouseReleaseEvent(QMouseEvent* event) override;
+    void keyPressEvent(QKeyEvent* event) override;
+    void keyReleaseEvent(QKeyEvent *event) override;
+    void wheelEvent(QWheelEvent* event) override;
+
+    //**** IOpenGLContext overrides
+
+    virtual void MakeCurrent() override;
+    virtual void SwapBuffer() override {}
+
+    virtual unsigned int GetCanvasWidth() const override
+    {
+      return this->width();
+    }
+
+    virtual unsigned int GetCanvasHeight() const override
+    {
+      return this->height();
+    }
+
+  public:
+
+    void SetInteractor(boost::shared_ptr<Scene2DInteractor> sceneInteractor)
+    {
+      sceneInteractor_ = sceneInteractor;
+    }
+
+    virtual ICompositor& GetCompositor()
+    {
+      return *compositor_;
+    }
+
+  protected:
+    void mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType);
+    bool keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType);
+
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Qt/Scene2DInteractor.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,72 @@
+#include "Scene2DInteractor.h"
+
+#include "../../Framework/Scene2D/PanSceneTracker.h"
+#include "../../Framework/Scene2D/ZoomSceneTracker.h"
+#include "../../Framework/Scene2D/RotateSceneTracker.h"
+
+
+namespace OrthancStone
+{
+
+}
+
+using namespace OrthancStone;
+
+
+bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent)
+{
+  if (currentTracker_.get() != NULL)
+  {
+    switch (event.type)
+    {
+    case GUIADAPTER_EVENT_MOUSEUP:
+    {
+      currentTracker_->PointerUp(pointerEvent);
+      if (!currentTracker_->IsAlive())
+      {
+        currentTracker_.reset();
+      }
+    };break;
+    case GUIADAPTER_EVENT_MOUSEMOVE:
+    {
+      currentTracker_->PointerMove(pointerEvent);
+    };break;
+    }
+    return true;
+  }
+  else
+  {
+    if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT)
+    {
+      currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent));
+    }
+    else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE)
+    {
+      currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent));
+    }
+    else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT && compositor_.get() != NULL)
+    {
+      currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, compositor_->GetHeight()));
+    }
+    return true;
+  }
+  return false;
+}
+
+bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent)
+{
+  switch (guiEvent.sym[0])
+  {
+  case 's':
+  {
+    viewportController_->FitContent(compositor_->GetWidth(), compositor_->GetHeight());
+    return true;
+  };
+  }
+  return false;
+}
+
+bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent)
+{
+  return false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Qt/Scene2DInteractor.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "../../Applications/Generic/Scene2DInteractor.h"
+#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
+
+
+class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor
+{
+  boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>  currentTracker_;
+public:
+  BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) :
+    Scene2DInteractor(viewportController)
+  {}
+
+  virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override;
+  virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent);
+  virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent);
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/README.md	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,195 @@
+
+**These samples are deprecated and not guaranteed to work. A migration to a 
+new version of the Stone API is underway.**
+
+Please read orthanc-stone/Samples/README.md first for general requirements.
+
+
+
+Deprecated -- to be sorted
+===========================
+
+The following assumes that the source code to be downloaded in
+`~/orthanc-stone` and Orthanc source code to be checked out in
+`~/orthanc`. 
+
+Building the WASM samples
+-------------------------------------
+
+```
+cd ~/orthanc-stone/Applications/Samples
+./build-wasm.sh
+```
+
+Serving the WASM samples
+------------------------------------
+```
+# launch an Orthanc listening on 8042 port:
+Orthanc
+
+# launch an nginx that will serve the WASM static files and reverse
+# proxy
+sudo nginx -p $(pwd) -c nginx.local.conf
+```
+
+You can now open the samples in http://localhost:9977
+
+Building the SDL native samples (SimpleViewer only)
+---------------------------------------------------
+
+The following also assumes that you have checked out the Orthanc
+source code in an `orthanc` folder next to the Stone of Orthanc
+repository, please enter the following:
+
+**Simple make generator with dynamic build**
+
+```
+# Please set $currentDir to the current folder
+mkdir -p ~/builds/orthanc-stone-build
+cd ~/builds/orthanc-stone-build
+cmake -DORTHANC_FRAMEWORK_SOURCE=path \
+    -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc \
+    -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON \
+    ~/orthanc-stone/Applications/Samples/
+```
+
+**Ninja generator with static SDL build (pwsh script)**
+
+```
+# Please yourself one level above the orthanc-stone and orthanc folders
+if( -not (test-path stone_build_sdl)) { mkdir stone_build_sdl }
+cd stone_build_sdl
+cmake -G Ninja -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples/
+```
+
+**Ninja generator with static SDL build (bash/zsh script)**
+
+```
+# Please yourself one level above the orthanc-stone and orthanc folders
+if( -not (test-path stone_build_sdl)) { mkdir stone_build_sdl }
+cd stone_build_sdl
+cmake -G Ninja -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="`pwd`/../orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples/
+```
+
+**Visual Studio 2017 generator with static SDL build  (pwsh script)**
+
+```
+# The following will use Visual Studio 2017 to build the SDL samples
+# in debug mode (with multiple compilers in parallel). NOTE: place 
+# yourself one level above the `orthanc-stone` and `orthanc` folders
+
+if( -not (test-path stone_build_sdl)) { mkdir stone_build_sdl }
+cd stone_build_sdl
+cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples/
+cmake --build . --config Debug
+```
+
+If you are working on Windows, add the correct generator option to
+cmake to, for instance, generate msbuild files for Visual Studio.
+
+Then, under Linux:
+```
+cmake --build . --target OrthancStoneSimpleViewer -- -j 5
+```
+
+Note: replace `$($pwd)` with the current directory when not using Powershell
+
+Building the Qt native samples (SimpleViewer only) under Windows:
+------------------------------------------------------------------
+
+**Visual Studio 2017 generator with static Qt build  (pwsh script)**
+
+For instance, if Qt is installed in `C:\Qt\5.12.0\msvc2017_64`
+
+```
+# The following will use Visual Studio 2017 to build the SDL samples
+# in debug mode (with multiple compilers in parallel). NOTE: place 
+# yourself one level above the `orthanc-stone` and `orthanc` folders
+
+if( -not (test-path stone_build_qt)) { mkdir stone_build_qt }
+cd stone_build_qt
+cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DCMAKE_PREFIX_PATH=C:\Qt\5.12.0\msvc2017_64 -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_QT=ON  ../orthanc-stone/Applications/Samples/
+cmake --build . --config Debug
+```
+
+Note: replace `$($pwd)` with the current directory when not using Powershell
+
+
+
+
+
+
+Building the SDL native samples (SimpleViewer only) under Windows:
+------------------------------------------------------------------
+`cmake -DSTATIC_BUILD=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON -G "Visual Studio 15 2017 Win64" ../orthanc-stone/Applications/Samples/`
+
+Note: replace `$($pwd)` with the current directory when not using Powershell
+
+Executing the native samples:
+--------------------------------
+```
+# launch an Orthanc listening on 8042 port:
+Orthanc
+
+# launch the sample
+./OrthancStoneSimpleViewer --studyId=XX
+``` 
+
+Build the Application Samples
+-----------------------------
+
+**Visual Studio 2008 (v90) **
+
+```
+cmake -G "Visual Studio 9 2008" -DUSE_LEGACY_JSONCPP=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples
+```
+
+**Visual Studio 2019 (v142) **
+
+```
+cmake -G "Visual Studio 16 2019" -A x64 -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples
+```
+
+**Visual Studio 2017 (v140) **
+
+```
+cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples
+```
+
+
+Build the core Samples
+---------------------------
+How to build the newest (2019-04-29) SDL samples under Windows, *inside* a
+folder that is sibling to the orthanc-stone folder: 
+
+**Visual Studio 2019 (v142) **
+
+```
+cmake -G "Visual Studio 16 2019" -A x64 -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl
+```
+
+**Visual Studio 2017 (v140) **
+
+```
+cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl
+```
+
+**Visual Studio 2008 (v90) **
+
+```
+cmake -G "Visual Studio 9 2008" -DUSE_LEGACY_JSONCPP=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl
+```
+
+And under Ubuntu (note the /mnt/c/osi/dev/orthanc folder):
+```
+cmake -G "Ninja" -DENABLE_OPENGL=ON -DSTATIC_BUILD=OFF -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="/mnt/c/osi/dev/orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl
+```
+
+TODO trackers:
+- CANCELLED (using outlined text now) text overlay 50% --> ColorTextureLayer 50%
+- DONE angle tracker: draw arcs
+- Handles on arc
+- Select measure tool with hit test --> Delete command
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Sdl/BasicScene.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,418 @@
+/**
+ * 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/>.
+ **/
+
+
+// From Stone
+#include "../../Framework/Viewport/SdlViewport.h"
+#include "../../Framework/Scene2D/CairoCompositor.h"
+#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Scene2D/PanSceneTracker.h"
+#include "../../Framework/Scene2D/RotateSceneTracker.h"
+#include "../../Framework/Scene2D/ZoomSceneTracker.h"
+#include "../../Framework/Scene2DViewport/ViewportController.h"
+#include "../../Framework/Scene2DViewport/UndoStack.h"
+
+#include "../../Framework/StoneInitialization.h"
+#include "../../Framework/Messages/MessageBroker.h"
+
+// From Orthanc framework
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/PngWriter.h>
+
+#include <boost/make_shared.hpp>
+
+#include <SDL.h>
+#include <stdio.h>
+
+static const unsigned int FONT_SIZE = 32;
+static const int LAYER_POSITION = 150;
+
+#define OPENGL_ENABLED 0
+
+void PrepareScene(OrthancStone::Scene2D& scene)
+{
+  using namespace OrthancStone;
+
+  // Texture of 2x2 size
+  {
+    Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false);
+    
+    uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0));
+    p[0] = 255;
+    p[1] = 0;
+    p[2] = 0;
+
+    p[3] = 0;
+    p[4] = 255;
+    p[5] = 0;
+
+    p = reinterpret_cast<uint8_t*>(i.GetRow(1));
+    p[0] = 0;
+    p[1] = 0;
+    p[2] = 255;
+
+    p[3] = 255;
+    p[4] = 0;
+    p[5] = 0;
+
+    scene.SetLayer(12, new ColorTextureSceneLayer(i));
+
+    std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
+    l->SetOrigin(-3, 2);
+    l->SetPixelSpacing(1.5, 1);
+    l->SetAngle(20.0 / 180.0 * M_PI);
+    scene.SetLayer(14, l.release());
+  }
+
+  // Texture of 1x1 size
+  {
+    Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false);
+    
+    uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0));
+    p[0] = 255;
+    p[1] = 0;
+    p[2] = 0;
+
+    std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
+    l->SetOrigin(-2, 1);
+    l->SetAngle(20.0 / 180.0 * M_PI);
+    scene.SetLayer(13, l.release());
+  }
+
+  // Some lines
+  {
+    std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer);
+
+    layer->SetThickness(10);
+
+    PolylineSceneLayer::Chain chain;
+    chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5));
+    chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5));
+    chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5));
+    chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5));
+    layer->AddChain(chain, true, 255, 0, 0);
+
+    chain.clear();
+    chain.push_back(ScenePoint2D(-5, -5));
+    chain.push_back(ScenePoint2D(5, -5));
+    chain.push_back(ScenePoint2D(5, 5));
+    chain.push_back(ScenePoint2D(-5, 5));
+    layer->AddChain(chain, true, 0, 255, 0);
+
+    double dy = 1.01;
+    chain.clear();
+    chain.push_back(ScenePoint2D(-4, -4));
+    chain.push_back(ScenePoint2D(4, -4 + dy));
+    chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy));
+    chain.push_back(ScenePoint2D(4, 2));
+    layer->AddChain(chain, false, 0, 0, 255);
+
+    scene.SetLayer(50, layer.release());
+  }
+
+  // Some text
+  {
+    std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+    layer->SetText("Hello");
+    scene.SetLayer(100, layer.release());
+  }
+}
+
+
+void TakeScreenshot(const std::string& target,
+                    const OrthancStone::Scene2D& scene,
+                    unsigned int canvasWidth,
+                    unsigned int canvasHeight)
+{
+  using namespace OrthancStone;
+  // Take a screenshot, then save it as PNG file
+  CairoCompositor compositor(scene, canvasWidth, canvasHeight);
+  compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE, Orthanc::Encoding_Latin1);
+  compositor.Refresh();
+
+  Orthanc::ImageAccessor canvas;
+  compositor.GetCanvas().GetReadOnlyAccessor(canvas);
+
+  Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
+  Orthanc::ImageProcessing::Convert(png, canvas);
+        
+  Orthanc::PngWriter writer;
+  writer.WriteToFile(target, png);
+}
+
+
+void HandleApplicationEvent(const SDL_Event& event,
+                            boost::shared_ptr<OrthancStone::ViewportController>& controller,
+                            boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>& activeTracker)
+{
+  using namespace OrthancStone;
+
+  Scene2D& scene = controller->GetScene();
+  IViewport& viewport = controller->GetViewport();
+
+  if (event.type == SDL_MOUSEMOTION)
+  {
+    int scancodeCount = 0;
+    const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
+
+    if (activeTracker.get() == NULL &&
+        SDL_SCANCODE_LCTRL < scancodeCount &&
+        keyboardState[SDL_SCANCODE_LCTRL])
+    {
+      // The "left-ctrl" key is down, while no tracker is present
+
+      PointerEvent e;
+      e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y));
+
+      ScenePoint2D p = e.GetMainPosition().Apply(scene.GetCanvasToSceneTransform());
+
+      char buf[64];
+      sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY());
+
+      if (scene.HasLayer(LAYER_POSITION))
+      {
+        TextSceneLayer& layer =
+          dynamic_cast<TextSceneLayer&>(scene.GetLayer(LAYER_POSITION));
+        layer.SetText(buf);
+        layer.SetPosition(p.GetX(), p.GetY());
+      }
+      else
+      {
+        std::unique_ptr<TextSceneLayer> 
+          layer(new TextSceneLayer);
+        layer->SetColor(0, 255, 0);
+        layer->SetText(buf);
+        layer->SetBorder(20);
+        layer->SetAnchor(BitmapAnchor_BottomCenter);
+        layer->SetPosition(p.GetX(), p.GetY());
+        scene.SetLayer(LAYER_POSITION, layer.release());
+      }
+    }
+    else
+    {
+      scene.DeleteLayer(LAYER_POSITION);
+    }
+  }
+  else if (event.type == SDL_MOUSEBUTTONDOWN)
+  {
+    PointerEvent e;
+    e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y));
+
+    switch (event.button.button)
+    {
+      case SDL_BUTTON_MIDDLE:
+        activeTracker = boost::make_shared<PanSceneTracker>(controller, e);
+        break;
+
+      case SDL_BUTTON_RIGHT:
+        activeTracker = boost::make_shared<ZoomSceneTracker>
+          (controller, e, viewport.GetCanvasHeight());
+        break;
+
+      case SDL_BUTTON_LEFT:
+        activeTracker = boost::make_shared<RotateSceneTracker>(controller, e);
+        break;
+
+      default:
+        break;
+    }
+  }
+  else if (event.type == SDL_KEYDOWN &&
+           event.key.repeat == 0 /* Ignore key bounce */)
+  {
+    switch (event.key.keysym.sym)
+    {
+      case SDLK_s:
+        controller->FitContent(viewport.GetCanvasWidth(), 
+                               viewport.GetCanvasHeight());
+        break;
+              
+      case SDLK_c:
+        TakeScreenshot("screenshot.png", scene, 
+                       viewport.GetCanvasWidth(), 
+                       viewport.GetCanvasHeight());
+        break;
+              
+      default:
+        break;
+    }
+  }
+}
+
+#if OPENGL_ENABLED==1
+static void GLAPIENTRY
+OpenGLMessageCallback(GLenum source,
+                      GLenum type,
+                      GLuint id,
+                      GLenum severity,
+                      GLsizei length,
+                      const GLchar* message,
+                      const void* userParam )
+{
+  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
+  {
+    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+            ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
+            type, severity, message );
+  }
+}
+#endif
+
+void Run(OrthancStone::MessageBroker& broker,
+         OrthancStone::SdlViewport& viewport)
+{
+  using namespace OrthancStone;
+  
+  boost::shared_ptr<ViewportController> controller(
+    new ViewportController(boost::make_shared<UndoStack>(), broker, viewport));
+  
+#if OPENGL_ENABLED==1
+  glEnable(GL_DEBUG_OUTPUT);
+  glDebugMessageCallback(OpenGLMessageCallback, 0);
+#endif
+
+  boost::shared_ptr<IFlexiblePointerTracker> tracker;
+
+  bool firstShown = true;
+  bool stop = false;
+  while (!stop)
+  {
+    viewport.Refresh();
+
+    SDL_Event event;
+    while (!stop &&
+           SDL_PollEvent(&event))
+    {
+      if (event.type == SDL_QUIT)
+      {
+        stop = true;
+        break;
+      }
+      else if (event.type == SDL_MOUSEMOTION)
+      {
+        if (tracker)
+        {
+          PointerEvent e;
+          e.AddPosition(viewport.GetPixelCenterCoordinates(
+            event.button.x, event.button.y));
+          tracker->PointerMove(e);
+        }
+      }
+      else if (event.type == SDL_MOUSEBUTTONUP)
+      {
+        if (tracker)
+        {
+          PointerEvent e;
+          e.AddPosition(viewport.GetPixelCenterCoordinates(
+            event.button.x, event.button.y));
+          tracker->PointerUp(e);
+          if(!tracker->IsAlive())
+            tracker.reset();
+        }
+      }
+      else if (event.type == SDL_WINDOWEVENT)
+      {
+        switch (event.window.event)
+        {
+          case SDL_WINDOWEVENT_SIZE_CHANGED:
+            tracker.reset();
+            viewport.UpdateSize(event.window.data1, event.window.data2);
+            break;
+
+          case SDL_WINDOWEVENT_SHOWN:
+            if (firstShown)
+            {
+              // Once the window is first shown, fit the content to its size
+              controller->FitContent(viewport.GetCanvasWidth(), viewport.GetCanvasHeight());
+              firstShown = false;
+            }
+            
+            break;
+
+          default:
+            break;
+        }
+      }
+      else if (event.type == SDL_KEYDOWN &&
+               event.key.repeat == 0 /* Ignore key bounce */)
+      {
+        switch (event.key.keysym.sym)
+        {
+          case SDLK_f:
+            viewport.GetWindow().ToggleMaximize();
+            break;
+              
+          case SDLK_q:
+            stop = true;
+            break;
+
+          default:
+            break;
+        }
+      }
+      
+      HandleApplicationEvent(event, controller, tracker);
+    }
+
+    SDL_Delay(1);
+  }
+}
+
+
+
+
+/**
+ * IMPORTANT: The full arguments to "main()" are needed for SDL on
+ * Windows. Otherwise, one gets the linking error "undefined reference
+ * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
+ **/
+int main(int argc, char* argv[])
+{
+  OrthancStone::StoneInitialize();
+  Orthanc::Logging::EnableInfoLevel(true);
+
+  try
+  {
+#if OPENGL_ENABLED==1
+    OrthancStone::SdlOpenGLViewport viewport("Hello", 1024, 768);
+#else
+    OrthancStone::SdlCairoViewport viewport("Hello", 1024, 768);
+#endif
+    PrepareScene(viewport.GetScene());
+
+    viewport.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
+                                     FONT_SIZE, Orthanc::Encoding_Latin1);
+    
+    OrthancStone::MessageBroker broker;
+    Run(broker, viewport);
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "EXCEPTION: " << e.What();
+  }
+
+  OrthancStone::StoneFinalize();
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Sdl/CMakeLists.txt	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,129 @@
+cmake_minimum_required(VERSION 2.8.3)
+
+#####################################################################
+## Configuration of the Orthanc framework
+#####################################################################
+
+# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it
+# must be the first inclusion
+include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/Version.cmake)
+
+if (ORTHANC_STONE_VERSION STREQUAL "mainline")
+  set(ORTHANC_FRAMEWORK_VERSION "mainline")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
+else()
+  set(ORTHANC_FRAMEWORK_VERSION "1.5.7")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
+endif()
+
+set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")")
+set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"")
+set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
+
+
+#####################################################################
+## Configuration of the Stone framework
+#####################################################################
+
+include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
+
+DownloadPackage(
+  "a24b8136b8f3bb93f166baf97d9328de"
+  "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip"
+  "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83")
+
+set(ORTHANC_STONE_APPLICATION_RESOURCES
+  UBUNTU_FONT  ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf
+  )
+
+SET(ENABLE_SDL_CONSOLE OFF CACHE BOOL "Enable the use of the MIT-licensed SDL_Console")
+SET(ENABLE_GOOGLE_TEST OFF)
+SET(ENABLE_LOCALE ON)
+SET(ENABLE_SDL ON)
+SET(ENABLE_WEB_CLIENT ON)
+SET(ORTHANC_SANDBOXED OFF)
+LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
+
+include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake)
+
+add_definitions(
+  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
+  )
+
+
+#####################################################################
+## Build the samples
+#####################################################################
+
+add_library(OrthancStone STATIC
+  ${ORTHANC_STONE_SOURCES}
+  )
+
+#
+# BasicScene
+# 
+
+add_executable(BasicScene
+  BasicScene.cpp
+  )
+
+target_link_libraries(BasicScene OrthancStone)
+
+#
+# TrackerSample
+# 
+
+LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSample.cpp")
+LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.cpp")
+LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.h")
+
+if (MSVC AND MSVC_VERSION GREATER 1700)
+  LIST(APPEND TRACKERSAMPLE_SOURCE "cpp.hint")
+endif()
+
+add_executable(TrackerSample
+  ${TRACKERSAMPLE_SOURCE}
+  )
+
+target_link_libraries(TrackerSample OrthancStone)
+
+#
+# Loader
+# 
+
+add_executable(Loader
+  Loader.cpp
+  )
+
+target_link_libraries(Loader OrthancStone)
+
+#
+# FusionMprSdl
+# 
+
+add_executable(FusionMprSdl
+  FusionMprSdl.cpp
+  FusionMprSdl.h
+)
+
+target_link_libraries(FusionMprSdl OrthancStone)
+
+#
+# Multiplatform Basic Scene
+#
+
+LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.cpp")
+LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.h")
+LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/mainSdl.cpp")
+
+if (MSVC AND MSVC_VERSION GREATER 1700)
+  LIST(APPEND MP_BASIC_SCENE_SOURCE "cpp.hint")
+endif()
+
+add_executable(MpBasicScene
+  ${MP_BASIC_SCENE_SOURCE}
+  )
+
+target_include_directories(MpBasicScene PUBLIC ${ORTHANC_STONE_ROOT})
+target_link_libraries(MpBasicScene OrthancStone)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Sdl/FusionMprSdl.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,805 @@
+/**
+ * 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/>.
+ **/
+
+#include "FusionMprSdl.h"
+
+#include "../../Framework/OpenGL/SdlOpenGLContext.h"
+
+#include "../../Framework/StoneInitialization.h"
+
+#include "../../Framework/Scene2D/CairoCompositor.h"
+#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Scene2D/PanSceneTracker.h"
+#include "../../Framework/Scene2D/ZoomSceneTracker.h"
+#include "../../Framework/Scene2D/RotateSceneTracker.h"
+
+#include "../../Framework/Scene2DViewport/UndoStack.h"
+#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h"
+#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h"
+#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
+#include "../../Framework/Scene2DViewport/MeasureTool.h"
+#include "../../Framework/Scene2DViewport/PredeclaredTypes.h"
+
+#include "../../Framework/Volumes/VolumeSceneLayerSource.h"
+
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/PngWriter.h>
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/make_shared.hpp>
+
+#include <stdio.h>
+#include "../../Framework/Oracle/GetOrthancWebViewerJpegCommand.h"
+#include "../../Framework/Oracle/ThreadedOracle.h"
+#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
+#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h"
+#include "../../Framework/Loaders/DicomStructureSetLoader.h"
+#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h"
+#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h"
+#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h"
+#include "Core/SystemToolbox.h"
+
+namespace OrthancStone
+{
+  const char* FusionMprMeasureToolToString(size_t i)
+  {
+    static const char* descs[] = {
+      "FusionMprGuiTool_Rotate",
+      "FusionMprGuiTool_Pan",
+      "FusionMprGuiTool_Zoom",
+      "FusionMprGuiTool_LineMeasure",
+      "FusionMprGuiTool_CircleMeasure",
+      "FusionMprGuiTool_AngleMeasure",
+      "FusionMprGuiTool_EllipseMeasure",
+      "FusionMprGuiTool_LAST"
+    };
+    if (i >= FusionMprGuiTool_LAST)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index");
+    }
+    return descs[i];
+  }
+
+  Scene2D& FusionMprSdlApp::GetScene()
+  {
+    return controller_->GetScene();
+  }
+
+  const Scene2D& FusionMprSdlApp::GetScene() const
+  {
+    return controller_->GetScene();
+  }
+
+  void FusionMprSdlApp::SelectNextTool()
+  {
+    currentTool_ = static_cast<FusionMprGuiTool>(currentTool_ + 1);
+    if (currentTool_ == FusionMprGuiTool_LAST)
+      currentTool_ = static_cast<FusionMprGuiTool>(0);;
+    printf("Current tool is now: %s\n", FusionMprMeasureToolToString(currentTool_));
+  }
+
+  void FusionMprSdlApp::DisplayInfoText()
+  {
+    // do not try to use stuff too early!
+    ICompositor* pCompositor = &(viewport_.GetCompositor());
+    if (pCompositor == NULL)
+      return;
+
+    std::stringstream msg;
+	
+	for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin();
+		kv != infoTextMap_.end(); ++kv)
+    {
+      msg << kv->first << " : " << kv->second << std::endl;
+    }
+	std::string msgS = msg.str();
+
+    TextSceneLayer* layerP = NULL;
+    if (GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX))
+    {
+      TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>(
+        GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX));
+      layerP = &layer;
+    }
+    else
+    {
+      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+      layerP = layer.get();
+      layer->SetColor(0, 255, 0);
+      layer->SetFontIndex(1);
+      layer->SetBorder(20);
+      layer->SetAnchor(BitmapAnchor_TopLeft);
+      //layer->SetPosition(0,0);
+      GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release());
+    }
+    // position the fixed info text in the upper right corner
+    layerP->SetText(msgS.c_str());
+    double cX = viewport_.GetCompositor().GetCanvasWidth() * (-0.5);
+    double cY = viewport_.GetCompositor().GetCanvasHeight() * (-0.5);
+    GetScene().GetCanvasToSceneTransform().Apply(cX,cY);
+    layerP->SetPosition(cX, cY);
+  }
+
+  void FusionMprSdlApp::DisplayFloatingCtrlInfoText(const PointerEvent& e)
+  {
+    ScenePoint2D p = e.GetMainPosition().Apply(GetScene().GetCanvasToSceneTransform());
+
+    char buf[128];
+    sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", 
+      p.GetX(), p.GetY(), 
+      e.GetMainPosition().GetX(), e.GetMainPosition().GetY());
+
+    if (GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX))
+    {
+      TextSceneLayer& layer =
+        dynamic_cast<TextSceneLayer&>(GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX));
+      layer.SetText(buf);
+      layer.SetPosition(p.GetX(), p.GetY());
+    }
+    else
+    {
+      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+      layer->SetColor(0, 255, 0);
+      layer->SetText(buf);
+      layer->SetBorder(20);
+      layer->SetAnchor(BitmapAnchor_BottomCenter);
+      layer->SetPosition(p.GetX(), p.GetY());
+      GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release());
+    }
+  }
+
+  void FusionMprSdlApp::HideInfoText()
+  {
+    GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX);
+  }
+
+  void FusionMprSdlApp::HandleApplicationEvent(
+    const SDL_Event & event)
+  {
+    DisplayInfoText();
+
+    if (event.type == SDL_MOUSEMOTION)
+    {
+      int scancodeCount = 0;
+      const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
+
+      if (activeTracker_.get() == NULL &&
+        SDL_SCANCODE_LALT < scancodeCount &&
+        keyboardState[SDL_SCANCODE_LALT])
+      {
+        // The "left-ctrl" key is down, while no tracker is present
+        // Let's display the info text
+        PointerEvent e;
+        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
+          event.button.x, event.button.y));
+
+        DisplayFloatingCtrlInfoText(e);
+      }
+      else
+      {
+        HideInfoText();
+        //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)";
+        if (activeTracker_.get() != NULL)
+        {
+          //LOG(TRACE) << "(activeTracker_.get() != NULL)";
+          PointerEvent e;
+          e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
+            event.button.x, event.button.y));
+          
+          //LOG(TRACE) << "event.button.x = " << event.button.x << "     " <<
+          //  "event.button.y = " << event.button.y;
+          LOG(TRACE) << "activeTracker_->PointerMove(e); " <<
+            e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY();
+          
+          activeTracker_->PointerMove(e);
+          if (!activeTracker_->IsAlive())
+            activeTracker_.reset();
+        }
+      }
+    }
+    else if (event.type == SDL_MOUSEBUTTONUP)
+    {
+      if (activeTracker_)
+      {
+        PointerEvent e;
+        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
+        activeTracker_->PointerUp(e);
+        if (!activeTracker_->IsAlive())
+          activeTracker_.reset();
+      }
+    }
+    else if (event.type == SDL_MOUSEBUTTONDOWN)
+    {
+      PointerEvent e;
+      e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
+        event.button.x, event.button.y));
+      if (activeTracker_)
+      {
+        activeTracker_->PointerDown(e);
+        if (!activeTracker_->IsAlive())
+          activeTracker_.reset();
+      }
+      else
+      {
+        // we ATTEMPT to create a tracker if need be
+        activeTracker_ = CreateSuitableTracker(event, e);
+      }
+    }
+    else if (event.type == SDL_KEYDOWN &&
+      event.key.repeat == 0 /* Ignore key bounce */)
+    {
+      switch (event.key.keysym.sym)
+      {
+      case SDLK_ESCAPE:
+        if (activeTracker_)
+        {
+          activeTracker_->Cancel();
+          if (!activeTracker_->IsAlive())
+            activeTracker_.reset();
+        }
+        break;
+
+      case SDLK_t:
+        if (!activeTracker_)
+          SelectNextTool();
+        else
+        {
+          LOG(WARNING) << "You cannot change the active tool when an interaction"
+            " is taking place";
+        }
+        break;
+      case SDLK_s:
+        controller_->FitContent(viewport_.GetCompositor().GetCanvasWidth(),
+          viewport_.GetCompositor().GetCanvasHeight());
+        break;
+
+      case SDLK_z:
+        LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
+        if (event.key.keysym.mod & KMOD_CTRL)
+        {
+          if (controller_->CanUndo())
+          {
+            LOG(TRACE) << "Undoing...";
+            controller_->Undo();
+          }
+          else
+          {
+            LOG(WARNING) << "Nothing to undo!!!";
+          }
+        }
+        break;
+
+      case SDLK_y:
+        LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
+        if (event.key.keysym.mod & KMOD_CTRL)
+        {
+          if (controller_->CanRedo())
+          {
+            LOG(TRACE) << "Redoing...";
+            controller_->Redo();
+          }
+          else
+          {
+            LOG(WARNING) << "Nothing to redo!!!";
+          }
+        }
+        break;
+
+      case SDLK_c:
+        TakeScreenshot(
+          "screenshot.png",
+          viewport_.GetCompositor().GetCanvasWidth(),
+          viewport_.GetCompositor().GetCanvasHeight());
+        break;
+
+      default:
+        break;
+      }
+    }
+  }
+
+
+  void FusionMprSdlApp::OnSceneTransformChanged(
+    const ViewportController::SceneTransformChanged& message)
+  {
+    DisplayInfoText();
+  }
+
+  boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::CreateSuitableTracker(
+    const SDL_Event & event,
+    const PointerEvent & e)
+  {
+    using namespace Orthanc;
+
+    switch (event.button.button)
+    {
+    case SDL_BUTTON_MIDDLE:
+      return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker
+        (controller_, e));
+
+    case SDL_BUTTON_RIGHT:
+      return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker
+        (controller_, e, viewport_.GetCompositor().GetCanvasHeight()));
+
+    case SDL_BUTTON_LEFT:
+    {
+      //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:";
+      // TODO: we need to iterate on the set of measuring tool and perform
+      // a hit test to check if a tracker needs to be created for edition.
+      // Otherwise, depending upon the active tool, we might want to create
+      // a "measuring tool creation" tracker
+
+      // TODO: if there are conflicts, we should prefer a tracker that 
+      // pertains to the type of measuring tool currently selected (TBD?)
+      boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e);
+
+      if (hitTestTracker != NULL)
+      {
+        //LOG(TRACE) << "hitTestTracker != NULL";
+        return hitTestTracker;
+      }
+      else
+      {
+        switch (currentTool_)
+        {
+        case FusionMprGuiTool_Rotate:
+          //LOG(TRACE) << "Creating RotateSceneTracker";
+          return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker(
+            controller_, e));
+        case FusionMprGuiTool_Pan:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker(
+            controller_, e));
+        case FusionMprGuiTool_Zoom:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker(
+            controller_, e, viewport_.GetCompositor().GetCanvasHeight()));
+        //case GuiTool_AngleMeasure:
+        //  return new AngleMeasureTracker(GetScene(), e);
+        //case GuiTool_CircleMeasure:
+        //  return new CircleMeasureTracker(GetScene(), e);
+        //case GuiTool_EllipseMeasure:
+        //  return new EllipseMeasureTracker(GetScene(), e);
+        case FusionMprGuiTool_LineMeasure:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker(
+            IObserver::GetBroker(), controller_, e));
+        case FusionMprGuiTool_AngleMeasure:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker(
+            IObserver::GetBroker(), controller_, e));
+        case FusionMprGuiTool_CircleMeasure:
+          LOG(ERROR) << "Not implemented yet!";
+          return boost::shared_ptr<IFlexiblePointerTracker>();
+        case FusionMprGuiTool_EllipseMeasure:
+          LOG(ERROR) << "Not implemented yet!";
+          return boost::shared_ptr<IFlexiblePointerTracker>();
+        default:
+          throw OrthancException(ErrorCode_InternalError, "Wrong tool!");
+        }
+      }
+    }
+    default:
+      return boost::shared_ptr<IFlexiblePointerTracker>();
+    }
+  }
+
+
+  FusionMprSdlApp::FusionMprSdlApp(MessageBroker& broker)
+    : IObserver(broker)
+    , broker_(broker)
+    , oracleObservable_(broker)
+    , oracle_(*this)
+    , currentTool_(FusionMprGuiTool_Rotate)
+    , undoStack_(new UndoStack)
+    , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled
+  {
+    //oracleObservable.RegisterObserverCallback
+    //(new Callable
+    //  <FusionMprSdlApp, SleepOracleCommand::TimeoutMessage>(*this, &FusionMprSdlApp::Handle));
+
+    //oracleObservable.RegisterObserverCallback
+    //(new Callable
+    //  <Toto, GetOrthancImageCommand::SuccessMessage>(*this, &FusionMprSdlApp::Handle));
+
+    //oracleObservable.RegisterObserverCallback
+    //(new Callable
+    //  <FusionMprSdlApp, GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &ToFusionMprSdlAppto::Handle));
+
+    oracleObservable_.RegisterObserverCallback
+    (new Callable
+      <FusionMprSdlApp, OracleCommandExceptionMessage>(*this, &FusionMprSdlApp::Handle));
+    
+    controller_ = boost::shared_ptr<ViewportController>(
+      new ViewportController(undoStack_, broker_, viewport_));
+
+    controller_->RegisterObserverCallback(
+      new Callable<FusionMprSdlApp, ViewportController::SceneTransformChanged>
+      (*this, &FusionMprSdlApp::OnSceneTransformChanged));
+
+    TEXTURE_2x2_1_ZINDEX = 1;
+    TEXTURE_1x1_ZINDEX = 2;
+    TEXTURE_2x2_2_ZINDEX = 3;
+    LINESET_1_ZINDEX = 4;
+    LINESET_2_ZINDEX = 5;
+    FLOATING_INFOTEXT_LAYER_ZINDEX = 6;
+    FIXED_INFOTEXT_LAYER_ZINDEX = 7;
+  }
+
+  void FusionMprSdlApp::PrepareScene()
+  {
+    // Texture of 2x2 size
+    {
+      Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false);
+
+      uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0));
+      p[0] = 255;
+      p[1] = 0;
+      p[2] = 0;
+
+      p[3] = 0;
+      p[4] = 255;
+      p[5] = 0;
+
+      p = reinterpret_cast<uint8_t*>(i.GetRow(1));
+      p[0] = 0;
+      p[1] = 0;
+      p[2] = 255;
+
+      p[3] = 255;
+      p[4] = 0;
+      p[5] = 0;
+
+      GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i));
+    }
+  }
+
+  void FusionMprSdlApp::DisableTracker()
+  {
+    if (activeTracker_)
+    {
+      activeTracker_->Cancel();
+      activeTracker_.reset();
+    }
+  }
+
+  void FusionMprSdlApp::TakeScreenshot(const std::string& target,
+    unsigned int canvasWidth,
+    unsigned int canvasHeight)
+  {
+    CairoCompositor compositor(GetScene(), canvasWidth, canvasHeight);
+    compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1);
+    compositor.Refresh();
+
+    Orthanc::ImageAccessor canvas;
+    compositor.GetCanvas().GetReadOnlyAccessor(canvas);
+
+    Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
+    Orthanc::ImageProcessing::Convert(png, canvas);
+
+    Orthanc::PngWriter writer;
+    writer.WriteToFile(target, png);
+  }
+
+
+  boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::TrackerHitTest(const PointerEvent & e)
+  {
+    // std::vector<boost::shared_ptr<MeasureTool>> measureTools_;
+    return boost::shared_ptr<IFlexiblePointerTracker>();
+  }
+
+  static void GLAPIENTRY
+    OpenGLMessageCallback(GLenum source,
+      GLenum type,
+      GLuint id,
+      GLenum severity,
+      GLsizei length,
+      const GLchar* message,
+      const void* userParam)
+  {
+    if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
+    {
+      fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+        (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
+        type, severity, message);
+    }
+  }
+
+  static bool g_stopApplication = false;
+  
+
+  void FusionMprSdlApp::Handle(const DicomVolumeImage::GeometryReadyMessage& message)
+  {
+    printf("Geometry ready\n");
+
+    //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry();
+    //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry();
+    plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry();
+    plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f));
+
+    //Refresh();
+  }
+
+
+  void FusionMprSdlApp::Handle(const OracleCommandExceptionMessage& message)
+  {
+    printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType());
+
+    switch (message.GetCommand().GetType())
+    {
+    case IOracleCommand::Type_GetOrthancWebViewerJpeg:
+      printf("URI: [%s]\n", dynamic_cast<const GetOrthancWebViewerJpegCommand&>
+        (message.GetCommand()).GetUri().c_str());
+      break;
+
+    default:
+      break;
+    }
+  }
+
+  void FusionMprSdlApp::SetVolume1(int depth,
+    const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
+    OrthancStone::ILayerStyleConfigurator* style)
+  {
+    source1_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume));
+
+    if (style != NULL)
+    {
+      source1_->SetConfigurator(style);
+    }
+  }
+
+  void FusionMprSdlApp::SetVolume2(int depth,
+    const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
+    OrthancStone::ILayerStyleConfigurator* style)
+  {
+    source2_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume));
+
+    if (style != NULL)
+    {
+      source2_->SetConfigurator(style);
+    }
+  }
+
+  void FusionMprSdlApp::SetStructureSet(int depth,
+    const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume)
+  {
+    source3_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume));
+  }
+  
+  void FusionMprSdlApp::Run()
+  {
+    // False means we do NOT let Windows treat this as a legacy application
+    // that needs to be scaled
+    controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight());
+
+    glEnable(GL_DEBUG_OUTPUT);
+    glDebugMessageCallback(OpenGLMessageCallback, 0);
+
+    viewport_.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
+      FONT_SIZE_0, Orthanc::Encoding_Latin1);
+    viewport_.GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
+      FONT_SIZE_1, Orthanc::Encoding_Latin1);
+
+
+    //////// from loader
+    {
+      Orthanc::WebServiceParameters p;
+      //p.SetUrl("http://localhost:8043/");
+      p.SetCredentials("orthanc", "orthanc");
+      oracle_.SetOrthancParameters(p);
+    }
+
+    //////// from Run
+
+    boost::shared_ptr<DicomVolumeImage>  ct(new DicomVolumeImage);
+    boost::shared_ptr<DicomVolumeImage>  dose(new DicomVolumeImage);
+
+
+    boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> ctLoader;
+    boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader;
+    boost::shared_ptr<DicomStructureSetLoader>  rtstructLoader;
+
+    {
+      ctLoader.reset(new OrthancSeriesVolumeProgressiveLoader(ct, oracle_, oracleObservable_));
+      doseLoader.reset(new OrthancMultiframeVolumeLoader(dose, oracle_, oracleObservable_));
+      rtstructLoader.reset(new DicomStructureSetLoader(oracle_, oracleObservable_));
+    }
+
+    //toto->SetReferenceLoader(*ctLoader);
+    //doseLoader->RegisterObserverCallback
+    //(new Callable
+    //  <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle));
+    ctLoader->RegisterObserverCallback
+    (new Callable
+      <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle));
+
+    this->SetVolume1(0, ctLoader, new GrayscaleStyleConfigurator);
+
+    {
+      std::unique_ptr<LookupTableStyleConfigurator> config(new LookupTableStyleConfigurator);
+      config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT);
+
+      boost::shared_ptr<DicomVolumeImageMPRSlicer> tmp(new DicomVolumeImageMPRSlicer(dose));
+      this->SetVolume2(1, tmp, config.release());
+    }
+
+    this->SetStructureSet(2, rtstructLoader);
+
+#if 1
+    /*
+    BGO data
+    http://localhost:8042/twiga-orthanc-viewer-demo/twiga-orthanc-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa
+    &
+    dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb
+    &
+    struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9
+    */
+    ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
+    doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb");  // RT-DOSE
+    rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
+#else
+    //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618");  // CT
+    //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6");  // RT-DOSE
+    //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6");  // RT-STRUCT
+
+    // 2017-05-16
+    ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
+    doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad");  // RT-DOSE
+    rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
+#endif
+
+    oracle_.Start();
+
+//// END from loader
+
+    while (!g_stopApplication)
+    {
+      viewport_.GetCompositor().Refresh();
+
+//////// from loader
+      if (source1_.get() != NULL)
+      {
+        source1_->Update(plane_);
+      }
+
+      if (source2_.get() != NULL)
+      {
+        source2_->Update(plane_);
+      }
+
+      if (source3_.get() != NULL)
+      {
+        source3_->Update(plane_);
+      }
+//// END from loader
+
+      SDL_Event event;
+      while (!g_stopApplication && SDL_PollEvent(&event))
+      {
+        if (event.type == SDL_QUIT)
+        {
+          g_stopApplication = true;
+          break;
+        }
+        else if (event.type == SDL_WINDOWEVENT &&
+          event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
+        {
+          DisableTracker(); // was: tracker.reset(NULL);
+        }
+        else if (event.type == SDL_KEYDOWN &&
+          event.key.repeat == 0 /* Ignore key bounce */)
+        {
+          switch (event.key.keysym.sym)
+          {
+          case SDLK_f:
+            viewport_.GetWindow().ToggleMaximize();
+            break;
+
+          case SDLK_s:
+            controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight());
+            break;
+
+          case SDLK_q:
+            g_stopApplication = true;
+            break;
+          default:
+            break;
+          }
+        }
+        HandleApplicationEvent(event);
+      }
+      SDL_Delay(1);
+    }
+
+    //// from loader
+
+    //Orthanc::SystemToolbox::ServerBarrier();
+
+    /**
+     * WARNING => The oracle must be stopped BEFORE the objects using
+     * it are destroyed!!! This forces to wait for the completion of
+     * the running callback methods. Otherwise, the callbacks methods
+     * might still be running while their parent object is destroyed,
+     * resulting in crashes. This is very visible if adding a sleep(),
+     * as in (*).
+     **/
+
+    oracle_.Stop();
+    //// END from loader
+  }
+
+  void FusionMprSdlApp::SetInfoDisplayMessage(
+    std::string key, std::string value)
+  {
+    if (value == "")
+      infoTextMap_.erase(key);
+    else
+      infoTextMap_[key] = value;
+    DisplayInfoText();
+  }
+
+}
+
+
+boost::weak_ptr<OrthancStone::FusionMprSdlApp> g_app;
+
+void FusionMprSdl_SetInfoDisplayMessage(std::string key, std::string value)
+{
+  boost::shared_ptr<OrthancStone::FusionMprSdlApp> app = g_app.lock();
+  if (app)
+  {
+    app->SetInfoDisplayMessage(key, value);
+  }
+}
+
+/**
+ * IMPORTANT: The full arguments to "main()" are needed for SDL on
+ * Windows. Otherwise, one gets the linking error "undefined reference
+ * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
+ **/
+int main(int argc, char* argv[])
+{
+  using namespace OrthancStone;
+
+  StoneInitialize();
+  Orthanc::Logging::EnableInfoLevel(true);
+//  Orthanc::Logging::EnableTraceLevel(true);
+
+  try
+  {
+    OrthancStone::MessageBroker broker;
+    boost::shared_ptr<FusionMprSdlApp> app(new FusionMprSdlApp(broker));
+    g_app = app;
+    app->PrepareScene();
+    app->Run();
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "EXCEPTION: " << e.What();
+  }
+
+  StoneFinalize();
+
+  return 0;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Sdl/FusionMprSdl.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,206 @@
+/**
+ * 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/>.
+ **/
+
+#include "../../Framework/Viewport/SdlViewport.h"
+
+#include "../../Framework/Messages/IObserver.h"
+#include "../../Framework/Messages/IMessageEmitter.h"
+#include "../../Framework/Oracle/OracleCommandExceptionMessage.h"
+#include "../../Framework/Scene2DViewport/ViewportController.h"
+#include "../../Framework/Volumes/DicomVolumeImage.h"
+#include "../../Framework/Oracle/ThreadedOracle.h"
+
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/thread.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <SDL.h>
+
+namespace OrthancStone
+{
+  class OpenGLCompositor;
+  class IVolumeSlicer;
+  class ILayerStyleConfigurator;
+  class DicomStructureSetLoader;
+  class IOracle;
+  class ThreadedOracle;
+  class VolumeSceneLayerSource;
+  class NativeFusionMprApplicationContext;
+  class SdlOpenGLViewport;
+   
+  enum FusionMprGuiTool
+  {
+    FusionMprGuiTool_Rotate = 0,
+    FusionMprGuiTool_Pan,
+    FusionMprGuiTool_Zoom,
+    FusionMprGuiTool_LineMeasure,
+    FusionMprGuiTool_CircleMeasure,
+    FusionMprGuiTool_AngleMeasure,
+    FusionMprGuiTool_EllipseMeasure,
+    FusionMprGuiTool_LAST
+  };
+
+  const char* MeasureToolToString(size_t i);
+
+  static const unsigned int FONT_SIZE_0 = 32;
+  static const unsigned int FONT_SIZE_1 = 24;
+
+  class Scene2D;
+  class UndoStack;
+
+  /**
+  This application subclasses IMessageEmitter to use a mutex before forwarding Oracle messages (that
+  can be sent from multiple threads)
+  */
+  class FusionMprSdlApp : public IObserver
+    , public boost::enable_shared_from_this<FusionMprSdlApp>
+    , public IMessageEmitter
+  {
+  public:
+    // 12 because.
+    FusionMprSdlApp(MessageBroker& broker);
+
+    void PrepareScene();
+    void Run();
+    void SetInfoDisplayMessage(std::string key, std::string value);
+    void DisableTracker();
+
+    Scene2D&       GetScene();
+    const Scene2D& GetScene() const;
+
+    void HandleApplicationEvent(const SDL_Event& event);
+
+    /**
+    This method is called when the scene transform changes. It allows to
+    recompute the visual elements whose content depend upon the scene transform
+    */
+    void OnSceneTransformChanged(
+      const ViewportController::SceneTransformChanged& message);
+
+
+    virtual void EmitMessage(const IObserver& observer,
+      const IMessage& message) ORTHANC_OVERRIDE
+    {
+      try
+      {
+        boost::unique_lock<boost::shared_mutex>  lock(mutex_);
+        oracleObservable_.EmitMessage(observer, message);
+      }
+      catch (Orthanc::OrthancException& e)
+      {
+        LOG(ERROR) << "Exception while emitting a message: " << e.What();
+        throw;
+      }
+    }
+    
+  private:
+#if 1
+    // if threaded (not wasm)
+    MessageBroker& broker_;
+    IObservable oracleObservable_;
+    ThreadedOracle oracle_;
+    boost::shared_mutex mutex_; // to serialize messages from the ThreadedOracle
+#endif
+
+    void SelectNextTool();
+
+    /**
+    This returns a random point in the canvas part of the scene, but in
+    scene coordinates
+    */
+    ScenePoint2D GetRandomPointInScene() const;
+
+    boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e);
+
+    boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker(
+      const SDL_Event& event,
+      const PointerEvent& e);
+
+    void TakeScreenshot(
+      const std::string& target,
+      unsigned int canvasWidth,
+      unsigned int canvasHeight);
+
+    /**
+      This adds the command at the top of the undo stack
+    */
+    void Commit(boost::shared_ptr<TrackerCommand> cmd);
+    void Undo();
+    void Redo();
+
+
+    // TODO private
+    void Handle(const DicomVolumeImage::GeometryReadyMessage& message);
+    void Handle(const OracleCommandExceptionMessage& message);
+
+    void SetVolume1(
+      int depth,
+      const boost::shared_ptr<IVolumeSlicer>& volume,
+      ILayerStyleConfigurator* style);
+    
+    void SetVolume2(
+      int depth,
+      const boost::shared_ptr<IVolumeSlicer>& volume,
+      ILayerStyleConfigurator* style);
+
+    void SetStructureSet(
+      int depth, 
+      const boost::shared_ptr<DicomStructureSetLoader>& volume);
+
+
+
+  private:
+    void DisplayFloatingCtrlInfoText(const PointerEvent& e);
+    void DisplayInfoText();
+    void HideInfoText();
+
+  private:
+    CoordinateSystem3D  plane_;
+
+    boost::shared_ptr<VolumeSceneLayerSource>  source1_, source2_, source3_;
+
+    /**
+    WARNING: the measuring tools do store a reference to the scene, and it
+    paramount that the scene gets destroyed AFTER the measurement tools.
+    */
+    boost::shared_ptr<ViewportController> controller_;
+
+    std::map<std::string, std::string> infoTextMap_;
+    boost::shared_ptr<IFlexiblePointerTracker> activeTracker_;
+
+    //static const int LAYER_POSITION = 150;
+
+    int TEXTURE_2x2_1_ZINDEX;
+    int TEXTURE_1x1_ZINDEX;
+    int TEXTURE_2x2_2_ZINDEX;
+    int LINESET_1_ZINDEX;
+    int LINESET_2_ZINDEX;
+    int FLOATING_INFOTEXT_LAYER_ZINDEX;
+    int FIXED_INFOTEXT_LAYER_ZINDEX;
+
+    FusionMprGuiTool currentTool_;
+    boost::shared_ptr<UndoStack> undoStack_;
+    SdlOpenGLViewport viewport_;
+  };
+
+}
+
+
+ 
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Sdl/Loader.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,518 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "../../Framework/Loaders/DicomStructureSetLoader.h"
+#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h"
+#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
+#include "../../Framework/Oracle/SleepOracleCommand.h"
+#include "../../Framework/Oracle/ThreadedOracle.h"
+#include "../../Framework/Scene2D/CairoCompositor.h"
+#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h"
+#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h"
+#include "../../Framework/StoneInitialization.h"
+#include "../../Framework/Volumes/VolumeSceneLayerSource.h"
+#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h"
+#include "../../Framework/Volumes/DicomVolumeImageReslicer.h"
+
+// From Orthanc framework
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/PngWriter.h>
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <Core/SystemToolbox.h>
+
+
+namespace OrthancStone
+{
+  class NativeApplicationContext : public IMessageEmitter
+  {
+  private:
+    boost::shared_mutex  mutex_;
+    MessageBroker        broker_;
+    IObservable          oracleObservable_;
+
+  public:
+    NativeApplicationContext() :
+      oracleObservable_(broker_)
+    {
+    }
+
+
+    virtual void EmitMessage(const IObserver& observer,
+                             const IMessage& message) ORTHANC_OVERRIDE
+    {
+      try
+      {
+        boost::unique_lock<boost::shared_mutex>  lock(mutex_);
+        oracleObservable_.EmitMessage(observer, message);
+      }
+      catch (Orthanc::OrthancException& e)
+      {
+        LOG(ERROR) << "Exception while emitting a message: " << e.What();
+      }
+    }
+
+
+    class ReaderLock : public boost::noncopyable
+    {
+    private:
+      NativeApplicationContext&                that_;
+      boost::shared_lock<boost::shared_mutex>  lock_;
+
+    public:
+      ReaderLock(NativeApplicationContext& that) : 
+        that_(that),
+        lock_(that.mutex_)
+      {
+      }
+    };
+
+
+    class WriterLock : public boost::noncopyable
+    {
+    private:
+      NativeApplicationContext&                that_;
+      boost::unique_lock<boost::shared_mutex>  lock_;
+
+    public:
+      WriterLock(NativeApplicationContext& that) : 
+        that_(that),
+        lock_(that.mutex_)
+      {
+      }
+
+      MessageBroker& GetBroker() 
+      {
+        return that_.broker_;
+      }
+
+      IObservable& GetOracleObservable()
+      {
+        return that_.oracleObservable_;
+      }
+    };
+  };
+}
+
+
+
+class Toto : public OrthancStone::IObserver
+{
+private:
+  OrthancStone::CoordinateSystem3D  plane_;
+  OrthancStone::IOracle&            oracle_;
+  OrthancStone::Scene2D             scene_;
+  std::unique_ptr<OrthancStone::VolumeSceneLayerSource>  source1_, source2_, source3_;
+
+
+  void Refresh()
+  {
+    if (source1_.get() != NULL)
+    {
+      source1_->Update(plane_);
+    }
+      
+    if (source2_.get() != NULL)
+    {
+      source2_->Update(plane_);
+    }
+
+    if (source3_.get() != NULL)
+    {
+      source3_->Update(plane_);
+    }
+
+    scene_.FitContent(1024, 768);
+      
+    {
+      OrthancStone::CairoCompositor compositor(scene_, 1024, 768);
+      compositor.Refresh();
+        
+      Orthanc::ImageAccessor accessor;
+      compositor.GetCanvas().GetReadOnlyAccessor(accessor);
+
+      Orthanc::Image tmp(Orthanc::PixelFormat_RGB24, accessor.GetWidth(), accessor.GetHeight(), false);
+      Orthanc::ImageProcessing::Convert(tmp, accessor);
+        
+      static unsigned int count = 0;
+      char buf[64];
+      sprintf(buf, "scene-%06d.png", count++);
+        
+      Orthanc::PngWriter writer;
+      writer.WriteToFile(buf, tmp);
+    }
+  }
+
+  
+  void Handle(const OrthancStone::DicomVolumeImage::GeometryReadyMessage& message)
+  {
+    printf("Geometry ready\n");
+    
+    plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry();
+    //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry();
+    //plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry();
+    plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f));
+
+    Refresh();
+  }
+  
+  
+  void Handle(const OrthancStone::SleepOracleCommand::TimeoutMessage& message)
+  {
+    if (message.GetOrigin().HasPayload())
+    {
+      printf("TIMEOUT! %d\n", dynamic_cast<const Orthanc::SingleValueObject<unsigned int>& >(message.GetOrigin().GetPayload()).GetValue());
+    }
+    else
+    {
+      printf("TIMEOUT\n");
+
+      Refresh();
+
+      /**
+       * The sleep() leads to a crash if the oracle is still running,
+       * while this object is destroyed. Always stop the oracle before
+       * destroying active objects.  (*)
+       **/
+      // boost::this_thread::sleep(boost::posix_time::seconds(2));
+
+      oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(message.GetOrigin().GetDelay()));
+    }
+  }
+
+  void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message)
+  {
+    Json::Value v;
+    message.ParseJsonBody(v);
+
+    printf("ICI [%s]\n", v.toStyledString().c_str());
+  }
+
+  void Handle(const OrthancStone::GetOrthancImageCommand::SuccessMessage& message)
+  {
+    printf("IMAGE %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight());
+  }
+
+  void Handle(const OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage& message)
+  {
+    printf("WebViewer %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight());
+  }
+
+  void Handle(const OrthancStone::OracleCommandExceptionMessage& message)
+  {
+    printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType());
+
+    switch (message.GetCommand().GetType())
+    {
+      case OrthancStone::IOracleCommand::Type_GetOrthancWebViewerJpeg:
+        printf("URI: [%s]\n", dynamic_cast<const OrthancStone::GetOrthancWebViewerJpegCommand&>
+               (message.GetCommand()).GetUri().c_str());
+        break;
+      
+      default:
+        break;
+    }
+  }
+
+public:
+  Toto(OrthancStone::IOracle& oracle,
+       OrthancStone::IObservable& oracleObservable) :
+    IObserver(oracleObservable.GetBroker()),
+    oracle_(oracle)
+  {
+    oracleObservable.RegisterObserverCallback
+      (new OrthancStone::Callable
+       <Toto, OrthancStone::SleepOracleCommand::TimeoutMessage>(*this, &Toto::Handle));
+
+    oracleObservable.RegisterObserverCallback
+      (new OrthancStone::Callable
+       <Toto, OrthancStone::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle));
+
+    oracleObservable.RegisterObserverCallback
+      (new OrthancStone::Callable
+       <Toto, OrthancStone::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle));
+
+    oracleObservable.RegisterObserverCallback
+      (new OrthancStone::Callable
+       <Toto, OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle));
+
+    oracleObservable.RegisterObserverCallback
+      (new OrthancStone::Callable
+       <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle));
+  }
+
+  void SetReferenceLoader(OrthancStone::IObservable& loader)
+  {
+    loader.RegisterObserverCallback
+      (new OrthancStone::Callable
+       <Toto, OrthancStone::DicomVolumeImage::GeometryReadyMessage>(*this, &Toto::Handle));
+  }
+
+  void SetVolume1(int depth,
+                  const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
+                  OrthancStone::ILayerStyleConfigurator* style)
+  {
+    source1_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
+
+    if (style != NULL)
+    {
+      source1_->SetConfigurator(style);
+    }
+  }
+
+  void SetVolume2(int depth,
+                  const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
+                  OrthancStone::ILayerStyleConfigurator* style)
+  {
+    source2_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
+
+    if (style != NULL)
+    {
+      source2_->SetConfigurator(style);
+    }
+  }
+
+  void SetStructureSet(int depth,
+                       const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume)
+  {
+    source3_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
+  }
+                       
+};
+
+
+void Run(OrthancStone::NativeApplicationContext& context,
+         OrthancStone::ThreadedOracle& oracle)
+{
+  // the oracle has been supplied with the context (as an IEmitter) upon
+  // creation
+  boost::shared_ptr<OrthancStone::DicomVolumeImage>  ct(new OrthancStone::DicomVolumeImage);
+  boost::shared_ptr<OrthancStone::DicomVolumeImage>  dose(new OrthancStone::DicomVolumeImage);
+
+  
+  boost::shared_ptr<Toto> toto;
+  boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader;
+  boost::shared_ptr<OrthancStone::OrthancMultiframeVolumeLoader> doseLoader;
+  boost::shared_ptr<OrthancStone::DicomStructureSetLoader>  rtstructLoader;
+
+  {
+    OrthancStone::NativeApplicationContext::WriterLock lock(context);
+    toto.reset(new Toto(oracle, lock.GetOracleObservable()));
+
+    // the oracle is used to schedule commands
+    // the oracleObservable is used by the loaders to:
+    // - request the broker (lifetime mgmt)
+    // - register the loader callbacks (called indirectly by the oracle)
+    ctLoader.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct, oracle, lock.GetOracleObservable()));
+    doseLoader.reset(new OrthancStone::OrthancMultiframeVolumeLoader(dose, oracle, lock.GetOracleObservable()));
+    rtstructLoader.reset(new OrthancStone::DicomStructureSetLoader(oracle, lock.GetOracleObservable()));
+  }
+
+
+  //toto->SetReferenceLoader(*ctLoader);
+  toto->SetReferenceLoader(*doseLoader);
+
+
+#if 1
+  toto->SetVolume1(0, ctLoader, new OrthancStone::GrayscaleStyleConfigurator);
+#else
+  {
+    boost::shared_ptr<OrthancStone::IVolumeSlicer> reslicer(new OrthancStone::DicomVolumeImageReslicer(ct));
+    toto->SetVolume1(0, reslicer, new OrthancStone::GrayscaleStyleConfigurator);
+  }
+#endif  
+  
+
+  {
+    std::unique_ptr<OrthancStone::LookupTableStyleConfigurator> config(new OrthancStone::LookupTableStyleConfigurator);
+    config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT);
+
+    boost::shared_ptr<OrthancStone::DicomVolumeImageMPRSlicer> tmp(new OrthancStone::DicomVolumeImageMPRSlicer(dose));
+    toto->SetVolume2(1, tmp, config.release());
+  }
+
+  toto->SetStructureSet(2, rtstructLoader);
+  
+  oracle.Schedule(*toto, new OrthancStone::SleepOracleCommand(100));
+
+  if (0)
+  {
+    Json::Value v = Json::objectValue;
+    v["Level"] = "Series";
+    v["Query"] = Json::objectValue;
+
+    std::unique_ptr<OrthancStone::OrthancRestApiCommand>  command(new OrthancStone::OrthancRestApiCommand);
+    command->SetMethod(Orthanc::HttpMethod_Post);
+    command->SetUri("/tools/find");
+    command->SetBody(v);
+
+    oracle.Schedule(*toto, command.release());
+  }
+  
+  if(0)
+  {
+    if (0)
+    {
+      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
+      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg)));
+      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview");
+      oracle.Schedule(*toto, command.release());
+    }
+    
+    if (0)
+    {
+      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
+      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png)));
+      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview");
+      oracle.Schedule(*toto, command.release());
+    }
+    
+    if (0)
+    {
+      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
+      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png)));
+      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
+      oracle.Schedule(*toto, command.release());
+    }
+    
+    if (0)
+    {
+      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
+      command->SetHttpHeader("Accept-Encoding", "gzip");
+      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam)));
+      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
+      oracle.Schedule(*toto, command.release());
+    }
+    
+    if (0)
+    {
+      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
+      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam)));
+      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
+      oracle.Schedule(*toto, command.release());
+    }
+
+    if (0)
+    {
+      std::unique_ptr<OrthancStone::GetOrthancWebViewerJpegCommand>  command(new OrthancStone::GetOrthancWebViewerJpegCommand);
+      command->SetHttpHeader("Accept-Encoding", "gzip");
+      command->SetInstance("e6c7c20b-c9f65d7e-0d76f2e2-830186f2-3e3c600e");
+      command->SetQuality(90);
+      oracle.Schedule(*toto, command.release());
+    }
+
+
+    if (0)
+    {
+      for (unsigned int i = 0; i < 10; i++)
+      {
+        std::unique_ptr<OrthancStone::SleepOracleCommand> command(new OrthancStone::SleepOracleCommand(i * 1000));
+        command->SetPayload(new Orthanc::SingleValueObject<unsigned int>(42 * i));
+        oracle.Schedule(*toto, command.release());
+      }
+    }
+  }
+  
+  // 2017-11-17-Anonymized
+#if 0
+  // BGO data
+  ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
+  doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb");  // RT-DOSE
+  //rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
+#else
+  //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618");  // CT
+  //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6");  // RT-DOSE
+  //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6");  // RT-STRUCT
+
+  // 2017-05-16
+  ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
+  doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad");  // RT-DOSE
+  rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
+#endif
+  // 2015-01-28-Multiframe
+  //doseLoader->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279");  // Multiframe CT
+  
+  // Delphine
+  //ctLoader->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e");  // CT
+  //ctLoader->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5");  // Lung 1/10mm
+
+
+  {
+    LOG(WARNING) << "...Waiting for Ctrl-C...";
+
+    oracle.Start();
+
+    Orthanc::SystemToolbox::ServerBarrier();
+
+    /**
+     * WARNING => The oracle must be stopped BEFORE the objects using
+     * it are destroyed!!! This forces to wait for the completion of
+     * the running callback methods. Otherwise, the callbacks methods
+     * might still be running while their parent object is destroyed,
+     * resulting in crashes. This is very visible if adding a sleep(),
+     * as in (*).
+     **/
+
+    oracle.Stop();
+  }
+}
+
+
+
+/**
+ * IMPORTANT: The full arguments to "main()" are needed for SDL on
+ * Windows. Otherwise, one gets the linking error "undefined reference
+ * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
+ **/
+int main(int argc, char* argv[])
+{
+  OrthancStone::StoneInitialize();
+  //Orthanc::Logging::EnableInfoLevel(true);
+
+  try
+  {
+    OrthancStone::NativeApplicationContext context;
+
+    OrthancStone::ThreadedOracle oracle(context);
+    //oracle.SetThreadsCount(1);
+
+    {
+      Orthanc::WebServiceParameters p;
+      //p.SetUrl("http://localhost:8043/");
+      p.SetCredentials("orthanc", "orthanc");
+      oracle.SetOrthancParameters(p);
+    }
+
+    //oracle.Start();
+
+    Run(context, oracle);
+    
+    //oracle.Stop();
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "EXCEPTION: " << e.What();
+  }
+
+  OrthancStone::StoneFinalize();
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Sdl/RadiographyEditor.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,267 @@
+/**
+ * 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/>.
+ **/
+
+#include "../Shared/RadiographyEditorApp.h"
+
+// From Stone
+#include "../../Framework/Oracle/SleepOracleCommand.h"
+#include "../../Framework/Oracle/ThreadedOracle.h"
+#include "../../Applications/Sdl/SdlOpenGLWindow.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Scene2D/CairoCompositor.h"
+#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/StoneInitialization.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+#include <SDL.h>
+#include <stdio.h>
+
+using namespace OrthancStone;
+
+namespace OrthancStone
+{
+  class NativeApplicationContext : public IMessageEmitter
+  {
+  private:
+    boost::shared_mutex  mutex_;
+    MessageBroker        broker_;
+    IObservable          oracleObservable_;
+
+  public:
+    NativeApplicationContext() :
+      oracleObservable_(broker_)
+    {
+    }
+
+
+    virtual void EmitMessage(const IObserver& observer,
+                             const IMessage& message) ORTHANC_OVERRIDE
+    {
+      try
+      {
+        boost::unique_lock<boost::shared_mutex>  lock(mutex_);
+        oracleObservable_.EmitMessage(observer, message);
+      }
+      catch (Orthanc::OrthancException& e)
+      {
+        LOG(ERROR) << "Exception while emitting a message: " << e.What();
+      }
+    }
+
+
+    class ReaderLock : public boost::noncopyable
+    {
+    private:
+      NativeApplicationContext&                that_;
+      boost::shared_lock<boost::shared_mutex>  lock_;
+
+    public:
+      ReaderLock(NativeApplicationContext& that) :
+        that_(that),
+        lock_(that.mutex_)
+      {
+      }
+    };
+
+
+    class WriterLock : public boost::noncopyable
+    {
+    private:
+      NativeApplicationContext&                that_;
+      boost::unique_lock<boost::shared_mutex>  lock_;
+
+    public:
+      WriterLock(NativeApplicationContext& that) :
+        that_(that),
+        lock_(that.mutex_)
+      {
+      }
+
+      MessageBroker& GetBroker()
+      {
+        return that_.broker_;
+      }
+
+      IObservable& GetOracleObservable()
+      {
+        return that_.oracleObservable_;
+      }
+    };
+  };
+}
+
+class OpenGlSdlCompositorFactory : public ICompositorFactory
+{
+  OpenGL::IOpenGLContext& openGlContext_;
+
+public:
+  OpenGlSdlCompositorFactory(OpenGL::IOpenGLContext& openGlContext) :
+    openGlContext_(openGlContext)
+  {}
+
+  ICompositor* GetCompositor(const Scene2D& scene)
+  {
+
+    OpenGLCompositor* compositor = new OpenGLCompositor(openGlContext_, scene);
+    compositor->SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
+                         FONT_SIZE_0, Orthanc::Encoding_Latin1);
+    compositor->SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
+                         FONT_SIZE_1, Orthanc::Encoding_Latin1);
+    return compositor;
+  }
+};
+
+static void GLAPIENTRY
+OpenGLMessageCallback(GLenum source,
+                      GLenum type,
+                      GLuint id,
+                      GLenum severity,
+                      GLsizei length,
+                      const GLchar* message,
+                      const void* userParam)
+{
+  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
+  {
+    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+            (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
+            type, severity, message);
+  }
+}
+
+
+/**
+ * IMPORTANT: The full arguments to "main()" are needed for SDL on
+ * Windows. Otherwise, one gets the linking error "undefined reference
+ * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
+ **/
+int main(int argc, char* argv[])
+{
+  using namespace OrthancStone;
+
+  StoneInitialize();
+  Orthanc::Logging::EnableInfoLevel(true);
+  //  Orthanc::Logging::EnableTraceLevel(true);
+
+  try
+  {
+    OrthancStone::NativeApplicationContext context;
+    OrthancStone::NativeApplicationContext::WriterLock lock(context);
+    OrthancStone::ThreadedOracle oracle(context);
+
+    // False means we do NOT let Windows treat this as a legacy application
+    // that needs to be scaled
+    SdlOpenGLWindow window("Hello", 1024, 1024, false);
+
+    glEnable(GL_DEBUG_OUTPUT);
+    glDebugMessageCallback(OpenGLMessageCallback, 0);
+
+    std::unique_ptr<OpenGlSdlCompositorFactory> compositorFactory(new OpenGlSdlCompositorFactory(window));
+    boost::shared_ptr<RadiographyEditorApp> app(new RadiographyEditorApp(oracle, lock.GetOracleObservable(), compositorFactory.release()));
+    app->PrepareScene();
+    app->FitContent(window.GetCanvasWidth(), window.GetCanvasHeight());
+
+    bool stopApplication = false;
+
+    while (!stopApplication)
+    {
+      app->Refresh();
+
+      SDL_Event event;
+      while (!stopApplication && SDL_PollEvent(&event))
+      {
+        OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None;
+        if (event.key.keysym.mod & KMOD_CTRL)
+          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Control));
+        if (event.key.keysym.mod & KMOD_ALT)
+          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Alt));
+        if (event.key.keysym.mod & KMOD_SHIFT)
+          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Shift));
+
+        OrthancStone::MouseButton button;
+        if (event.button.button == SDL_BUTTON_LEFT)
+          button = OrthancStone::MouseButton_Left;
+        else if (event.button.button == SDL_BUTTON_MIDDLE)
+          button = OrthancStone::MouseButton_Middle;
+        else if (event.button.button == SDL_BUTTON_RIGHT)
+          button = OrthancStone::MouseButton_Right;
+
+        if (event.type == SDL_QUIT)
+        {
+          stopApplication = true;
+          break;
+        }
+        else if (event.type == SDL_WINDOWEVENT &&
+                 event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
+        {
+          app->DisableTracker(); // was: tracker.reset(NULL);
+          app->UpdateSize();
+        }
+        else if (event.type == SDL_KEYDOWN &&
+                 event.key.repeat == 0 /* Ignore key bounce */)
+        {
+          switch (event.key.keysym.sym)
+          {
+          case SDLK_f:
+            window.GetWindow().ToggleMaximize();
+            break;
+
+          case SDLK_q:
+            stopApplication = true;
+            break;
+          default:
+          {
+            app->OnKeyPressed(event.key.keysym.sym, modifiers);
+           }
+          }
+        }
+        else if (event.type == SDL_MOUSEBUTTONDOWN)
+        {
+          app->OnMouseDown(event.button.x, event.button.y, modifiers, button);
+        }
+        else if (event.type == SDL_MOUSEMOTION)
+        {
+          app->OnMouseMove(event.button.x, event.button.y, modifiers);
+        }
+        else if (event.type == SDL_MOUSEBUTTONUP)
+        {
+          app->OnMouseUp(event.button.x, event.button.y, modifiers, button);
+        }
+      }
+      SDL_Delay(1);
+    }
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "EXCEPTION: " << e.What();
+  }
+
+  StoneFinalize();
+
+  return 0;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Sdl/TrackerSample.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,93 @@
+/**
+ * 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/>.
+ **/
+
+#include "TrackerSampleApp.h"
+
+ // From Stone
+#include "../../Framework/OpenGL/SdlOpenGLContext.h"
+#include "../../Framework/Scene2D/CairoCompositor.h"
+#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/StoneInitialization.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+#include <SDL.h>
+#include <stdio.h>
+
+/*
+TODO:
+
+- to decouple the trackers from the sample, we need to supply them with
+  the scene rather than the app
+
+- in order to do that, we need a GetNextFreeZIndex function (or something 
+  along those lines) in the scene object
+
+*/
+
+boost::weak_ptr<OrthancStone::TrackerSampleApp> g_app;
+
+void TrackerSample_SetInfoDisplayMessage(std::string key, std::string value)
+{
+  boost::shared_ptr<OrthancStone::TrackerSampleApp> app = g_app.lock();
+  if (app)
+  {
+    app->SetInfoDisplayMessage(key, value);
+  }
+}
+
+/**
+ * IMPORTANT: The full arguments to "main()" are needed for SDL on
+ * Windows. Otherwise, one gets the linking error "undefined reference
+ * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
+ **/
+int main(int argc, char* argv[])
+{
+  using namespace OrthancStone;
+
+  StoneInitialize();
+  Orthanc::Logging::EnableInfoLevel(true);
+//  Orthanc::Logging::EnableTraceLevel(true);
+
+  try
+  {
+    MessageBroker broker;
+    boost::shared_ptr<TrackerSampleApp> app(new TrackerSampleApp(broker));
+    g_app = app;
+    app->PrepareScene();
+    app->Run();
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "EXCEPTION: " << e.What();
+  }
+
+  StoneFinalize();
+
+  return 0;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Sdl/TrackerSampleApp.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,733 @@
+/**
+ * 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/>.
+ **/
+
+#include "TrackerSampleApp.h"
+
+#include "../../Framework/OpenGL/SdlOpenGLContext.h"
+#include "../../Framework/Scene2D/CairoCompositor.h"
+#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Scene2D/PanSceneTracker.h"
+#include "../../Framework/Scene2D/RotateSceneTracker.h"
+#include "../../Framework/Scene2D/Scene2D.h"
+#include "../../Framework/Scene2D/ZoomSceneTracker.h"
+#include "../../Framework/Scene2DViewport/UndoStack.h"
+#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h"
+#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h"
+#include "../../Framework/StoneInitialization.h"
+
+// From Orthanc framework
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/PngWriter.h>
+
+#include <boost/ref.hpp>
+#include <boost/make_shared.hpp>
+#include <SDL.h>
+
+#include <stdio.h>
+
+namespace OrthancStone
+{
+  const char* MeasureToolToString(size_t i)
+  {
+    static const char* descs[] = {
+      "GuiTool_Rotate",
+      "GuiTool_Pan",
+      "GuiTool_Zoom",
+      "GuiTool_LineMeasure",
+      "GuiTool_CircleMeasure",
+      "GuiTool_AngleMeasure",
+      "GuiTool_EllipseMeasure",
+      "GuiTool_LAST"
+    };
+    if (i >= GuiTool_LAST)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index");
+    }
+    return descs[i];
+  }
+
+  void TrackerSampleApp::SelectNextTool()
+  {
+    currentTool_ = static_cast<GuiTool>(currentTool_ + 1);
+    if (currentTool_ == GuiTool_LAST)
+      currentTool_ = static_cast<GuiTool>(0);;
+    printf("Current tool is now: %s\n", MeasureToolToString(currentTool_));
+  }
+
+  void TrackerSampleApp::DisplayInfoText()
+  {
+    // do not try to use stuff too early!
+    std::stringstream msg;
+	
+	for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin();
+		kv != infoTextMap_.end(); ++kv)
+    {
+      msg << kv->first << " : " << kv->second << std::endl;
+    }
+	std::string msgS = msg.str();
+
+    TextSceneLayer* layerP = NULL;
+    if (controller_->GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX))
+    {
+      TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>(
+        controller_->GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX));
+      layerP = &layer;
+    }
+    else
+    {
+      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+      layerP = layer.get();
+      layer->SetColor(0, 255, 0);
+      layer->SetFontIndex(1);
+      layer->SetBorder(20);
+      layer->SetAnchor(BitmapAnchor_TopLeft);
+      //layer->SetPosition(0,0);
+      controller_->GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release());
+    }
+    // position the fixed info text in the upper right corner
+    layerP->SetText(msgS.c_str());
+    double cX = GetCompositor().GetCanvasWidth() * (-0.5);
+    double cY = GetCompositor().GetCanvasHeight() * (-0.5);
+    controller_->GetScene().GetCanvasToSceneTransform().Apply(cX,cY);
+    layerP->SetPosition(cX, cY);
+  }
+
+  void TrackerSampleApp::DisplayFloatingCtrlInfoText(const PointerEvent& e)
+  {
+    ScenePoint2D p = e.GetMainPosition().Apply(controller_->GetScene().GetCanvasToSceneTransform());
+
+    char buf[128];
+    sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", 
+      p.GetX(), p.GetY(), 
+      e.GetMainPosition().GetX(), e.GetMainPosition().GetY());
+
+    if (controller_->GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX))
+    {
+      TextSceneLayer& layer =
+        dynamic_cast<TextSceneLayer&>(controller_->GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX));
+      layer.SetText(buf);
+      layer.SetPosition(p.GetX(), p.GetY());
+    }
+    else
+    {
+      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+      layer->SetColor(0, 255, 0);
+      layer->SetText(buf);
+      layer->SetBorder(20);
+      layer->SetAnchor(BitmapAnchor_BottomCenter);
+      layer->SetPosition(p.GetX(), p.GetY());
+      controller_->GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release());
+    }
+  }
+
+  void TrackerSampleApp::HideInfoText()
+  {
+    controller_->GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX);
+  }
+
+  ScenePoint2D TrackerSampleApp::GetRandomPointInScene() const
+  {
+    unsigned int w = GetCompositor().GetCanvasWidth();
+    LOG(TRACE) << "GetCompositor().GetCanvasWidth() = " << 
+      GetCompositor().GetCanvasWidth();
+    unsigned int h = GetCompositor().GetCanvasHeight();
+    LOG(TRACE) << "GetCompositor().GetCanvasHeight() = " << 
+      GetCompositor().GetCanvasHeight();
+
+    if ((w >= RAND_MAX) || (h >= RAND_MAX))
+      LOG(WARNING) << "Canvas is too big : tools will not be randomly placed";
+
+    int x = rand() % w;
+    int y = rand() % h;
+    LOG(TRACE) << "random x = " << x << "random y = " << y;
+
+    ScenePoint2D p = controller_->GetViewport().GetPixelCenterCoordinates(x, y);
+    LOG(TRACE) << "--> p.GetX() = " << p.GetX() << " p.GetY() = " << p.GetY();
+
+    ScenePoint2D r = p.Apply(controller_->GetScene().GetCanvasToSceneTransform());
+    LOG(TRACE) << "--> r.GetX() = " << r.GetX() << " r.GetY() = " << r.GetY();
+    return r;
+  }
+
+  void TrackerSampleApp::CreateRandomMeasureTool()
+  {
+    static bool srandCalled = false;
+    if (!srandCalled)
+    {
+      srand(42);
+      srandCalled = true;
+    }
+
+    int i = rand() % 2;
+    LOG(TRACE) << "random i = " << i;
+    switch (i)
+    {
+    case 0:
+      // line measure
+      {
+        boost::shared_ptr<CreateLineMeasureCommand> cmd = 
+          boost::make_shared<CreateLineMeasureCommand>(
+		  boost::ref(IObserver::GetBroker()),
+            controller_,
+            GetRandomPointInScene());
+        cmd->SetEnd(GetRandomPointInScene());
+        controller_->PushCommand(cmd);
+      }
+      break;
+    case 1:
+      // angle measure
+      {
+      boost::shared_ptr<CreateAngleMeasureCommand> cmd =
+        boost::make_shared<CreateAngleMeasureCommand>(
+          boost::ref(IObserver::GetBroker()),
+          controller_,
+          GetRandomPointInScene());
+        cmd->SetCenter(GetRandomPointInScene());
+        cmd->SetSide2End(GetRandomPointInScene());
+        controller_->PushCommand(cmd);
+      }
+      break;
+    }
+  }
+
+  void TrackerSampleApp::HandleApplicationEvent(
+    const SDL_Event & event)
+  {
+    DisplayInfoText();
+
+    if (event.type == SDL_MOUSEMOTION)
+    {
+      int scancodeCount = 0;
+      const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
+
+      if (activeTracker_.get() == NULL &&
+        SDL_SCANCODE_LALT < scancodeCount &&
+        keyboardState[SDL_SCANCODE_LALT])
+      {
+        // The "left-ctrl" key is down, while no tracker is present
+        // Let's display the info text
+        PointerEvent e;
+        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
+          event.button.x, event.button.y));
+
+        DisplayFloatingCtrlInfoText(e);
+      }
+      else if (activeTracker_.get() != NULL)
+      {
+        HideInfoText();
+        //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)";
+        if (activeTracker_.get() != NULL)
+        {
+          //LOG(TRACE) << "(activeTracker_.get() != NULL)";
+          PointerEvent e;
+          e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
+            event.button.x, event.button.y));
+          
+          //LOG(TRACE) << "event.button.x = " << event.button.x << "     " <<
+          //  "event.button.y = " << event.button.y;
+          LOG(TRACE) << "activeTracker_->PointerMove(e); " <<
+            e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY();
+          
+          activeTracker_->PointerMove(e);
+          if (!activeTracker_->IsAlive())
+            activeTracker_.reset();
+        }
+      }
+      else
+      {
+        HideInfoText();
+
+        PointerEvent e;
+        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
+
+        ScenePoint2D scenePos = e.GetMainPosition().Apply(
+          controller_->GetScene().GetCanvasToSceneTransform());
+        //auto measureTools = GetController()->HitTestMeasureTools(scenePos);
+        //LOG(TRACE) << "# of hit tests: " << measureTools.size();
+        
+        // this returns the collection of measuring tools where hit test is true
+        std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos);
+
+        // let's refresh the measuring tools highlighted state
+        // first let's tag them as "unhighlighted"
+        controller_->ResetMeasuringToolsHighlight();
+
+        // then immediately take the first one and ask it to highlight the 
+        // measuring tool UI part that is hot
+        if (measureTools.size() > 0)
+        {
+          measureTools[0]->Highlight(scenePos);
+        }
+      }
+    }
+    else if (event.type == SDL_MOUSEBUTTONUP)
+    {
+      if (activeTracker_)
+      {
+        PointerEvent e;
+        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
+        activeTracker_->PointerUp(e);
+        if (!activeTracker_->IsAlive())
+          activeTracker_.reset();
+      }
+    }
+    else if (event.type == SDL_MOUSEBUTTONDOWN)
+    {
+      PointerEvent e;
+      e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
+        event.button.x, event.button.y));
+      if (activeTracker_)
+      {
+        activeTracker_->PointerDown(e);
+        if (!activeTracker_->IsAlive())
+          activeTracker_.reset();
+      }
+      else
+      {
+        // we ATTEMPT to create a tracker if need be
+        activeTracker_ = CreateSuitableTracker(event, e);
+      }
+    }
+    else if (event.type == SDL_KEYDOWN &&
+      event.key.repeat == 0 /* Ignore key bounce */)
+    {
+      switch (event.key.keysym.sym)
+      {
+      case SDLK_ESCAPE:
+        if (activeTracker_)
+        {
+          activeTracker_->Cancel();
+          if (!activeTracker_->IsAlive())
+            activeTracker_.reset();
+        }
+        break;
+
+      case SDLK_t:
+        if (!activeTracker_)
+          SelectNextTool();
+        else
+        {
+          LOG(WARNING) << "You cannot change the active tool when an interaction"
+            " is taking place";
+        }
+        break;
+
+      case SDLK_m:
+        CreateRandomMeasureTool();
+        break;
+      case SDLK_s:
+        controller_->FitContent(GetCompositor().GetCanvasWidth(),
+          GetCompositor().GetCanvasHeight());
+        break;
+
+      case SDLK_z:
+        LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
+        if (event.key.keysym.mod & KMOD_CTRL)
+        {
+          if (controller_->CanUndo())
+          {
+            LOG(TRACE) << "Undoing...";
+            controller_->Undo();
+          }
+          else
+          {
+            LOG(WARNING) << "Nothing to undo!!!";
+          }
+        }
+        break;
+
+      case SDLK_y:
+        LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
+        if (event.key.keysym.mod & KMOD_CTRL)
+        {
+          if (controller_->CanRedo())
+          {
+            LOG(TRACE) << "Redoing...";
+            controller_->Redo();
+          }
+          else
+          {
+            LOG(WARNING) << "Nothing to redo!!!";
+          }
+        }
+        break;
+
+      case SDLK_c:
+        TakeScreenshot(
+          "screenshot.png",
+          GetCompositor().GetCanvasWidth(),
+          GetCompositor().GetCanvasHeight());
+        break;
+
+      default:
+        break;
+      }
+    }
+  }
+
+
+  void TrackerSampleApp::OnSceneTransformChanged(
+    const ViewportController::SceneTransformChanged& message)
+  {
+    DisplayInfoText();
+  }
+
+  boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::CreateSuitableTracker(
+    const SDL_Event & event,
+    const PointerEvent & e)
+  {
+    using namespace Orthanc;
+
+    switch (event.button.button)
+    {
+    case SDL_BUTTON_MIDDLE:
+      return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker
+        (controller_, e));
+
+    case SDL_BUTTON_RIGHT:
+      return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker
+        (controller_, e, GetCompositor().GetCanvasHeight()));
+
+    case SDL_BUTTON_LEFT:
+    {
+      //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:";
+      // TODO: we need to iterate on the set of measuring tool and perform
+      // a hit test to check if a tracker needs to be created for edition.
+      // Otherwise, depending upon the active tool, we might want to create
+      // a "measuring tool creation" tracker
+
+      // TODO: if there are conflicts, we should prefer a tracker that 
+      // pertains to the type of measuring tool currently selected (TBD?)
+      boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e);
+
+      if (hitTestTracker != NULL)
+      {
+        //LOG(TRACE) << "hitTestTracker != NULL";
+        return hitTestTracker;
+      }
+      else
+      {
+        switch (currentTool_)
+        {
+        case GuiTool_Rotate:
+          //LOG(TRACE) << "Creating RotateSceneTracker";
+          return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker(
+            controller_, e));
+        case GuiTool_Pan:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker(
+            controller_, e));
+        case GuiTool_Zoom:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker(
+            controller_, e, GetCompositor().GetCanvasHeight()));
+        //case GuiTool_AngleMeasure:
+        //  return new AngleMeasureTracker(GetScene(), e);
+        //case GuiTool_CircleMeasure:
+        //  return new CircleMeasureTracker(GetScene(), e);
+        //case GuiTool_EllipseMeasure:
+        //  return new EllipseMeasureTracker(GetScene(), e);
+        case GuiTool_LineMeasure:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker(
+            IObserver::GetBroker(), controller_, e));
+        case GuiTool_AngleMeasure:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker(
+            IObserver::GetBroker(), controller_, e));
+        case GuiTool_CircleMeasure:
+          LOG(ERROR) << "Not implemented yet!";
+          return boost::shared_ptr<IFlexiblePointerTracker>();
+        case GuiTool_EllipseMeasure:
+          LOG(ERROR) << "Not implemented yet!";
+          return boost::shared_ptr<IFlexiblePointerTracker>();
+        default:
+          throw OrthancException(ErrorCode_InternalError, "Wrong tool!");
+        }
+      }
+    }
+    default:
+      return boost::shared_ptr<IFlexiblePointerTracker>();
+    }
+  }
+
+
+  TrackerSampleApp::TrackerSampleApp(MessageBroker& broker) : IObserver(broker)
+    , currentTool_(GuiTool_Rotate)
+    , undoStack_(new UndoStack)
+    , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled
+  {
+    controller_ = boost::shared_ptr<ViewportController>(
+      new ViewportController(undoStack_, broker, viewport_));
+
+    controller_->RegisterObserverCallback(
+      new Callable<TrackerSampleApp, ViewportController::SceneTransformChanged>
+      (*this, &TrackerSampleApp::OnSceneTransformChanged));
+
+    TEXTURE_2x2_1_ZINDEX = 1;
+    TEXTURE_1x1_ZINDEX = 2;
+    TEXTURE_2x2_2_ZINDEX = 3;
+    LINESET_1_ZINDEX = 4;
+    LINESET_2_ZINDEX = 5;
+    FLOATING_INFOTEXT_LAYER_ZINDEX = 6;
+    FIXED_INFOTEXT_LAYER_ZINDEX = 7;
+  }
+
+  void TrackerSampleApp::PrepareScene()
+  {
+    // Texture of 2x2 size
+    {
+      Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false);
+
+      uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0));
+      p[0] = 255;
+      p[1] = 0;
+      p[2] = 0;
+
+      p[3] = 0;
+      p[4] = 255;
+      p[5] = 0;
+
+      p = reinterpret_cast<uint8_t*>(i.GetRow(1));
+      p[0] = 0;
+      p[1] = 0;
+      p[2] = 255;
+
+      p[3] = 255;
+      p[4] = 0;
+      p[5] = 0;
+
+      controller_->GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i));
+
+      std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
+      l->SetOrigin(-3, 2);
+      l->SetPixelSpacing(1.5, 1);
+      l->SetAngle(20.0 / 180.0 * M_PI);
+      controller_->GetScene().SetLayer(TEXTURE_2x2_2_ZINDEX, l.release());
+    }
+
+    // Texture of 1x1 size
+    {
+      Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false);
+
+      uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0));
+      p[0] = 255;
+      p[1] = 0;
+      p[2] = 0;
+
+      std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
+      l->SetOrigin(-2, 1);
+      l->SetAngle(20.0 / 180.0 * M_PI);
+      controller_->GetScene().SetLayer(TEXTURE_1x1_ZINDEX, l.release());
+    }
+
+    // Some lines
+    {
+      std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer);
+
+      layer->SetThickness(1);
+
+      PolylineSceneLayer::Chain chain;
+      chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5));
+      chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5));
+      chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5));
+      chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5));
+      layer->AddChain(chain, true, 255, 0, 0);
+
+      chain.clear();
+      chain.push_back(ScenePoint2D(-5, -5));
+      chain.push_back(ScenePoint2D(5, -5));
+      chain.push_back(ScenePoint2D(5, 5));
+      chain.push_back(ScenePoint2D(-5, 5));
+      layer->AddChain(chain, true, 0, 255, 0);
+
+      double dy = 1.01;
+      chain.clear();
+      chain.push_back(ScenePoint2D(-4, -4));
+      chain.push_back(ScenePoint2D(4, -4 + dy));
+      chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy));
+      chain.push_back(ScenePoint2D(4, 2));
+      layer->AddChain(chain, false, 0, 0, 255);
+
+      controller_->GetScene().SetLayer(LINESET_1_ZINDEX, layer.release());
+    }
+
+    // Some text
+    {
+      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+      layer->SetText("Hello");
+      controller_->GetScene().SetLayer(LINESET_2_ZINDEX, layer.release());
+    }
+  }
+
+
+  void TrackerSampleApp::DisableTracker()
+  {
+    if (activeTracker_)
+    {
+      activeTracker_->Cancel();
+      activeTracker_.reset();
+    }
+  }
+
+  void TrackerSampleApp::TakeScreenshot(const std::string& target,
+    unsigned int canvasWidth,
+    unsigned int canvasHeight)
+  {
+    CairoCompositor compositor(controller_->GetScene(), canvasWidth, canvasHeight);
+    compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1);
+    compositor.Refresh();
+
+    Orthanc::ImageAccessor canvas;
+    compositor.GetCanvas().GetReadOnlyAccessor(canvas);
+
+    Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
+    Orthanc::ImageProcessing::Convert(png, canvas);
+
+    Orthanc::PngWriter writer;
+    writer.WriteToFile(target, png);
+  }
+
+
+  boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::TrackerHitTest(const PointerEvent & e)
+  {
+    // std::vector<boost::shared_ptr<MeasureTool>> measureTools_;
+    ScenePoint2D scenePos = e.GetMainPosition().Apply(
+      controller_->GetScene().GetCanvasToSceneTransform());
+
+    std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos);
+
+    if (measureTools.size() > 0)
+    {
+      return measureTools[0]->CreateEditionTracker(e);
+    }
+    return boost::shared_ptr<IFlexiblePointerTracker>();
+  }
+
+  static void GLAPIENTRY
+    OpenGLMessageCallback(GLenum source,
+      GLenum type,
+      GLuint id,
+      GLenum severity,
+      GLsizei length,
+      const GLchar* message,
+      const void* userParam)
+  {
+    if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
+    {
+      fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+        (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
+        type, severity, message);
+    }
+  }
+
+  static bool g_stopApplication = false;
+  
+  ICompositor& TrackerSampleApp::GetCompositor()
+  {
+    using namespace Orthanc;
+    try
+    {
+      SdlViewport& viewport = dynamic_cast<SdlViewport&>(viewport_);
+      return viewport.GetCompositor();
+    }
+    catch (std::bad_cast e)
+    {
+      throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!");
+     }
+  }
+
+  const ICompositor& TrackerSampleApp::GetCompositor() const
+  {
+    using namespace Orthanc;
+    try
+    {
+      SdlViewport& viewport = const_cast<SdlViewport&>(dynamic_cast<const SdlViewport&>(viewport_));
+      return viewport.GetCompositor();
+    }
+    catch (std::bad_cast e)
+    {
+      throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!");
+    }
+  }
+
+
+  void TrackerSampleApp::Run()
+  {
+    controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight());
+
+    glEnable(GL_DEBUG_OUTPUT);
+    glDebugMessageCallback(OpenGLMessageCallback, 0);
+
+    GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
+      FONT_SIZE_0, Orthanc::Encoding_Latin1);
+    GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
+      FONT_SIZE_1, Orthanc::Encoding_Latin1);
+
+    while (!g_stopApplication)
+    {
+      GetCompositor().Refresh();
+
+      SDL_Event event;
+      while (!g_stopApplication && SDL_PollEvent(&event))
+      {
+        if (event.type == SDL_QUIT)
+        {
+          g_stopApplication = true;
+          break;
+        }
+        else if (event.type == SDL_WINDOWEVENT &&
+          event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
+        {
+          DisableTracker(); // was: tracker.reset(NULL);
+        }
+        else if (event.type == SDL_KEYDOWN &&
+          event.key.repeat == 0 /* Ignore key bounce */)
+        {
+          switch (event.key.keysym.sym)
+          {
+          case SDLK_f:
+            viewport_.GetWindow().ToggleMaximize();
+            break;
+
+          case SDLK_q:
+            g_stopApplication = true;
+            break;
+          default:
+            break;
+          }
+        }
+        HandleApplicationEvent(event);
+      }
+      SDL_Delay(1);
+    }
+  }
+
+  void TrackerSampleApp::SetInfoDisplayMessage(
+    std::string key, std::string value)
+  {
+    if (value == "")
+      infoTextMap_.erase(key);
+    else
+      infoTextMap_[key] = value;
+    DisplayInfoText();
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Sdl/TrackerSampleApp.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,148 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "../../Framework/Messages/IObserver.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
+#include "../../Framework/Scene2DViewport/MeasureTool.h"
+#include "../../Framework/Scene2DViewport/PredeclaredTypes.h"
+#include "../../Framework/Scene2DViewport/ViewportController.h"
+#include "../../Framework/Viewport/SdlViewport.h"
+
+#include <SDL.h>
+
+#include <boost/make_shared.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+namespace OrthancStone
+{
+  enum GuiTool
+  {
+    GuiTool_Rotate = 0,
+    GuiTool_Pan,
+    GuiTool_Zoom,
+    GuiTool_LineMeasure,
+    GuiTool_CircleMeasure,
+    GuiTool_AngleMeasure,
+    GuiTool_EllipseMeasure,
+    GuiTool_LAST
+  };
+
+  const char* MeasureToolToString(size_t i);
+
+  static const unsigned int FONT_SIZE_0 = 32;
+  static const unsigned int FONT_SIZE_1 = 24;
+
+  class Scene2D;
+  class UndoStack;
+
+  class TrackerSampleApp : public IObserver
+    , public boost::enable_shared_from_this<TrackerSampleApp>
+  {
+  public:
+    // 12 because.
+    TrackerSampleApp(MessageBroker& broker);
+    void PrepareScene();
+    void Run();
+    void SetInfoDisplayMessage(std::string key, std::string value);
+    void DisableTracker();
+
+    void HandleApplicationEvent(const SDL_Event& event);
+
+    /**
+    This method is called when the scene transform changes. It allows to
+    recompute the visual elements whose content depend upon the scene transform
+    */
+    void OnSceneTransformChanged(
+      const ViewportController::SceneTransformChanged& message);
+
+  private:
+    void SelectNextTool();
+    void CreateRandomMeasureTool();
+
+
+    /**
+    In the case of this app, the viewport is an SDL viewport and it has 
+    a OpenGLCompositor& GetCompositor() method
+    */
+    ICompositor& GetCompositor();
+
+    /**
+    See the other overload
+    */
+    const ICompositor& GetCompositor() const;
+
+    /**
+    This returns a random point in the canvas part of the scene, but in
+    scene coordinates
+    */
+    ScenePoint2D GetRandomPointInScene() const;
+
+    boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e);
+
+    boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker(
+      const SDL_Event& event,
+      const PointerEvent& e);
+
+    void TakeScreenshot(
+      const std::string& target,
+      unsigned int canvasWidth,
+      unsigned int canvasHeight);
+
+    /**
+      This adds the command at the top of the undo stack
+    */
+    void Commit(boost::shared_ptr<TrackerCommand> cmd);
+    void Undo();
+    void Redo();
+
+  private:
+    void DisplayFloatingCtrlInfoText(const PointerEvent& e);
+    void DisplayInfoText();
+    void HideInfoText();
+
+  private:
+    /**
+    WARNING: the measuring tools do store a reference to the scene, and it 
+    paramount that the scene gets destroyed AFTER the measurement tools.
+    */
+    boost::shared_ptr<ViewportController> controller_;
+
+    std::map<std::string, std::string> infoTextMap_;
+    boost::shared_ptr<IFlexiblePointerTracker> activeTracker_;
+
+    //static const int LAYER_POSITION = 150;
+
+    int TEXTURE_2x2_1_ZINDEX;
+    int TEXTURE_1x1_ZINDEX;
+    int TEXTURE_2x2_2_ZINDEX;
+    int LINESET_1_ZINDEX;
+    int LINESET_2_ZINDEX;
+    int FLOATING_INFOTEXT_LAYER_ZINDEX;
+    int FIXED_INFOTEXT_LAYER_ZINDEX;
+
+    GuiTool currentTool_;
+    boost::shared_ptr<UndoStack> undoStack_;
+    SdlOpenGLViewport viewport_;
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/Sdl/cpp.hint	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,2 @@
+#define ORTHANC_OVERRIDE
+#define ORTHANC_STONE_DEFINE_EMPTY_MESSAGE(FILE, LINE, NAME) class NAME : public ::OrthancStone::IMessage {};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/WebAssembly/BasicMPR.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,427 @@
+/**
+ * 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/>.
+ **/
+
+
+
+#include "dev.h"
+
+#include <emscripten.h>
+
+#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
+#include "../../Framework/Oracle/SleepOracleCommand.h"
+#include "../../Framework/Oracle/WebAssemblyOracle.h"
+#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h"
+#include "../../Framework/StoneInitialization.h"
+#include "../../Framework/Volumes/VolumeSceneLayerSource.h"
+
+
+namespace OrthancStone
+{
+  class VolumeSlicerWidget : public IObserver
+  {
+  private:
+    OrthancStone::WebAssemblyViewport      viewport_;
+    std::unique_ptr<VolumeSceneLayerSource>  source_;
+    VolumeProjection                       projection_;
+    std::vector<CoordinateSystem3D>        planes_;
+    size_t                                 currentPlane_;
+
+    void Handle(const DicomVolumeImage::GeometryReadyMessage& message)
+    {
+      LOG(INFO) << "Geometry is available";
+
+      const VolumeImageGeometry& geometry = message.GetOrigin().GetGeometry();
+
+      const unsigned int depth = geometry.GetProjectionDepth(projection_);
+      currentPlane_ = depth / 2;
+      
+      planes_.resize(depth);
+
+      for (unsigned int z = 0; z < depth; z++)
+      {
+        planes_[z] = geometry.GetProjectionSlice(projection_, z);
+      }
+
+      Refresh();
+
+      viewport_.FitContent();
+    }
+    
+  public:
+    VolumeSlicerWidget(MessageBroker& broker,
+                         const std::string& canvas,
+                         VolumeProjection projection) :
+      IObserver(broker),
+      viewport_(broker, canvas),
+      projection_(projection),
+      currentPlane_(0)
+    {
+    }
+
+    void UpdateSize()
+    {
+      viewport_.UpdateSize();
+    }
+
+    void SetSlicer(int layerDepth,
+                   const boost::shared_ptr<IVolumeSlicer>& slicer,
+                   IObservable& loader,
+                   ILayerStyleConfigurator* configurator)
+    {
+      if (source_.get() != NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
+                                        "Only one slicer can be registered");
+      }
+      
+      loader.RegisterObserverCallback(
+        new Callable<VolumeSlicerWidget, DicomVolumeImage::GeometryReadyMessage>
+        (*this, &VolumeSlicerWidget::Handle));
+
+      source_.reset(new VolumeSceneLayerSource(viewport_.GetScene(), layerDepth, slicer));
+
+      if (configurator != NULL)
+      {
+        source_->SetConfigurator(configurator);
+      }
+    }    
+
+    void Refresh()
+    {
+      if (source_.get() != NULL &&
+          currentPlane_ < planes_.size())
+      {
+        source_->Update(planes_[currentPlane_]);
+        viewport_.Refresh();
+      }
+    }
+
+    size_t GetSlicesCount() const
+    {
+      return planes_.size();
+    }
+
+    void Scroll(int delta)
+    {
+      if (!planes_.empty())
+      {
+        int tmp = static_cast<int>(currentPlane_) + delta;
+        unsigned int next;
+
+        if (tmp < 0)
+        {
+          next = 0;
+        }
+        else if (tmp >= static_cast<int>(planes_.size()))
+        {
+          next = planes_.size() - 1;
+        }
+        else
+        {
+          next = static_cast<size_t>(tmp);
+        }
+
+        if (next != currentPlane_)
+        {
+          currentPlane_ = next;
+          Refresh();
+        }
+      }
+    }
+  };
+}
+
+
+
+
+boost::shared_ptr<OrthancStone::DicomVolumeImage>  ct_(new OrthancStone::DicomVolumeImage);
+
+boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader>  loader_;
+
+std::unique_ptr<OrthancStone::VolumeSlicerWidget>  widget1_;
+std::unique_ptr<OrthancStone::VolumeSlicerWidget>  widget2_;
+std::unique_ptr<OrthancStone::VolumeSlicerWidget>  widget3_;
+
+OrthancStone::MessageBroker  broker_;
+OrthancStone::WebAssemblyOracle  oracle_(broker_);
+
+
+EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
+{
+  try
+  {
+    if (widget1_.get() != NULL)
+    {
+      widget1_->UpdateSize();
+    }
+  
+    if (widget2_.get() != NULL)
+    {
+      widget2_->UpdateSize();
+    }
+  
+    if (widget3_.get() != NULL)
+    {
+      widget3_->UpdateSize();
+    }
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "Exception while updating canvas size: " << e.What();
+  }
+  
+  return true;
+}
+
+
+
+
+EM_BOOL OnAnimationFrame(double time, void *userData)
+{
+  try
+  {
+    if (widget1_.get() != NULL)
+    {
+      widget1_->Refresh();
+    }
+  
+    if (widget2_.get() != NULL)
+    {
+      widget2_->Refresh();
+    }
+  
+    if (widget3_.get() != NULL)
+    {
+      widget3_->Refresh();
+    }
+
+    return true;
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "Exception in the animation loop, stopping now: " << e.What();
+    return false;
+  }  
+}
+
+
+static bool ctrlDown_ = false;
+
+
+EM_BOOL OnMouseWheel(int eventType,
+                     const EmscriptenWheelEvent *wheelEvent,
+                     void *userData)
+{
+  try
+  {
+    if (userData != NULL)
+    {
+      int delta = 0;
+
+      if (wheelEvent->deltaY < 0)
+      {
+        delta = -1;
+      }
+           
+      if (wheelEvent->deltaY > 0)
+      {
+        delta = 1;
+      }
+
+      OrthancStone::VolumeSlicerWidget& widget =
+        *reinterpret_cast<OrthancStone::VolumeSlicerWidget*>(userData);
+      
+      if (ctrlDown_)
+      {
+        delta *= static_cast<int>(widget.GetSlicesCount() / 10);
+      }
+
+      widget.Scroll(delta);
+    }
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "Exception in the wheel event: " << e.What();
+  }
+  
+  return true;
+}
+
+
+EM_BOOL OnKeyDown(int eventType,
+                  const EmscriptenKeyboardEvent *keyEvent,
+                  void *userData)
+{
+  ctrlDown_ = keyEvent->ctrlKey;
+  return false;
+}
+
+
+EM_BOOL OnKeyUp(int eventType,
+                const EmscriptenKeyboardEvent *keyEvent,
+                void *userData)
+{
+  ctrlDown_ = false;
+  return false;
+}
+
+
+
+
+namespace OrthancStone
+{
+  class TestSleep : public IObserver
+  {
+  private:
+    WebAssemblyOracle&  oracle_;
+
+    void Schedule()
+    {
+      oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(2000));
+    }
+    
+    void Handle(const SleepOracleCommand::TimeoutMessage& message)
+    {
+      LOG(INFO) << "TIMEOUT";
+      Schedule();
+    }
+    
+  public:
+    TestSleep(MessageBroker& broker,
+              WebAssemblyOracle& oracle) :
+      IObserver(broker),
+      oracle_(oracle)
+    {
+      oracle.RegisterObserverCallback(
+        new Callable<TestSleep, SleepOracleCommand::TimeoutMessage>
+        (*this, &TestSleep::Handle));
+
+      LOG(INFO) << "STARTING";
+      Schedule();
+    }
+  };
+
+  //static TestSleep testSleep(broker_, oracle_);
+}
+
+
+
+static std::map<std::string, std::string> arguments_;
+
+static bool GetArgument(std::string& value,
+                        const std::string& key)
+{
+  std::map<std::string, std::string>::const_iterator found = arguments_.find(key);
+
+  if (found == arguments_.end())
+  {
+    return false;
+  }
+  else
+  {
+    value = found->second;
+    return true;
+  }
+}
+
+
+extern "C"
+{
+  int main(int argc, char const *argv[]) 
+  {
+    OrthancStone::StoneInitialize();
+    Orthanc::Logging::EnableInfoLevel(true);
+    // Orthanc::Logging::EnableTraceLevel(true);
+    EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded")););
+  }
+
+  EMSCRIPTEN_KEEPALIVE
+  void SetArgument(const char* key, const char* value)
+  {
+    // This is called for each GET argument (cf. "app.js")
+    LOG(INFO) << "Received GET argument: [" << key << "] = [" << value << "]";
+    arguments_[key] = value;
+  }
+
+  EMSCRIPTEN_KEEPALIVE
+  void Initialize()
+  {
+    try
+    {
+      oracle_.SetOrthancRoot("..");
+      
+      loader_.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct_, oracle_, oracle_));
+    
+      widget1_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas1", OrthancStone::VolumeProjection_Axial));
+      {
+        std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator);
+        style->SetLinearInterpolation(true);
+        style->SetWindowing(OrthancStone::ImageWindowing_Bone);
+        widget1_->SetSlicer(0, loader_, *loader_, style.release());
+      }
+      widget1_->UpdateSize();
+
+      widget2_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas2", OrthancStone::VolumeProjection_Coronal));
+      {
+        std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator);
+        style->SetLinearInterpolation(true);
+        style->SetWindowing(OrthancStone::ImageWindowing_Bone);
+        widget2_->SetSlicer(0, loader_, *loader_, style.release());
+      }
+      widget2_->UpdateSize();
+
+      widget3_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas3", OrthancStone::VolumeProjection_Sagittal));
+      {
+        std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator);
+        style->SetLinearInterpolation(true);
+        style->SetWindowing(OrthancStone::ImageWindowing_Bone);
+        widget3_->SetSlicer(0, loader_, *loader_, style.release());
+      }
+      widget3_->UpdateSize();
+
+      emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnWindowResize); // DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 !!
+
+      emscripten_set_wheel_callback("#mycanvas1", widget1_.get(), false, OnMouseWheel);
+      emscripten_set_wheel_callback("#mycanvas2", widget2_.get(), false, OnMouseWheel);
+      emscripten_set_wheel_callback("#mycanvas3", widget3_.get(), false, OnMouseWheel);
+
+      emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyDown);
+      emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyUp);
+    
+      emscripten_request_animation_frame_loop(OnAnimationFrame, NULL);
+
+
+      std::string ct;
+      if (GetArgument(ct, "ct"))
+      {
+        //loader_->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");
+        loader_->LoadSeries(ct);
+      }
+      else
+      {
+        LOG(ERROR) << "No Orthanc identifier for the CT series was provided";
+      }
+    }
+    catch (Orthanc::OrthancException& e)
+    {
+      LOG(ERROR) << "Exception during Initialize(): " << e.What();
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/WebAssembly/BasicMPR.html	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,60 @@
+<!doctype html>
+<html lang="en-us">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <!-- Disable pinch zoom on mobile devices -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <meta name="HandheldFriendly" content="true" />
+    
+    
+    <title>Stone of Orthanc</title>
+
+    <style>
+      html, body {
+      width: 100%;
+      height: 100%;
+      margin: 0px;
+      border: 0;
+      overflow: hidden; /*  Disable scrollbars */
+      display: block;  /* No floating content on sides */
+      }
+
+      #mycanvas1 {
+      position:absolute;
+      left:0%;
+      top:0%;
+      background-color: red;
+      width: 50%;
+      height: 100%;
+      }
+
+      #mycanvas2 {
+      position:absolute;
+      left:50%;
+      top:0%;
+      background-color: green;
+      width: 50%;
+      height: 50%;
+      }
+
+      #mycanvas3 {
+      position:absolute;
+      left:50%;
+      top:50%;
+      background-color: blue;
+      width: 50%;
+      height: 50%;
+      }
+    </style>
+  </head>
+  <body>
+    <canvas id="mycanvas1" oncontextmenu="return false;"></canvas>
+    <canvas id="mycanvas2" oncontextmenu="return false;"></canvas>
+    <canvas id="mycanvas3" oncontextmenu="return false;"></canvas>
+
+    <script type="text/javascript" src="app.js"></script>
+    <script type="text/javascript" async src="BasicMPR.js"></script>
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/WebAssembly/BasicScene.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,210 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "dev.h"
+
+#include <emscripten.h>
+#include <emscripten/html5.h>
+
+// From Stone
+#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "../../Framework/StoneInitialization.h"
+
+// From Orthanc framework
+#include <Core/Images/Image.h>
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+void PrepareScene(OrthancStone::Scene2D& scene)
+{
+  using namespace OrthancStone;
+
+  // Texture of 2x2 size
+  if (1)
+  {
+    Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false);
+    
+    uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0));
+    p[0] = 255;
+    p[1] = 0;
+    p[2] = 0;
+
+    p[3] = 0;
+    p[4] = 255;
+    p[5] = 0;
+
+    p = reinterpret_cast<uint8_t*>(i.GetRow(1));
+    p[0] = 0;
+    p[1] = 0;
+    p[2] = 255;
+
+    p[3] = 255;
+    p[4] = 0;
+    p[5] = 0;
+
+    scene.SetLayer(12, new ColorTextureSceneLayer(i));
+
+    std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
+    l->SetOrigin(-3, 2);
+    l->SetPixelSpacing(1.5, 1);
+    l->SetAngle(20.0 / 180.0 * M_PI);
+    scene.SetLayer(14, l.release());
+  }
+
+  // Texture of 1x1 size
+  if (1)
+  {
+    Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false);
+    
+    uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0));
+    p[0] = 255;
+    p[1] = 0;
+    p[2] = 0;
+
+    std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
+    l->SetOrigin(-2, 1);
+    l->SetAngle(20.0 / 180.0 * M_PI);
+    scene.SetLayer(13, l.release());
+  }
+
+  // Some lines
+  if (1)
+  {
+    std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer);
+
+    layer->SetThickness(1);
+
+    PolylineSceneLayer::Chain chain;
+    chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5));
+    chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5));
+    chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5));
+    chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5));
+    layer->AddChain(chain, true, 255, 0, 0);
+
+    chain.clear();
+    chain.push_back(ScenePoint2D(-5, -5));
+    chain.push_back(ScenePoint2D(5, -5));
+    chain.push_back(ScenePoint2D(5, 5));
+    chain.push_back(ScenePoint2D(-5, 5));
+    layer->AddChain(chain, true, 0, 255, 0);
+
+    double dy = 1.01;
+    chain.clear();
+    chain.push_back(ScenePoint2D(-4, -4));
+    chain.push_back(ScenePoint2D(4, -4 + dy));
+    chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy));
+    chain.push_back(ScenePoint2D(4, 2));
+    layer->AddChain(chain, false, 0, 0, 255);
+
+    scene.SetLayer(50, layer.release());
+  }
+
+  // Some text
+  if (1)
+  {
+    std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+    layer->SetText("Hello");
+    scene.SetLayer(100, layer.release());
+  }
+}
+
+
+std::unique_ptr<OrthancStone::WebAssemblyViewport>  viewport1_;
+std::unique_ptr<OrthancStone::WebAssemblyViewport>  viewport2_;
+std::unique_ptr<OrthancStone::WebAssemblyViewport>  viewport3_;
+boost::shared_ptr<OrthancStone::ViewportController>   controller1_;
+boost::shared_ptr<OrthancStone::ViewportController>   controller2_;
+boost::shared_ptr<OrthancStone::ViewportController>   controller3_;
+OrthancStone::MessageBroker broker_;
+
+
+EM_BOOL OnWindowResize(
+  int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
+{
+  if (viewport1_.get() != NULL)
+  {
+    viewport1_->UpdateSize();
+  }
+  
+  if (viewport2_.get() != NULL)
+  {
+    viewport2_->UpdateSize();
+  }
+  
+  if (viewport3_.get() != NULL)
+  {
+    viewport3_->UpdateSize();
+  }
+  
+  return true;
+}
+
+extern "C"
+{
+  int main(int argc, char const *argv[]) 
+  {
+    OrthancStone::StoneInitialize();
+    // Orthanc::Logging::EnableInfoLevel(true);
+    // Orthanc::Logging::EnableTraceLevel(true);
+    EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded")););
+  }
+
+  EMSCRIPTEN_KEEPALIVE
+  void Initialize()
+  {
+    viewport1_.reset(new OrthancStone::WebAssemblyViewport("mycanvas1"));
+    PrepareScene(viewport1_->GetScene());
+    viewport1_->UpdateSize();
+
+    viewport2_.reset(new OrthancStone::WebAssemblyViewport("mycanvas2"));
+    PrepareScene(viewport2_->GetScene());
+    viewport2_->UpdateSize();
+
+    viewport3_.reset(new OrthancStone::WebAssemblyViewport("mycanvas3"));
+    PrepareScene(viewport3_->GetScene());
+    viewport3_->UpdateSize();
+
+    viewport1_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
+                                        FONT_SIZE, Orthanc::Encoding_Latin1);
+    viewport2_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
+                                        FONT_SIZE, Orthanc::Encoding_Latin1);
+    viewport3_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
+                                        FONT_SIZE, Orthanc::Encoding_Latin1);
+
+    controller1_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport1_));
+    controller2_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport2_));
+    controller3_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport3_));
+
+    controller1_->FitContent(viewport1_->GetCanvasWidth(), viewport1_->GetCanvasHeight());
+    controller2_->FitContent(viewport2_->GetCanvasWidth(), viewport2_->GetCanvasHeight());
+    controller3_->FitContent(viewport3_->GetCanvasWidth(), viewport3_->GetCanvasHeight());
+
+    viewport1_->Refresh();
+    viewport2_->Refresh();
+    viewport3_->Refresh();
+
+    SetupEvents("mycanvas1", controller1_);
+    SetupEvents("mycanvas2", controller2_);
+    SetupEvents("mycanvas3", controller3_);
+
+    emscripten_set_resize_callback("#window", NULL, false, OnWindowResize);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/WebAssembly/BasicScene.html	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,69 @@
+<!doctype html>
+<html lang="en-us">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <!-- Disable pinch zoom on mobile devices -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <meta name="HandheldFriendly" content="true" />
+    
+    
+    <title>Stone of Orthanc</title>
+
+    <style>
+      html, body {
+      width: 100%;
+      height: 100%;
+      margin: 0px;
+      border: 0;
+      overflow: hidden; /*  Disable scrollbars */
+      display: block;  /* No floating content on sides */
+      }
+
+      #mycanvas1 {
+      position:absolute;
+      left:0%;
+      top:0%;
+      background-color: red;
+      width: 50%;
+      height: 100%;
+      }
+
+      #mycanvas2 {
+      position:absolute;
+      left:50%;
+      top:0%;
+      background-color: green;
+      width: 50%;
+      height: 50%;
+      }
+
+      #mycanvas3 {
+      position:absolute;
+      left:50%;
+      top:50%;
+      background-color: blue;
+      width: 50%;
+      height: 50%;
+      }
+    </style>
+  </head>
+  <body>
+    <canvas id="mycanvas1" oncontextmenu="return false;"></canvas>
+    <canvas id="mycanvas2" oncontextmenu="return false;"></canvas>
+    <canvas id="mycanvas3" oncontextmenu="return false;"></canvas>
+
+    <script type="text/javascript">
+      if (!('WebAssembly' in window)) {
+      alert('Sorry, your browser does not support WebAssembly :(');
+      } else {
+      window.addEventListener('WebAssemblyLoaded', function() {
+      Module.ccall('Initialize', null, null, null);
+      });
+      }
+    </script>
+
+    <script type="text/javascript" async src="BasicScene.js"></script>
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/WebAssembly/CMakeLists.txt	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,119 @@
+cmake_minimum_required(VERSION 2.8.3)
+
+
+#####################################################################
+## Configuration of the Emscripten compiler for WebAssembly target
+#####################################################################
+
+set(WASM_FLAGS "-s WASM=1 -s FETCH=1")
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0")
+#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXIT_RUNTIME=1")
+
+#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1")
+
+
+#####################################################################
+## Configuration of the Orthanc framework
+#####################################################################
+
+# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it
+# must be the first inclusion
+include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/Version.cmake)
+
+if (ORTHANC_STONE_VERSION STREQUAL "mainline")
+  set(ORTHANC_FRAMEWORK_VERSION "mainline")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
+else()
+  set(ORTHANC_FRAMEWORK_VERSION "1.5.7")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
+endif()
+
+set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")")
+set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"")
+set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
+
+
+#####################################################################
+## Configuration of the Stone framework
+#####################################################################
+
+include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
+
+DownloadPackage(
+  "a24b8136b8f3bb93f166baf97d9328de"
+  "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip"
+  "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83")
+
+set(ORTHANC_STONE_APPLICATION_RESOURCES
+  UBUNTU_FONT  ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf
+  )
+
+SET(ENABLE_GOOGLE_TEST OFF)
+SET(ENABLE_LOCALE ON)
+SET(ORTHANC_SANDBOXED ON)
+SET(ENABLE_WASM ON)
+
+include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake)
+
+add_definitions(
+  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
+  )
+
+
+#####################################################################
+## Build the samples
+#####################################################################
+
+add_library(OrthancStone STATIC
+  ${ORTHANC_STONE_SOURCES}
+  )
+
+
+if (ON)
+  add_executable(BasicScene
+    BasicScene.cpp
+    #${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.h
+    ${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.cpp
+    )
+
+  target_link_libraries(BasicScene OrthancStone)
+
+  install(
+    TARGETS BasicScene
+    RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}
+    )
+endif()
+
+
+if (ON)
+  add_executable(BasicMPR
+    BasicMPR.cpp
+    )
+
+  target_link_libraries(BasicMPR OrthancStone)
+
+  install(
+    TARGETS BasicMPR
+    RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}
+    )
+endif()
+  
+
+install(
+  FILES
+  ${CMAKE_CURRENT_BINARY_DIR}/BasicMPR.wasm
+  ${CMAKE_CURRENT_BINARY_DIR}/BasicScene.wasm
+  ${CMAKE_SOURCE_DIR}/BasicMPR.html
+  ${CMAKE_SOURCE_DIR}/BasicScene.html
+  ${CMAKE_SOURCE_DIR}/Configuration.json
+  ${CMAKE_SOURCE_DIR}/app.js
+  ${CMAKE_SOURCE_DIR}/index.html
+  DESTINATION ${CMAKE_INSTALL_PREFIX}
+  )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/WebAssembly/Configuration.json	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,17 @@
+{
+  "Plugins": [
+    "/usr/local/share/orthanc/plugins/libOrthancWebViewer.so",
+    "/usr/local/share/orthanc/plugins/libServeFolders.so"
+  ],
+  "StorageDirectory" : "/var/lib/orthanc/db",
+  "IndexDirectory" : "/var/lib/orthanc/db",
+  "RemoteAccessAllowed" : true,
+  "AuthenticationEnabled" : false,
+  "ServeFolders" : {
+    "AllowCache" : false,
+    "GenerateETag" : true,
+    "Folders" : {
+      "/stone" : "/root/stone"
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/WebAssembly/ConfigurationLocalSJO.json	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,20 @@
+{
+  "Plugins": [
+    "/home/jodogne/Subversion/orthanc-webviewer/r/libOrthancWebViewer.so",
+    "/home/jodogne/Subversion/orthanc/r/libServeFolders.so"
+  ],
+  "StorageDirectory" : "/tmp/orthanc-db",
+  "IndexDirectory" : "/tmp/orthanc-db",
+  "RemoteAccessAllowed" : true,
+  "AuthenticationEnabled" : false,
+  "ServeFolders" : {
+    "AllowCache" : false,
+    "GenerateETag" : true,
+    "Folders" : {
+      "/stone" : "/tmp/stone"
+    }
+  },
+  "WebViewer" : {
+    "CachePath" : "/tmp/orthanc-db/WebViewerCache"
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/WebAssembly/NOTES.txt	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,76 @@
+Docker SJO
+==========
+
+$ source ~/Downloads/emsdk/emsdk_env.sh
+$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/tmp/stone
+$ ninja install
+$ docker run -p 4242:4242 -p 8042:8042 --rm -v /tmp/stone:/root/stone:ro -v /tmp/stone-db/:/var/lib/orthanc/db/ jodogne/orthanc-plugins:latest /root/stone/Configuration.json --verbose
+
+WARNING: This won't work using "orthanc-plugins:1.5.6", as support for
+PAM is mandatatory in "/instances/.../image-uint16".
+
+
+Docker BGO
+==========
+
+On Ubuntu WSL
+-------------
+. ~/apps/emsdk/emsdk_env.sh
+cd /mnt/c/osi/dev/
+mkdir -p build_stone_newsamples_wasm_wsl
+mkdir -p build_install_stone_newsamples_wasm_wsl
+cd build_stone_newsamples_wasm_wsl
+cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl
+ninja install
+
+Then, on Windows
+-----------------
+docker run -p 4242:4242 -p 8042:8042 --rm -v "C:/osi/dev/build_install_stone_newsamples_wasm_wsl:/root/stone:ro" jodogne/orthanc-plugins:1.5.6 /root/stone/Configuration.json --verbose
+
+# WAIT A COUPLE OF SECS
+# if the archive has NOT already been unzipped, unzip it
+# upload dicom files to running orthanc
+
+cd C:\osi\dev\twiga-orthanc-viewer\demo\dicomfiles
+if (-not (test-path RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57)) { unzip RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57.zip}
+ImportDicomFiles.ps1 127.0.0.1 8042 .\RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57\
+
+--> localhost:8042 --> Plugins --> serve-folders --> stone --> ...
+
+Local BGO
+==========
+
+. ~/apps/emsdk/emsdk_env.sh
+cd /mnt/c/osi/dev/
+mkdir -p build_stone_newsamples_wasm_wsl
+mkdir -p build_install_stone_newsamples_wasm_wsl
+cd build_stone_newsamples_wasm_wsl
+cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl
+
+
+
+TODO: Orthanc.exe 
+
+
+Local SJO
+==========
+
+$ source ~/Downloads/emsdk/emsdk_env.sh
+$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/tmp/stone
+$ ninja install
+
+$ make -C ~/Subversion/orthanc/r -j4
+$ make -C ~/Subversion/orthanc-webviewer/r -j4
+$ ~/Subversion/orthanc/r/Orthanc ../ConfigurationLocalSJO.json
+
+
+Local AM
+========
+
+. ~/apps/emsdk/emsdk_env.sh
+cd /mnt/c/o/
+mkdir -p build_stone_newsamples_wasm_wsl
+mkdir -p build_install_stone_newsamples_wasm_wsl
+cd build_stone_newsamples_wasm_wsl
+cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/fastcomp/emscripten/cmake/Modules/Platform/Emscripten.cmake -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=/mnt/c/o/orthanc/ -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/o/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/o/build_install_stone_newsamples_wasm_wsl
+ninja
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/WebAssembly/app.js	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,33 @@
+/**
+ * This is a generic bootstrap code that is shared by all the Stone
+ * sample applications.
+ **/
+
+// Check support for WebAssembly
+if (!('WebAssembly' in window)) {
+  alert('Sorry, your browser does not support WebAssembly :(');
+} else {
+
+  // Wait for the module to be loaded (the event "WebAssemblyLoaded"
+  // must be emitted by the "main" function)
+  window.addEventListener('WebAssemblyLoaded', function() {
+
+    // Loop over the GET arguments
+    var parameters = window.location.search.substr(1);
+    if (parameters != null && parameters != '') {
+      var tokens = parameters.split('&');
+      for (var i = 0; i < tokens.length; i++) {
+        var arg = tokens[i].split('=');
+        if (arg.length == 2) {
+
+          // Send each GET argument to WebAssembly
+          Module.ccall('SetArgument', null, [ 'string', 'string' ],
+                       [ arg[0], decodeURIComponent(arg[1]) ]);
+        }
+      }
+    }
+
+    // Inform the WebAssembly module that it can start
+    Module.ccall('Initialize', null, null, null);
+  });
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/WebAssembly/dev.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,202 @@
+/**
+ * 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 "../../Framework/Viewport/WebAssemblyViewport.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Scene2D/PanSceneTracker.h"
+#include "../../Framework/Scene2D/RotateSceneTracker.h"
+#include "../../Framework/Scene2D/ZoomSceneTracker.h"
+#include "../../Framework/Scene2DViewport/UndoStack.h"
+#include "../../Framework/Scene2DViewport/ViewportController.h"
+
+#include <Core/OrthancException.h>
+
+#include <emscripten/html5.h>
+#include <boost/make_shared.hpp>
+
+static const unsigned int FONT_SIZE = 32;
+
+namespace OrthancStone
+{
+  class ActiveTracker : public boost::noncopyable
+  {
+  private:
+    boost::shared_ptr<IFlexiblePointerTracker> tracker_;
+    std::string                             canvasIdentifier_;
+    bool                                    insideCanvas_;
+    
+  public:
+    ActiveTracker(const boost::shared_ptr<IFlexiblePointerTracker>& tracker,
+                  const std::string& canvasId) :
+      tracker_(tracker),
+      canvasIdentifier_(canvasId),
+      insideCanvas_(true)
+    {
+      if (tracker_.get() == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+    }
+
+    bool IsAlive() const
+    {
+      return tracker_->IsAlive();
+    }
+
+    void PointerMove(const PointerEvent& event)
+    {
+      tracker_->PointerMove(event);
+    }
+
+    void PointerUp(const PointerEvent& event)
+    {
+      tracker_->PointerUp(event);
+    }
+  };
+}
+
+static OrthancStone::PointerEvent* ConvertMouseEvent(
+  const EmscriptenMouseEvent&        source,
+  OrthancStone::IViewport& viewport)
+{
+  std::unique_ptr<OrthancStone::PointerEvent> target(
+    new OrthancStone::PointerEvent);
+
+  target->AddPosition(viewport.GetPixelCenterCoordinates(
+                        source.targetX, source.targetY));
+  target->SetAltModifier(source.altKey);
+  target->SetControlModifier(source.ctrlKey);
+  target->SetShiftModifier(source.shiftKey);
+
+  return target.release();
+}
+
+std::unique_ptr<OrthancStone::ActiveTracker> tracker_;
+
+EM_BOOL OnMouseEvent(int eventType, 
+                     const EmscriptenMouseEvent *mouseEvent, 
+                     void *userData)
+{
+  if (mouseEvent != NULL &&
+      userData != NULL)
+  {
+    boost::shared_ptr<OrthancStone::ViewportController>& controller = 
+      *reinterpret_cast<boost::shared_ptr<OrthancStone::ViewportController>*>(userData);
+
+    switch (eventType)
+    {
+      case EMSCRIPTEN_EVENT_CLICK:
+      {
+        static unsigned int count = 0;
+        char buf[64];
+        sprintf(buf, "click %d", count++);
+
+        std::unique_ptr<OrthancStone::TextSceneLayer> layer(new OrthancStone::TextSceneLayer);
+        layer->SetText(buf);
+        controller->GetViewport().GetScene().SetLayer(100, layer.release());
+        controller->GetViewport().Refresh();
+        break;
+      }
+
+      case EMSCRIPTEN_EVENT_MOUSEDOWN:
+      {
+        boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> t;
+
+        {
+          std::unique_ptr<OrthancStone::PointerEvent> event(
+            ConvertMouseEvent(*mouseEvent, controller->GetViewport()));
+
+          switch (mouseEvent->button)
+          {
+            case 0:  // Left button
+              emscripten_console_log("Creating RotateSceneTracker");
+              t.reset(new OrthancStone::RotateSceneTracker(
+                        controller, *event));
+              break;
+
+            case 1:  // Middle button
+              emscripten_console_log("Creating PanSceneTracker");
+              LOG(INFO) << "Creating PanSceneTracker" ;
+              t.reset(new OrthancStone::PanSceneTracker(
+                        controller, *event));
+              break;
+
+            case 2:  // Right button
+              emscripten_console_log("Creating ZoomSceneTracker");
+              t.reset(new OrthancStone::ZoomSceneTracker(
+                        controller, *event, controller->GetViewport().GetCanvasWidth()));
+              break;
+
+            default:
+              break;
+          }
+        }
+
+        if (t.get() != NULL)
+        {
+          tracker_.reset(
+            new OrthancStone::ActiveTracker(t, controller->GetViewport().GetCanvasIdentifier()));
+          controller->GetViewport().Refresh();
+        }
+
+        break;
+      }
+
+      case EMSCRIPTEN_EVENT_MOUSEMOVE:
+        if (tracker_.get() != NULL)
+        {
+          std::unique_ptr<OrthancStone::PointerEvent> event(
+            ConvertMouseEvent(*mouseEvent, controller->GetViewport()));
+          tracker_->PointerMove(*event);
+          controller->GetViewport().Refresh();
+        }
+        break;
+
+      case EMSCRIPTEN_EVENT_MOUSEUP:
+        if (tracker_.get() != NULL)
+        {
+          std::unique_ptr<OrthancStone::PointerEvent> event(
+            ConvertMouseEvent(*mouseEvent, controller->GetViewport()));
+          tracker_->PointerUp(*event);
+          controller->GetViewport().Refresh();
+          if (!tracker_->IsAlive())
+            tracker_.reset();
+        }
+        break;
+
+      default:
+        break;
+    }
+  }
+
+  return true;
+}
+
+
+void SetupEvents(const std::string& canvas,
+                 boost::shared_ptr<OrthancStone::ViewportController>& controller)
+{
+  emscripten_set_mousedown_callback(canvas.c_str(), &controller, false, OnMouseEvent);
+  emscripten_set_mousemove_callback(canvas.c_str(), &controller, false, OnMouseEvent);
+  emscripten_set_mouseup_callback(canvas.c_str(), &controller, false, OnMouseEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Deprecated/Samples/WebAssembly/index.html	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,16 @@
+<!doctype html>
+<html lang="en-us">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <title>Stone of Orthanc</title>
+  </head>
+  <body>
+    <h1>Available samples</h1>
+    <ul>
+      <li><a href="BasicScene.html">Basic scene</a></li>
+      <li><a href="BasicMPR.html">Basic MPR display</a></li>
+    </ul>
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyAlphaLayer.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,152 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographyAlphaLayer.h"
+
+#include "RadiographyScene.h"
+
+#include <Core/Compatibility.h>
+#include <Core/Images/Image.h>
+#include <Core/OrthancException.h>
+
+#include "../Toolbox/ImageGeometry.h"
+
+namespace OrthancStone
+{
+
+  void RadiographyAlphaLayer::SetAlpha(Orthanc::ImageAccessor* image)
+  {
+    std::unique_ptr<Orthanc::ImageAccessor> raii(image);
+
+    if (image == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+
+    if (image->GetFormat() != Orthanc::PixelFormat_Grayscale8)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+    }
+
+    SetSize(image->GetWidth(), image->GetHeight());
+
+#if __cplusplus < 201103L
+    alpha_.reset(raii.release());
+#else
+    alpha_ = std::move(raii);
+#endif
+
+    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
+  }
+
+  void RadiographyAlphaLayer::Render(Orthanc::ImageAccessor& buffer,
+                                     const AffineTransform2D& viewTransform,
+                                     ImageInterpolation interpolation,
+                                     float windowCenter,
+                                     float windowWidth,
+                                     bool applyWindowing) const
+  {
+    if (alpha_.get() == NULL)
+    {
+      return;
+    }
+
+    if (buffer.GetFormat() != Orthanc::PixelFormat_Float32)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+    }
+
+    unsigned int cropX, cropY, cropWidth, cropHeight;
+    GetCrop(cropX, cropY, cropWidth, cropHeight);
+
+    const AffineTransform2D t = AffineTransform2D::Combine(
+          viewTransform, GetTransform(),
+          AffineTransform2D::CreateOffset(cropX, cropY));
+
+    Orthanc::ImageAccessor cropped;
+    alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
+
+    Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false);
+
+    unsigned int x1, y1, x2, y2;
+
+    if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2,
+                                                    t.GetHomogeneousMatrix(),
+                                                    cropped.GetWidth(),
+                                                    cropped.GetHeight(),
+                                                    buffer.GetWidth(),
+                                                    buffer.GetHeight()))
+    {
+      return;  // layer is outside the buffer
+    }
+
+    t.Apply(tmp, cropped, interpolation, true /* clear */);
+
+    float value = foreground_;
+
+    if (!applyWindowing) // if applying the windowing, it means we are ie rendering the image for a realtime visualization -> the foreground_ value is the value we want to see on the screen -> don't change it
+    {
+      // if not applying the windowing, it means ie that we are saving a dicom image to file and the windowing will be applied by a viewer later on -> we want the "foreground" value to be correct once the windowing will be applied
+      value = windowCenter - windowWidth/2 + (foreground_ / 65535.0f) * windowWidth;
+
+      if (value < 0.0f)
+      {
+        value = 0.0f;
+      }
+      if (value > 65535.0f)
+      {
+        value = 65535.0f;
+      }
+    }
+
+    for (unsigned int y = y1; y <= y2; y++)
+    {
+      float *q = reinterpret_cast<float*>(buffer.GetRow(y)) + x1;
+      const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y)) + x1;
+
+      for (unsigned int x = x1; x <= x2; x++, p++, q++)
+      {
+        float a = static_cast<float>(*p) / 255.0f;
+
+        *q = (a * value + (1.0f - a) * (*q));
+      }
+    }
+  }
+
+  bool RadiographyAlphaLayer::GetRange(float& minValue,
+                                       float& maxValue) const
+  {
+    minValue = 0;
+    maxValue = 0;
+
+    if (foreground_ < 0)
+    {
+      minValue = foreground_;
+    }
+
+    if (foreground_ > 0)
+    {
+      maxValue = foreground_;
+    }
+
+    return true;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyAlphaLayer.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,85 @@
+/**
+ * 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 "RadiographyLayer.h"
+
+#include <Core/Compatibility.h>
+
+namespace OrthancStone
+{
+  class RadiographyScene;
+
+  // creates a transparent layer whose alpha channel is provided as a UINT8 image to SetAlpha.
+  // The color of the "mask" is either defined by a ForegroundValue or by the center value of the
+  // windowing from the scene.
+  class RadiographyAlphaLayer : public RadiographyLayer
+  {
+  private:
+    std::unique_ptr<Orthanc::ImageAccessor>  alpha_;       // Grayscale8 in the range [0, 255]  0 = transparent, 255 = opaque -> the foreground value will be displayed
+    float                                  foreground_;  // in the range [0.0, 65535.0]
+
+  public:
+    RadiographyAlphaLayer(const RadiographyScene& scene) :
+      RadiographyLayer(scene),
+      foreground_(0)
+    {
+    }
+
+
+    void SetForegroundValue(float foreground)
+    {
+      foreground_ = foreground;
+    }
+
+    float GetForegroundValue() const
+    {
+      return foreground_;
+    }
+
+    void SetAlpha(Orthanc::ImageAccessor* image);
+
+    virtual bool GetDefaultWindowing(float& center,
+                                     float& width) const
+    {
+      return false;
+    }
+
+
+    virtual void Render(Orthanc::ImageAccessor& buffer,
+                        const AffineTransform2D& viewTransform,
+                        ImageInterpolation interpolation,
+                        float windowCenter,
+                        float windowWidth,
+                        bool applyWindowing) const;
+
+    virtual bool GetRange(float& minValue,
+                          float& maxValue) const;
+
+    const Orthanc::ImageAccessor& GetAlpha() const
+    {
+      return *(alpha_.get());
+    }
+
+
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyDicomLayer.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,264 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographyDicomLayer.h"
+
+#include "RadiographyScene.h"
+#include "../Deprecated/Toolbox/DicomFrameConverter.h"
+
+#include <Core/OrthancException.h>
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Plugins/Samples/Common/DicomDatasetReader.h>
+#include "../Toolbox/ImageGeometry.h"
+
+static OrthancPlugins::DicomTag  ConvertTag(const Orthanc::DicomTag& tag)
+{
+  return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement());
+}
+
+namespace OrthancStone
+{
+
+  void RadiographyDicomLayer::ApplyConverter()
+  {
+    if (source_.get() != NULL &&
+        converter_.get() != NULL)
+    {
+      converted_.reset(converter_->ConvertFrame(*source_));
+    }
+  }
+
+
+  RadiographyDicomLayer::RadiographyDicomLayer(const RadiographyScene& scene) :
+    RadiographyLayer(scene)
+  {
+
+  }
+
+  void RadiographyDicomLayer::SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset)
+  {
+    converter_.reset(new Deprecated::DicomFrameConverter);
+    converter_->ReadParameters(dataset);
+    ApplyConverter();
+
+    std::string tmp;
+    Vector pixelSpacing;
+
+    if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) &&
+        LinearAlgebra::ParseVector(pixelSpacing, tmp) &&
+        pixelSpacing.size() == 2)
+    {
+      SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]);
+    }
+
+    OrthancPlugins::DicomDatasetReader reader(dataset);
+
+    unsigned int width, height;
+    if (!reader.GetUnsignedIntegerValue(width, ConvertTag(Orthanc::DICOM_TAG_COLUMNS)) ||
+        !reader.GetUnsignedIntegerValue(height, ConvertTag(Orthanc::DICOM_TAG_ROWS)))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+    else
+    {
+      SetSize(width, height);
+    }
+
+    if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION)))
+    {
+      if (tmp == "MONOCHROME1")
+      {
+        SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome1);
+      }
+      else if (tmp == "MONOCHROME2")
+      {
+        SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome2);
+      }
+    }
+  }
+
+  void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image)   // Takes ownership
+  {
+    std::unique_ptr<Orthanc::ImageAccessor> raii(image);
+
+    if (image == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+
+    SetSize(image->GetWidth(), image->GetHeight());
+
+#if __cplusplus < 201103L
+      source_.reset(raii.release());
+#else
+      source_ = std::move(raii);
+#endif
+
+    ApplyConverter();
+
+    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
+  }
+
+  void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY, bool emitLayerEditedEvent)   // Takes ownership
+  {
+    std::unique_ptr<Orthanc::ImageAccessor> raii(image);
+
+    if (image == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+
+    SetSize(image->GetWidth(), image->GetHeight(), false);
+
+#if __cplusplus < 201103L
+    source_.reset(raii.release());
+#else
+    source_ = std::move(raii);
+#endif
+
+    ApplyConverter();
+
+    SetPixelSpacing(newPixelSpacingX, newPixelSpacingY, false);
+
+    if (emitLayerEditedEvent)
+    {
+      BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
+    }
+  }
+
+
+  void RadiographyDicomLayer::SetDicomFrameConverter(Deprecated::DicomFrameConverter* converter)
+  {
+    converter_.reset(converter);
+  }
+
+  void RadiographyDicomLayer::Render(Orthanc::ImageAccessor& buffer,
+                                     const AffineTransform2D& viewTransform,
+                                     ImageInterpolation interpolation,
+                                     float windowCenter,
+                                     float windowWidth,
+                                     bool applyWindowing) const
+  {
+    if (converted_.get() != NULL)
+    {
+      if (converted_->GetFormat() != Orthanc::PixelFormat_Float32)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+
+      unsigned int cropX, cropY, cropWidth, cropHeight;
+      GetCrop(cropX, cropY, cropWidth, cropHeight);
+
+      AffineTransform2D t = AffineTransform2D::Combine(
+            viewTransform, GetTransform(),
+            AffineTransform2D::CreateOffset(cropX, cropY));
+
+      Orthanc::ImageAccessor cropped;
+      converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
+
+      unsigned int x1, y1, x2, y2;
+      if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2,
+                                                      t.GetHomogeneousMatrix(),
+                                                      cropped.GetWidth(),
+                                                      cropped.GetHeight(),
+                                                      buffer.GetWidth(),
+                                                      buffer.GetHeight()))
+      {
+        return;  // layer is outside the buffer
+      }
+
+      t.Apply(buffer, cropped, interpolation, false);
+
+      if (applyWindowing)
+      {
+        // apply windowing but stay in the range [0.0, 65535.0]
+        float w0 = windowCenter - windowWidth / 2.0f;
+        float w1 = windowCenter + windowWidth / 2.0f;
+
+        if (windowWidth >= 0.001f)  // Avoid division by zero at (*)
+        {
+          float scaling = 1.0f / (w1 - w0) * 65535.0f;
+          for (unsigned int y = y1; y <= y2; y++)
+          {
+            float* p = reinterpret_cast<float*>(buffer.GetRow(y)) + x1;
+
+            for (unsigned int x = x1; x <= x2; x++, p++)
+            {
+              if (*p >= w1)
+              {
+                *p = 65535.0;
+              }
+              else if (*p <= w0)
+              {
+                *p = 0;
+              }
+              else
+              {
+                // https://en.wikipedia.org/wiki/Linear_interpolation
+                *p = scaling * (*p - w0);  // (*)
+              }
+            }
+          }
+        }
+      }
+
+    }
+  }
+
+
+  bool RadiographyDicomLayer::GetDefaultWindowing(float& center,
+                                                  float& width) const
+  {
+    if (converter_.get() != NULL &&
+        converter_->HasDefaultWindow())
+    {
+      center = static_cast<float>(converter_->GetDefaultWindowCenter());
+      width = static_cast<float>(converter_->GetDefaultWindowWidth());
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  bool RadiographyDicomLayer::GetRange(float& minValue,
+                                       float& maxValue) const
+  {
+    if (converted_.get() != NULL)
+    {
+      if (converted_->GetFormat() != Orthanc::PixelFormat_Float32)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+
+      Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, *converted_);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyDicomLayer.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,105 @@
+/**
+ * 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 "../Deprecated/Toolbox/DicomFrameConverter.h"
+#include "RadiographyLayer.h"
+
+#include <Plugins/Samples/Common/FullOrthancDataset.h>
+
+namespace OrthancStone
+{
+  class RadiographyScene;
+
+  class RadiographyDicomLayer : public RadiographyLayer
+  {
+  private:
+    std::unique_ptr<Orthanc::ImageAccessor>  source_;  // Content of PixelData
+    std::unique_ptr<Deprecated::DicomFrameConverter>     converter_;
+    std::unique_ptr<Orthanc::ImageAccessor>  converted_;  // Float32
+    std::string                            instanceId_;
+    unsigned int                           frame_;
+
+    void ApplyConverter();
+
+  public:
+    RadiographyDicomLayer(const RadiographyScene& scene);
+
+    void SetInstance(const std::string& instanceId, unsigned int frame)
+    {
+      instanceId_ = instanceId;
+      frame_ = frame;
+    }
+
+    std::string GetInstanceId() const
+    {
+      return instanceId_;
+    }
+
+    unsigned int GetFrame() const
+    {
+      return frame_;
+    }
+
+    virtual size_t GetApproximateMemoryUsage() const
+    {
+      size_t size = 0;
+      if (source_.get() != NULL)
+      {
+        size += source_->GetPitch() * source_->GetHeight();
+      }
+      if (converted_.get() != NULL)
+      {
+        size += converted_->GetPitch() * converted_->GetHeight();
+      }
+
+      return size;
+    }
+
+
+    void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset);
+
+    void SetSourceImage(Orthanc::ImageAccessor* image);   // Takes ownership
+
+    void SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY, bool emitLayerEditedEvent = true);   // Takes ownership
+
+    const Orthanc::ImageAccessor* GetSourceImage() const {return source_.get();}  // currently need this access to serialize scene in plain old data to send to a WASM worker
+
+    const Deprecated::DicomFrameConverter& GetDicomFrameConverter() const {return *converter_;} // currently need this access to serialize scene in plain old data to send to a WASM worker
+    
+     // Takes ownership
+    void SetDicomFrameConverter(Deprecated::DicomFrameConverter* converter);
+
+    virtual void Render(Orthanc::ImageAccessor& buffer,
+                        const AffineTransform2D& viewTransform,
+                        ImageInterpolation interpolation,
+                        float windowCenter,
+                        float windowWidth,
+                        bool applyWindowing) const;
+
+    virtual bool GetDefaultWindowing(float& center,
+                                     float& width) const;
+
+    virtual bool GetRange(float& minValue,
+                          float& maxValue) const;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyLayer.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,402 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographyLayer.h"
+
+#include <Core/OrthancException.h>
+
+
+namespace OrthancStone
+{
+  static double Square(double x)
+  {
+    return x * x;
+  }
+
+
+  RadiographyLayer::Geometry::Geometry() :
+    hasCrop_(false),
+    flipVertical_(false),
+    flipHorizontal_(false),
+    panX_(0),
+    panY_(0),
+    angle_(0),
+    resizeable_(false),
+    pixelSpacingX_(1),
+    pixelSpacingY_(1)
+  {
+
+  }
+
+  void RadiographyLayer::Geometry::GetCrop(unsigned int &x, unsigned int &y, unsigned int &width, unsigned int &height) const
+  {
+    if (!hasCrop_)
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);  // you should probably use RadiographyLayer::GetCrop() or at least call HasCrop() before
+
+    x = cropX_;
+    y = cropY_;
+    width = cropWidth_;
+    height = cropHeight_;
+  }
+
+  void RadiographyLayer::UpdateTransform()
+  {
+    // important to update transform_ before getting the center to use the right scaling !!!
+    transform_ = AffineTransform2D::CreateScaling(geometry_.GetScalingX(), geometry_.GetScalingY());
+
+    double centerX, centerY;
+    GetCenter(centerX, centerY);
+
+    transform_ = AffineTransform2D::Combine(
+          AffineTransform2D::CreateOffset(geometry_.GetPanX(), geometry_.GetPanY()),
+          AffineTransform2D::CreateRotation(geometry_.GetAngle(), centerX, centerY),
+          transform_);
+
+    transformInverse_ = AffineTransform2D::Invert(transform_);
+  }
+
+
+  void RadiographyLayer::AddToExtent(Extent2D& extent,
+                                     double x,
+                                     double y) const
+  {
+    GetTransform().Apply(x, y);
+    extent.AddPoint(x, y);
+  }
+
+  bool RadiographyLayer::Contains(double x,
+                                  double y) const
+  {
+    GetTransformInverse().Apply(x, y);
+
+    unsigned int cropX, cropY, cropWidth, cropHeight;
+    GetCrop(cropX, cropY, cropWidth, cropHeight);
+
+    return (x >= cropX && x <= cropX + cropWidth &&
+            y >= cropY && y <= cropY + cropHeight);
+  }
+
+
+  void RadiographyLayer::DrawBorders(CairoContext& context,
+                                     double zoom)
+  {
+    if (GetControlPointCount() < 3 )
+      return;
+
+    cairo_t* cr = context.GetObject();
+    cairo_set_line_width(cr, 2.0 / zoom);
+
+    ControlPoint cp;
+    GetControlPoint(cp, 0);
+    cairo_move_to(cr, cp.x, cp.y);
+
+    for (size_t i = 0; i < GetControlPointCount(); i++)
+    {
+      GetControlPoint(cp, i);
+      cairo_line_to(cr, cp.x, cp.y);
+    }
+
+    cairo_close_path(cr);
+    cairo_stroke(cr);
+  }
+
+
+  RadiographyLayer::RadiographyLayer(const RadiographyScene& scene) :
+    index_(0),
+    hasSize_(false),
+    width_(0),
+    height_(0),
+    prefferedPhotometricDisplayMode_(RadiographyPhotometricDisplayMode_Default),
+    scene_(scene)
+  {
+    UpdateTransform();
+  }
+
+  void RadiographyLayer::ResetCrop()
+  {
+    geometry_.ResetCrop();
+    UpdateTransform();
+  }
+
+  void RadiographyLayer::SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode  prefferedPhotometricDisplayMode)
+  {
+    prefferedPhotometricDisplayMode_ = prefferedPhotometricDisplayMode;
+
+    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
+  }
+
+  void RadiographyLayer::SetCrop(unsigned int x,
+                                 unsigned int y,
+                                 unsigned int width,
+                                 unsigned int height)
+  {
+    if (!hasSize_)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+
+    if (x + width > width_ ||
+        y + height > height_)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    geometry_.SetCrop(x, y, width, height);
+    UpdateTransform();
+
+    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
+  }
+
+  void RadiographyLayer::SetGeometry(const Geometry& geometry)
+  {
+    geometry_ = geometry;
+
+    if (hasSize_)
+    {
+      UpdateTransform();
+    }
+
+    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
+  }
+
+
+  void RadiographyLayer::GetCrop(unsigned int& x,
+                                 unsigned int& y,
+                                 unsigned int& width,
+                                 unsigned int& height) const
+  {
+    if (GetGeometry().HasCrop())
+    {
+      GetGeometry().GetCrop(x, y, width, height);
+    }
+    else
+    {
+      x = 0;
+      y = 0;
+      width = width_;
+      height = height_;
+    }
+  }
+
+  
+  void RadiographyLayer::SetAngle(double angle)
+  {
+    geometry_.SetAngle(angle);
+    UpdateTransform();
+
+    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
+  }
+
+  void RadiographyLayer::SetFlipVertical(bool flip)
+  {
+    geometry_.SetFlipVertical(flip);
+    UpdateTransform();
+
+    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
+  }
+
+  void RadiographyLayer::SetFlipHorizontal(bool flip)
+  {
+    geometry_.SetFlipHorizontal(flip);
+    UpdateTransform();
+
+    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
+  }
+
+  void RadiographyLayer::SetSize(unsigned int width,
+                                 unsigned int height,
+                                 bool emitLayerEditedEvent)
+  {
+    hasSize_ = true;
+    width_ = width;
+    height_ = height;
+
+    UpdateTransform();
+
+    if (emitLayerEditedEvent)
+    {
+      BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
+    }
+  }
+
+  Extent2D RadiographyLayer::GetSceneExtent(bool /*minimal*/) const
+  {
+    Extent2D extent;
+
+    unsigned int x, y, width, height;
+    GetCrop(x, y, width, height);
+
+    double dx = static_cast<double>(x);
+    double dy = static_cast<double>(y);
+    double dwidth = static_cast<double>(width);
+    double dheight = static_cast<double>(height);
+
+    // AddToExtent transforms the coordinates from image to scene
+    AddToExtent(extent, dx, dy);
+    AddToExtent(extent, dx + dwidth, dy);
+    AddToExtent(extent, dx, dy + dheight);
+    AddToExtent(extent, dx + dwidth, dy + dheight);
+
+    return extent;
+  }
+
+
+  bool RadiographyLayer::GetPixel(unsigned int& imageX,
+                                  unsigned int& imageY,
+                                  double sceneX,
+                                  double sceneY) const
+  {
+    if (width_ == 0 ||
+        height_ == 0)
+    {
+      return false;
+    }
+    else
+    {
+      GetTransformInverse().Apply(sceneX, sceneY);
+
+      int x = static_cast<int>(std::floor(sceneX));
+      int y = static_cast<int>(std::floor(sceneY));
+
+      if (x < 0)
+      {
+        imageX = 0;
+      }
+      else if (x >= static_cast<int>(width_))
+      {
+        imageX = width_;
+      }
+      else
+      {
+        imageX = static_cast<unsigned int>(x);
+      }
+
+      if (y < 0)
+      {
+        imageY = 0;
+      }
+      else if (y >= static_cast<int>(height_))
+      {
+        imageY = height_;
+      }
+      else
+      {
+        imageY = static_cast<unsigned int>(y);
+      }
+
+      return true;
+    }
+  }
+
+
+  void RadiographyLayer::SetPan(double x,
+                                double y)
+  {
+    geometry_.SetPan(x, y);
+    UpdateTransform();
+    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
+  }
+
+
+  void RadiographyLayer::SetPixelSpacing(double x,
+                                         double y,
+                                         bool emitLayerEditedEvent)
+  {
+    geometry_.SetPixelSpacing(x, y);
+    UpdateTransform();
+    if (emitLayerEditedEvent)
+    {
+      BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
+    }
+  }
+
+
+  void RadiographyLayer::GetCenter(double& centerX,
+                                   double& centerY) const
+  {
+    centerX = static_cast<double>(width_) / 2.0;
+    centerY = static_cast<double>(height_) / 2.0;
+    GetTransform().Apply(centerX, centerY);
+  }
+
+
+
+  size_t RadiographyLayer::GetControlPointCount() const {return 4;}
+
+  void RadiographyLayer::GetControlPoint(ControlPoint& cpScene /* out in scene coordinates */,
+                                         size_t index) const
+  {
+    unsigned int cropX, cropY, cropWidth, cropHeight;
+    GetCrop(cropX, cropY, cropWidth, cropHeight);
+
+    ControlPoint cp;
+    switch (index)
+    {
+      case RadiographyControlPointType_TopLeftCorner:
+        cp = ControlPoint(cropX, cropY, RadiographyControlPointType_TopLeftCorner);
+        break;
+
+      case RadiographyControlPointType_TopRightCorner:
+        cp = ControlPoint(cropX + cropWidth, cropY, RadiographyControlPointType_TopRightCorner);
+        break;
+
+      case RadiographyControlPointType_BottomLeftCorner:
+        cp = ControlPoint(cropX, cropY + cropHeight, RadiographyControlPointType_BottomLeftCorner);
+        break;
+
+      case RadiographyControlPointType_BottomRightCorner:
+        cp = ControlPoint(cropX + cropWidth, cropY + cropHeight, RadiographyControlPointType_BottomRightCorner);
+        break;
+
+    default:
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    // transforms image coordinates into scene coordinates
+    GetTransform().Apply(cp.x, cp.y);
+    cpScene = cp;
+  }
+
+  bool RadiographyLayer::LookupControlPoint(ControlPoint& cpScene /* out */,
+                                            double x,
+                                            double y,
+                                            double zoom,
+                                            double viewportDistance) const
+  {
+    double threshold = Square(viewportDistance / zoom);
+
+    for (size_t i = 0; i < GetControlPointCount(); i++)
+    {
+      ControlPoint cp;
+      GetControlPoint(cp, i);
+
+      double d = Square(cp.x - x) + Square(cp.y - y);
+
+      if (d <= threshold)
+      {
+        cpScene = cp;
+        return true;
+      }
+    }
+
+    return false;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyLayer.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,395 @@
+/**
+ * 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 <algorithm>
+
+#include "../Toolbox/AffineTransform2D.h"
+#include "../Toolbox/Extent2D.h"
+#include "../Wrappers/CairoContext.h"
+#include "../Messages/IMessage.h"
+#include "../Messages/IObservable.h"
+
+namespace OrthancStone
+{
+  class RadiographyScene;
+
+  enum RadiographyControlPointType
+  {
+    RadiographyControlPointType_TopLeftCorner = 0,
+    RadiographyControlPointType_TopRightCorner = 1,
+    RadiographyControlPointType_BottomRightCorner = 2,
+    RadiographyControlPointType_BottomLeftCorner = 3
+  };
+
+  enum RadiographyPhotometricDisplayMode
+  {
+    RadiographyPhotometricDisplayMode_Default,
+
+    RadiographyPhotometricDisplayMode_Monochrome1,
+    RadiographyPhotometricDisplayMode_Monochrome2
+  };
+
+  
+  struct ControlPoint
+  {
+    double x;
+    double y;
+    size_t index;
+
+    ControlPoint(double x, double y, size_t index)
+      : x(x),
+        y(y),
+        index(index)
+    {}
+
+    ControlPoint()
+      : x(0),
+        y(0),
+        index(std::numeric_limits<size_t>::max())
+    {}
+  };
+
+  class RadiographyLayer : public IObservable
+  {
+    friend class RadiographyScene;
+
+  public:
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, LayerEditedMessage, RadiographyLayer);
+
+    class Geometry
+    {
+      bool               hasCrop_;
+      unsigned int       cropX_;
+      unsigned int       cropY_;
+      unsigned int       cropWidth_;
+      unsigned int       cropHeight_;
+      bool               flipVertical_;
+      bool               flipHorizontal_;
+      double             panX_;
+      double             panY_;
+      double             angle_;
+      bool               resizeable_;
+      double             pixelSpacingX_;
+      double             pixelSpacingY_;
+
+    public:
+      Geometry();
+
+      void ResetCrop()
+      {
+        hasCrop_ = false;
+      }
+
+      void SetCrop(unsigned int x,
+                   unsigned int y,
+                   unsigned int width,
+                   unsigned int height)
+      {
+        hasCrop_ = true;
+        cropX_ = x;
+        cropY_ = y;
+        cropWidth_ = width;
+        cropHeight_ = height;
+      }
+
+      bool HasCrop() const
+      {
+        return hasCrop_;
+      }
+
+      void GetCrop(unsigned int& x,
+                   unsigned int& y,
+                   unsigned int& width,
+                   unsigned int& height) const;
+
+      void SetAngle(double angle)
+      {
+        angle_ = angle;
+      }
+
+      double GetAngle() const
+      {
+        return angle_;
+      }
+
+      void SetPan(double x,
+                  double y)
+      {
+        panX_ = x;
+        panY_ = y;
+      }
+
+      double GetPanX() const
+      {
+        return panX_;
+      }
+
+      double GetPanY() const
+      {
+        return panY_;
+      }
+
+      bool IsResizeable() const
+      {
+        return resizeable_;
+      }
+
+      void SetResizeable(bool resizeable)
+      {
+        resizeable_ = resizeable;
+      }
+
+      void SetPixelSpacing(double x,
+                           double y)
+      {
+        pixelSpacingX_ = x;
+        pixelSpacingY_ = y;
+      }
+
+      double GetPixelSpacingX() const
+      {
+        return pixelSpacingX_;
+      }
+
+      double GetPixelSpacingY() const
+      {
+        return pixelSpacingY_;
+      }
+
+      void SetFlipVertical(bool flip) //  mirrors image around an horizontal axis (note: flip is applied before the rotation !)
+      {
+        flipVertical_ = flip;
+      }
+
+      void SetFlipHorizontal(bool flip) //  mirrors image around a vertical axis (note: flip is applied before the rotation !)
+      {
+        flipHorizontal_ = flip;
+      }
+
+      bool GetFlipVertical() const
+      {
+        return flipVertical_;
+      }
+
+      bool GetFlipHorizontal() const
+      {
+        return flipHorizontal_;
+      }
+
+      double GetScalingX() const
+      {
+        return (flipHorizontal_ ? - pixelSpacingX_: pixelSpacingX_);
+      }
+
+      double GetScalingY() const
+      {
+        return (flipVertical_ ? - pixelSpacingY_: pixelSpacingY_);
+      }
+    };
+
+  private:
+    size_t             index_;
+    bool               hasSize_;
+    unsigned int       width_;
+    unsigned int       height_;
+    AffineTransform2D  transform_;
+    AffineTransform2D  transformInverse_;
+    Geometry           geometry_;
+    RadiographyPhotometricDisplayMode  prefferedPhotometricDisplayMode_;
+    const RadiographyScene&   scene_;
+
+  protected:
+    void SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode  prefferedPhotometricDisplayMode);
+
+  private:
+    void UpdateTransform();
+
+    void AddToExtent(Extent2D& extent,
+                     double x,
+                     double y) const;
+
+    void SetIndex(size_t index)
+    {
+      index_ = index;
+    }
+
+    bool Contains(double x,
+                  double y) const;
+
+    void DrawBorders(CairoContext& context,
+                     double zoom);
+
+  public:
+    RadiographyLayer(const RadiographyScene& scene);
+
+    virtual ~RadiographyLayer()
+    {
+    }
+
+    virtual const AffineTransform2D& GetTransform() const
+    {
+      return transform_;
+    }
+
+    virtual const AffineTransform2D& GetTransformInverse() const
+    {
+      return transformInverse_;
+    }
+
+    size_t GetIndex() const
+    {
+      return index_;
+    }
+
+    const RadiographyScene& GetScene() const
+    {
+      return scene_;
+    }
+
+    const Geometry& GetGeometry() const
+    {
+      return geometry_;
+    }
+
+    void SetGeometry(const Geometry& geometry);
+
+    void ResetCrop();
+
+    void SetCrop(unsigned int x,       // those are pixel coordinates/size
+                 unsigned int y,
+                 unsigned int width,
+                 unsigned int height);
+
+    void SetCrop(const Extent2D& sceneExtent)
+    {
+      Extent2D imageCrop;
+
+      {
+        double x = sceneExtent.GetX1();
+        double y = sceneExtent.GetY1();
+        GetTransformInverse().Apply(x, y);
+        imageCrop.AddPoint(x, y);
+      }
+
+      {
+        double x = sceneExtent.GetX2();
+        double y = sceneExtent.GetY2();
+        GetTransformInverse().Apply(x, y);
+        imageCrop.AddPoint(x, y);
+      }
+
+      SetCrop(static_cast<unsigned int>(std::max(0.0, std::floor(imageCrop.GetX1()))),
+              static_cast<unsigned int>(std::max(0.0, std::floor(imageCrop.GetY1()))),
+              std::min(width_, static_cast<unsigned int>(std::ceil(imageCrop.GetWidth()))),
+              std::min(height_, static_cast<unsigned int>(std::ceil(imageCrop.GetHeight())))
+              );
+    }
+
+
+    void GetCrop(unsigned int& x,
+                 unsigned int& y,
+                 unsigned int& width,
+                 unsigned int& height) const;
+
+    void SetAngle(double angle);
+
+    void SetPan(double x,
+                double y);
+
+    void SetFlipVertical(bool flip); //  mirrors image around an horizontal axis (note: flip is applied before the rotation !)
+
+    void SetFlipHorizontal(bool flip); //  mirrors image around a vertical axis (note: flip is applied before the rotation !)
+
+    void SetResizeable(bool resizeable)
+    {
+      geometry_.SetResizeable(resizeable);
+    }
+
+    void SetSize(unsigned int width,
+                 unsigned int height,
+                 bool emitLayerEditedEvent = true);
+
+    bool HasSize() const
+    {
+      return hasSize_;
+    }
+
+    unsigned int GetWidth() const
+    {
+      return width_;
+    }
+
+    unsigned int GetHeight() const
+    {
+      return height_;
+    }
+
+    virtual Extent2D GetSceneExtent(bool minimal) const;
+
+    virtual bool GetPixel(unsigned int& imageX,
+                          unsigned int& imageY,
+                          double sceneX,
+                          double sceneY) const;
+
+    void SetPixelSpacing(double x,
+                         double y,
+                         bool emitLayerEditedEvent = true);
+
+    void GetCenter(double& centerX,
+                   double& centerY) const;
+
+    virtual void GetControlPoint(ControlPoint& cpScene /* out in scene coordinates */,
+                                 size_t index) const;
+
+    virtual size_t GetControlPointCount() const;
+
+    bool LookupControlPoint(ControlPoint& cpScene /* out */,
+                            double x,
+                            double y,
+                            double zoom,
+                            double viewportDistance) const;
+
+    virtual bool GetDefaultWindowing(float& center,
+                                     float& width) const = 0;
+
+    RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const
+    {
+      return prefferedPhotometricDisplayMode_;
+    }
+
+    virtual void Render(Orthanc::ImageAccessor& buffer,
+                        const AffineTransform2D& viewTransform,
+                        ImageInterpolation interpolation,
+                        float windowCenter,
+                        float windowWidth,
+                        bool applyWindowing) const = 0;
+
+    virtual bool GetRange(float& minValue,
+                          float& maxValue) const = 0;
+
+    virtual size_t GetApproximateMemoryUsage() const // this is used to limit the number of scenes loaded in RAM when resources are limited (we actually only count the size used by the images, not the C structs)
+    {
+      return 0;
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyLayerCropTracker.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,145 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographyLayerCropTracker.h"
+
+#include "RadiographySceneCommand.h"
+
+#include <Core/OrthancException.h>
+
+namespace OrthancStone
+{
+  class RadiographyLayerCropTracker::UndoRedoCommand : public RadiographySceneCommand
+  {
+  private:
+    unsigned int  sourceCropX_;
+    unsigned int  sourceCropY_;
+    unsigned int  sourceCropWidth_;
+    unsigned int  sourceCropHeight_;
+    unsigned int  targetCropX_;
+    unsigned int  targetCropY_;
+    unsigned int  targetCropWidth_;
+    unsigned int  targetCropHeight_;
+
+  protected:
+    virtual void UndoInternal(RadiographyLayer& layer) const
+    {
+      layer.SetCrop(sourceCropX_, sourceCropY_, sourceCropWidth_, sourceCropHeight_);
+    }
+
+    virtual void RedoInternal(RadiographyLayer& layer) const
+    {
+      layer.SetCrop(targetCropX_, targetCropY_, targetCropWidth_, targetCropHeight_);
+    }
+
+  public:
+    UndoRedoCommand(const RadiographyLayerCropTracker& tracker) :
+      RadiographySceneCommand(tracker.accessor_),
+      sourceCropX_(tracker.cropX_),
+      sourceCropY_(tracker.cropY_),
+      sourceCropWidth_(tracker.cropWidth_),
+      sourceCropHeight_(tracker.cropHeight_)
+    {
+      tracker.accessor_.GetLayer().GetCrop(targetCropX_, targetCropY_,
+                                           targetCropWidth_, targetCropHeight_);
+    }
+  };
+
+
+  RadiographyLayerCropTracker::RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack,
+                                                           RadiographyScene& scene,
+                                                           const Deprecated::ViewportGeometry& view,
+                                                           size_t layer,
+                                                           const ControlPoint& startControlPoint) :
+    undoRedoStack_(undoRedoStack),
+    accessor_(scene, layer),
+    startControlPoint_(startControlPoint)
+  {
+    if (accessor_.IsValid())
+    {
+      accessor_.GetLayer().GetCrop(cropX_, cropY_, cropWidth_, cropHeight_);
+    }
+  }
+
+
+  void RadiographyLayerCropTracker::Render(CairoContext& context,
+                                           double zoom)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+
+
+  void RadiographyLayerCropTracker::MouseUp()
+  {
+    if (accessor_.IsValid())
+    {
+      undoRedoStack_.Add(new UndoRedoCommand(*this));
+    }
+  }
+
+  
+  void RadiographyLayerCropTracker::MouseMove(int displayX,
+                                              int displayY,
+                                              double sceneX,
+                                              double sceneY,
+                                              const std::vector<Deprecated::Touch>& displayTouches,
+                                              const std::vector<Deprecated::Touch>& sceneTouches)
+  {
+    if (accessor_.IsValid())
+    {
+      unsigned int x, y;
+        
+      RadiographyLayer& layer = accessor_.GetLayer();
+      if (layer.GetPixel(x, y, sceneX, sceneY))
+      {
+        unsigned int targetX, targetWidth;
+
+        if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner ||
+            startControlPoint_.index == RadiographyControlPointType_BottomLeftCorner)
+        {
+          targetX = std::min(x, cropX_ + cropWidth_);
+          targetWidth = cropX_ + cropWidth_ - targetX;
+        }
+        else
+        {
+          targetX = cropX_;
+          targetWidth = std::max(x, cropX_) - cropX_;
+        }
+
+        unsigned int targetY, targetHeight;
+
+        if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner ||
+            startControlPoint_.index == RadiographyControlPointType_TopRightCorner)
+        {
+          targetY = std::min(y, cropY_ + cropHeight_);
+          targetHeight = cropY_ + cropHeight_ - targetY;
+        }
+        else
+        {
+          targetY = cropY_;
+          targetHeight = std::max(y, cropY_) - cropY_;
+        }
+
+        layer.SetCrop(targetX, targetY, targetWidth, targetHeight);
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyLayerCropTracker.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,68 @@
+/**
+ * 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 "../Toolbox/UndoRedoStack.h"
+#include "../Deprecated/Toolbox/ViewportGeometry.h"
+#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
+#include "RadiographyScene.h"
+
+namespace OrthancStone
+{
+  class RadiographyLayerCropTracker : public Deprecated::IWorldSceneMouseTracker
+  {
+  private:
+    class UndoRedoCommand;
+
+    UndoRedoStack&                   undoRedoStack_;
+    RadiographyScene::LayerAccessor  accessor_;
+    ControlPoint                     startControlPoint_;
+    unsigned int                     cropX_;
+    unsigned int                     cropY_;
+    unsigned int                     cropWidth_;
+    unsigned int                     cropHeight_;
+
+  public:
+    RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack,
+                                RadiographyScene& scene,
+                                const Deprecated::ViewportGeometry& view,
+                                size_t layer,
+                                const ControlPoint& startControlPoint);
+
+    virtual bool HasRender() const
+    {
+      return false;
+    }
+
+    virtual void Render(CairoContext& context,
+                        double zoom);
+
+    virtual void MouseUp();
+
+    virtual void MouseMove(int displayX,
+                           int displayY,
+                           double sceneX,
+                           double sceneY,
+                           const std::vector<Deprecated::Touch>& displayTouches,
+                           const std::vector<Deprecated::Touch>& sceneTouches);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyLayerMaskTracker.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,140 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographyLayerMaskTracker.h"
+#include "RadiographyMaskLayer.h"
+
+#include "RadiographySceneCommand.h"
+
+#include <Core/OrthancException.h>
+
+namespace OrthancStone
+{
+  class RadiographyLayerMaskTracker::UndoRedoCommand : public RadiographySceneCommand
+  {
+  private:
+    ControlPoint sourceSceneCp_;
+    ControlPoint targetSceneCp_;
+
+  protected:
+    virtual void UndoInternal(RadiographyLayer& layer) const
+    {
+      RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&layer);
+      if (maskLayer == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+      unsigned int ix, iy; // image coordinates
+      if (maskLayer->GetPixel(ix, iy, sourceSceneCp_.x, sourceSceneCp_.y))
+      {
+        maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), sourceSceneCp_.index);
+      }
+    }
+
+    virtual void RedoInternal(RadiographyLayer& layer) const
+    {
+      RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&layer);
+      if (maskLayer == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+      unsigned int ix, iy; // image coordinates
+      if (maskLayer->GetPixel(ix, iy, targetSceneCp_.x, targetSceneCp_.y))
+      {
+        maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), targetSceneCp_.index);
+      }
+    }
+
+  public:
+    UndoRedoCommand(const RadiographyLayerMaskTracker& tracker) :
+      RadiographySceneCommand(tracker.accessor_),
+      sourceSceneCp_(tracker.startSceneCp_),
+      targetSceneCp_(tracker.endSceneCp_)
+    {
+      RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&(tracker.accessor_.GetLayer()));
+      if (maskLayer == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+      unsigned int ix, iy; // image coordinates
+      if (maskLayer->GetPixel(ix, iy, targetSceneCp_.x, targetSceneCp_.y))
+      {
+        maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), targetSceneCp_.index);
+      }
+    }
+  };
+
+
+  RadiographyLayerMaskTracker::RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack,
+                                                           RadiographyScene& scene,
+                                                           const Deprecated::ViewportGeometry& view,
+                                                           size_t layer,
+                                                           const ControlPoint& startSceneControlPoint) :
+    undoRedoStack_(undoRedoStack),
+    accessor_(scene, layer),
+    startSceneCp_(startSceneControlPoint),
+    endSceneCp_(startSceneControlPoint)
+  {
+  }
+
+
+  void RadiographyLayerMaskTracker::Render(CairoContext& context,
+                                           double zoom)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+
+
+  void RadiographyLayerMaskTracker::MouseUp()
+  {
+    if (accessor_.IsValid() && startSceneCp_.x != endSceneCp_.x && startSceneCp_.y != endSceneCp_.y)
+    {
+      undoRedoStack_.Add(new UndoRedoCommand(*this));
+    }
+  }
+
+  
+  void RadiographyLayerMaskTracker::MouseMove(int displayX,
+                                              int displayY,
+                                              double sceneX,
+                                              double sceneY,
+                                              const std::vector<Deprecated::Touch>& displayTouches,
+                                              const std::vector<Deprecated::Touch>& sceneTouches)
+  {
+    if (accessor_.IsValid())
+    {
+      unsigned int ix, iy; // image coordinates
+
+      RadiographyLayer& layer = accessor_.GetLayer();
+      if (layer.GetPixel(ix, iy, sceneX, sceneY))
+      {
+        endSceneCp_ = ControlPoint(sceneX, sceneY, startSceneCp_.index);
+
+        RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&layer);
+        if (maskLayer == NULL)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        }
+        maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), startSceneCp_.index);
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyLayerMaskTracker.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,65 @@
+/**
+ * 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 "../Toolbox/UndoRedoStack.h"
+#include "../Deprecated/Toolbox/ViewportGeometry.h"
+#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
+#include "RadiographyScene.h"
+
+namespace OrthancStone
+{
+  class RadiographyLayerMaskTracker : public Deprecated::IWorldSceneMouseTracker
+  {
+  private:
+    class UndoRedoCommand;
+
+    UndoRedoStack&                   undoRedoStack_;
+    RadiographyScene::LayerAccessor  accessor_;
+    ControlPoint                     startSceneCp_;
+    ControlPoint                     endSceneCp_;
+
+  public:
+    RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack,
+                                RadiographyScene& scene,
+                                const Deprecated::ViewportGeometry& view,
+                                size_t layer,
+                                const ControlPoint& startSceneControlPoint);
+
+    virtual bool HasRender() const
+    {
+      return false;
+    }
+
+    virtual void Render(CairoContext& context,
+                        double zoom);
+
+    virtual void MouseUp();
+
+    virtual void MouseMove(int displayX,
+                           int displayY,
+                           double sceneX,
+                           double sceneY,
+                           const std::vector<Deprecated::Touch>& displayTouches,
+                           const std::vector<Deprecated::Touch>& sceneTouches);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyLayerMoveTracker.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,126 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographyLayerMoveTracker.h"
+
+#include "RadiographySceneCommand.h"
+
+#include <Core/OrthancException.h>
+
+namespace OrthancStone
+{
+  class RadiographyLayerMoveTracker::UndoRedoCommand : public RadiographySceneCommand
+  {
+  private:
+    double  sourceX_;
+    double  sourceY_;
+    double  targetX_;
+    double  targetY_;
+
+  protected:
+    virtual void UndoInternal(RadiographyLayer& layer) const
+    {
+      layer.SetPan(sourceX_, sourceY_);
+    }
+
+    virtual void RedoInternal(RadiographyLayer& layer) const
+    {
+      layer.SetPan(targetX_, targetY_);
+    }
+
+  public:
+    UndoRedoCommand(const RadiographyLayerMoveTracker& tracker) :
+      RadiographySceneCommand(tracker.accessor_),
+      sourceX_(tracker.panX_),
+      sourceY_(tracker.panY_),
+      targetX_(tracker.accessor_.GetLayer().GetGeometry().GetPanX()),
+      targetY_(tracker.accessor_.GetLayer().GetGeometry().GetPanY())
+    {
+    }
+  };
+
+
+  RadiographyLayerMoveTracker::RadiographyLayerMoveTracker(UndoRedoStack& undoRedoStack,
+                                                           RadiographyScene& scene,
+                                                           size_t layer,
+                                                           double x,
+                                                           double y,
+                                                           bool oneAxis) :
+    undoRedoStack_(undoRedoStack),
+    accessor_(scene, layer),
+    clickX_(x),
+    clickY_(y),
+    oneAxis_(oneAxis)
+  {
+    if (accessor_.IsValid())
+    {
+      panX_ = accessor_.GetLayer().GetGeometry().GetPanX();
+      panY_ = accessor_.GetLayer().GetGeometry().GetPanY();
+    }
+  }
+
+
+  void RadiographyLayerMoveTracker::Render(CairoContext& context,
+                                           double zoom)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+
+  
+  void RadiographyLayerMoveTracker::MouseUp()
+  {
+    if (accessor_.IsValid())
+    {
+      undoRedoStack_.Add(new UndoRedoCommand(*this));
+    }
+  }
+
+  
+  void RadiographyLayerMoveTracker::MouseMove(int displayX,
+                                              int displayY,
+                                              double sceneX,
+                                              double sceneY,
+                                              const std::vector<Deprecated::Touch>& displayTouches,
+                                              const std::vector<Deprecated::Touch>& sceneTouches)
+  {
+    if (accessor_.IsValid())
+    {
+      double dx = sceneX - clickX_;
+      double dy = sceneY - clickY_;
+
+      if (oneAxis_)
+      {
+        if (fabs(dx) > fabs(dy))
+        {
+          accessor_.GetLayer().SetPan(dx + panX_, panY_);
+        }
+        else
+        {
+          accessor_.GetLayer().SetPan(panX_, dy + panY_);
+        }
+      }
+      else
+      {
+        accessor_.GetLayer().SetPan(dx + panX_, dy + panY_);
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyLayerMoveTracker.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,68 @@
+/**
+ * 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 "../Toolbox/UndoRedoStack.h"
+#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
+#include "RadiographyScene.h"
+
+namespace OrthancStone
+{
+  class RadiographyLayerMoveTracker : public Deprecated::IWorldSceneMouseTracker
+  {
+  private:
+    class UndoRedoCommand;
+    
+    UndoRedoStack&                   undoRedoStack_;
+    RadiographyScene::LayerAccessor  accessor_;
+    double                           clickX_;
+    double                           clickY_;
+    double                           panX_;
+    double                           panY_;
+    bool                             oneAxis_;
+
+  public:
+    RadiographyLayerMoveTracker(UndoRedoStack& undoRedoStack,
+                                RadiographyScene& scene,
+                                size_t layer,
+                                double x,
+                                double y,
+                                bool oneAxis);
+
+    virtual bool HasRender() const
+    {
+      return false;
+    }
+
+    virtual void Render(CairoContext& context,
+                        double zoom);
+
+    virtual void MouseUp();
+
+    virtual void MouseMove(int displayX,
+                           int displayY,
+                           double sceneX,
+                           double sceneY,
+                           const std::vector<Deprecated::Touch>& displayTouches,
+                           const std::vector<Deprecated::Touch>& sceneTouches);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyLayerResizeTracker.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,188 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographyLayerResizeTracker.h"
+
+#include "RadiographySceneCommand.h"
+
+#include <Core/OrthancException.h>
+
+#include <boost/math/special_functions/round.hpp>
+
+
+namespace OrthancStone
+{
+  static double ComputeDistance(double x1,
+                                double y1,
+                                double x2,
+                                double y2)
+  {
+    double dx = x1 - x2;
+    double dy = y1 - y2;
+    return sqrt(dx * dx + dy * dy);
+  }
+      
+
+  class RadiographyLayerResizeTracker::UndoRedoCommand : public RadiographySceneCommand
+  {
+  private:
+    double   sourceSpacingX_;
+    double   sourceSpacingY_;
+    double   sourcePanX_;
+    double   sourcePanY_;
+    double   targetSpacingX_;
+    double   targetSpacingY_;
+    double   targetPanX_;
+    double   targetPanY_;
+
+  protected:
+    virtual void UndoInternal(RadiographyLayer& layer) const
+    {
+      layer.SetPixelSpacing(sourceSpacingX_, sourceSpacingY_);
+      layer.SetPan(sourcePanX_, sourcePanY_);
+    }
+
+    virtual void RedoInternal(RadiographyLayer& layer) const
+    {
+      layer.SetPixelSpacing(targetSpacingX_, targetSpacingY_);
+      layer.SetPan(targetPanX_, targetPanY_);
+    }
+
+  public:
+    UndoRedoCommand(const RadiographyLayerResizeTracker& tracker) :
+      RadiographySceneCommand(tracker.accessor_),
+      sourceSpacingX_(tracker.originalSpacingX_),
+      sourceSpacingY_(tracker.originalSpacingY_),
+      sourcePanX_(tracker.originalPanX_),
+      sourcePanY_(tracker.originalPanY_),
+      targetSpacingX_(tracker.accessor_.GetLayer().GetGeometry().GetPixelSpacingX()),
+      targetSpacingY_(tracker.accessor_.GetLayer().GetGeometry().GetPixelSpacingY()),
+      targetPanX_(tracker.accessor_.GetLayer().GetGeometry().GetPanX()),
+      targetPanY_(tracker.accessor_.GetLayer().GetGeometry().GetPanY())
+    {
+    }
+  };
+
+
+  RadiographyLayerResizeTracker::RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack,
+                                                               RadiographyScene& scene,
+                                                               size_t layer,
+                                                               const ControlPoint& startControlPoint,
+                                                               bool roundScaling) :
+    undoRedoStack_(undoRedoStack),
+    accessor_(scene, layer),
+    roundScaling_(roundScaling)
+  {
+    if (accessor_.IsValid() &&
+        accessor_.GetLayer().GetGeometry().IsResizeable())
+    {
+      originalSpacingX_ = accessor_.GetLayer().GetGeometry().GetPixelSpacingX();
+      originalSpacingY_ = accessor_.GetLayer().GetGeometry().GetPixelSpacingY();
+      originalPanX_ = accessor_.GetLayer().GetGeometry().GetPanX();
+      originalPanY_ = accessor_.GetLayer().GetGeometry().GetPanY();
+
+      size_t oppositeControlPointType;
+      switch (startControlPoint.index)
+      {
+        case RadiographyControlPointType_TopLeftCorner:
+          oppositeControlPointType = RadiographyControlPointType_BottomRightCorner;
+          break;
+
+        case RadiographyControlPointType_TopRightCorner:
+          oppositeControlPointType = RadiographyControlPointType_BottomLeftCorner;
+          break;
+
+        case RadiographyControlPointType_BottomLeftCorner:
+          oppositeControlPointType = RadiographyControlPointType_TopRightCorner;
+          break;
+
+        case RadiographyControlPointType_BottomRightCorner:
+          oppositeControlPointType = RadiographyControlPointType_TopLeftCorner;
+          break;
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+
+      accessor_.GetLayer().GetControlPoint(startOppositeControlPoint_, oppositeControlPointType);
+
+      double d = ComputeDistance(startControlPoint.x, startControlPoint.y, startOppositeControlPoint_.x, startOppositeControlPoint_.y);
+      if (d >= std::numeric_limits<float>::epsilon())
+      {
+        baseScaling_ = 1.0 / d;
+      }
+      else
+      {
+        // Avoid division by zero in extreme cases
+        accessor_.Invalidate();
+      }
+    }
+  }
+
+
+  void RadiographyLayerResizeTracker::Render(CairoContext& context,
+                                             double zoom)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+
+
+  void RadiographyLayerResizeTracker::MouseUp()
+  {
+    if (accessor_.IsValid() &&
+        accessor_.GetLayer().GetGeometry().IsResizeable())
+    {
+      undoRedoStack_.Add(new UndoRedoCommand(*this));
+    }
+  }
+
+  
+  void RadiographyLayerResizeTracker::MouseMove(int displayX,
+                                                int displayY,
+                                                double sceneX,
+                                                double sceneY,
+                                                const std::vector<Deprecated::Touch>& displayTouches,
+                                                const std::vector<Deprecated::Touch>& sceneTouches)
+  {
+    static const double ROUND_SCALING = 0.1;
+        
+    if (accessor_.IsValid() &&
+        accessor_.GetLayer().GetGeometry().IsResizeable())
+    {
+      double scaling = ComputeDistance(startOppositeControlPoint_.x, startOppositeControlPoint_.y, sceneX, sceneY) * baseScaling_;
+
+      if (roundScaling_)
+      {
+        scaling = boost::math::round<double>((scaling / ROUND_SCALING) * ROUND_SCALING);
+      }
+          
+      RadiographyLayer& layer = accessor_.GetLayer();
+      layer.SetPixelSpacing(scaling * originalSpacingX_,
+                            scaling * originalSpacingY_);
+
+      // Keep the opposite corner at a fixed location
+      ControlPoint currentOppositeCorner;
+      layer.GetControlPoint(currentOppositeCorner, startOppositeControlPoint_.index);
+      layer.SetPan(layer.GetGeometry().GetPanX() + startOppositeControlPoint_.x - currentOppositeCorner.x,
+                   layer.GetGeometry().GetPanY() + startOppositeControlPoint_.y - currentOppositeCorner.y);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyLayerResizeTracker.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,69 @@
+/**
+ * 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 "../Toolbox/UndoRedoStack.h"
+#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
+#include "RadiographyScene.h"
+
+namespace OrthancStone
+{
+  class RadiographyLayerResizeTracker : public Deprecated::IWorldSceneMouseTracker
+  {
+  private:
+    class UndoRedoCommand;
+
+    UndoRedoStack&                   undoRedoStack_;
+    RadiographyScene::LayerAccessor  accessor_;
+    bool                             roundScaling_;
+    double                           originalSpacingX_;
+    double                           originalSpacingY_;
+    double                           originalPanX_;
+    double                           originalPanY_;
+    ControlPoint                     startOppositeControlPoint_;
+    double                           baseScaling_;
+
+  public:
+    RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack,
+                                  RadiographyScene& scene,
+                                  size_t layer,
+                                  const ControlPoint& startControlPoint,
+                                  bool roundScaling);
+
+    virtual bool HasRender() const
+    {
+      return false;
+    }
+
+    virtual void Render(CairoContext& context,
+                        double zoom);
+
+    virtual void MouseUp();
+
+    virtual void MouseMove(int displayX,
+                           int displayY,
+                           double sceneX,
+                           double sceneY,
+                           const std::vector<Deprecated::Touch>& displayTouches,
+                           const std::vector<Deprecated::Touch>& sceneTouches);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyLayerRotateTracker.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,156 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographyLayerRotateTracker.h"
+
+#include "RadiographySceneCommand.h"
+
+#include <Core/OrthancException.h>
+
+#include <boost/math/constants/constants.hpp>
+#include <boost/math/special_functions/round.hpp>
+
+namespace OrthancStone
+{
+  class RadiographyLayerRotateTracker::UndoRedoCommand : public RadiographySceneCommand
+  {
+  private:
+    double  sourceAngle_;
+    double  targetAngle_;
+
+    static int ToDegrees(double angle)
+    {
+      return boost::math::iround(angle * 180.0 / boost::math::constants::pi<double>());
+    }
+      
+  protected:
+    virtual void UndoInternal(RadiographyLayer& layer) const
+    {
+      LOG(INFO) << "Undo - Set angle to " << ToDegrees(sourceAngle_) << " degrees";
+      layer.SetAngle(sourceAngle_);
+    }
+
+    virtual void RedoInternal(RadiographyLayer& layer) const
+    {
+      LOG(INFO) << "Redo - Set angle to " << ToDegrees(sourceAngle_) << " degrees";
+      layer.SetAngle(targetAngle_);
+    }
+
+  public:
+    UndoRedoCommand(const RadiographyLayerRotateTracker& tracker) :
+      RadiographySceneCommand(tracker.accessor_),
+      sourceAngle_(tracker.originalAngle_),
+      targetAngle_(tracker.accessor_.GetLayer().GetGeometry().GetAngle())
+    {
+    }
+  };
+
+
+  bool RadiographyLayerRotateTracker::ComputeAngle(double& angle /* out */,
+                                                   double sceneX,
+                                                   double sceneY) const
+  {
+    Vector u;
+    LinearAlgebra::AssignVector(u, sceneX - centerX_, sceneY - centerY_);
+
+    double nu = boost::numeric::ublas::norm_2(u);
+
+    if (!LinearAlgebra::IsCloseToZero(nu))
+    {
+      u /= nu;
+      angle = atan2(u[1], u[0]);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  RadiographyLayerRotateTracker::RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack,
+                                                               RadiographyScene& scene,
+                                                               const Deprecated::ViewportGeometry& view,
+                                                               size_t layer,
+                                                               double x,
+                                                               double y,
+                                                               bool roundAngles) :
+    undoRedoStack_(undoRedoStack),
+    accessor_(scene, layer),
+    roundAngles_(roundAngles)
+  {
+    if (accessor_.IsValid())
+    {
+      accessor_.GetLayer().GetCenter(centerX_, centerY_);
+      originalAngle_ = accessor_.GetLayer().GetGeometry().GetAngle();
+
+      double sceneX, sceneY;
+      view.MapDisplayToScene(sceneX, sceneY, x, y);
+
+      if (!ComputeAngle(clickAngle_, x, y))
+      {
+        accessor_.Invalidate();
+      }
+    }
+  }
+
+
+  void RadiographyLayerRotateTracker::Render(CairoContext& context,
+                                             double zoom)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+
+
+  void RadiographyLayerRotateTracker::MouseUp()
+  {
+    if (accessor_.IsValid())
+    {
+      undoRedoStack_.Add(new UndoRedoCommand(*this));
+    }
+  }
+
+  
+  void RadiographyLayerRotateTracker::MouseMove(int displayX,
+                                                int displayY,
+                                                double sceneX,
+                                                double sceneY,
+                                                const std::vector<Deprecated::Touch>& displayTouches,
+                                                const std::vector<Deprecated::Touch>& sceneTouches)
+  {
+    static const double ROUND_ANGLE = 15.0 / 180.0 * boost::math::constants::pi<double>(); 
+        
+    double angle;
+        
+    if (accessor_.IsValid() &&
+        ComputeAngle(angle, sceneX, sceneY))
+    {
+      angle = angle - clickAngle_ + originalAngle_;
+
+      if (roundAngles_)
+      {
+        angle = boost::math::round<double>((angle / ROUND_ANGLE) * ROUND_ANGLE);
+      }
+          
+      accessor_.GetLayer().SetAngle(angle);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyLayerRotateTracker.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,75 @@
+/**
+ * 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 "../Toolbox/UndoRedoStack.h"
+#include "../Deprecated/Toolbox/ViewportGeometry.h"
+#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
+#include "RadiographyScene.h"
+
+
+namespace OrthancStone
+{
+  class RadiographyLayerRotateTracker : public Deprecated::IWorldSceneMouseTracker
+  {
+  private:
+    class UndoRedoCommand;
+
+    UndoRedoStack&                   undoRedoStack_;
+    RadiographyScene::LayerAccessor  accessor_;
+    double                           centerX_;
+    double                           centerY_;
+    double                           originalAngle_;
+    double                           clickAngle_;
+    bool                             roundAngles_;
+
+    bool ComputeAngle(double& angle /* out */,
+                      double sceneX,
+                      double sceneY) const;
+
+  public:
+    RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack,
+                                  RadiographyScene& scene,
+                                  const Deprecated::ViewportGeometry& view,
+                                  size_t layer,
+                                  double x,
+                                  double y,
+                                  bool roundAngles);
+
+    virtual bool HasRender() const
+    {
+      return false;
+    }
+
+    virtual void Render(CairoContext& context,
+                        double zoom);
+
+    virtual void MouseUp();
+
+    virtual void MouseMove(int displayX,
+                           int displayY,
+                           double sceneX,
+                           double sceneY,
+                           const std::vector<Deprecated::Touch>& displayTouches,
+                           const std::vector<Deprecated::Touch>& sceneTouches);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyMaskLayer.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,199 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographyMaskLayer.h"
+#include "RadiographyDicomLayer.h"
+
+#include "RadiographyScene.h"
+#include "Core/Images/Image.h"
+#include "Core/Images/ImageProcessing.h"
+#include <Core/OrthancException.h>
+#include "../Toolbox/ImageGeometry.h"
+
+namespace OrthancStone
+{
+  const unsigned char IN_MASK_VALUE = 0x77;
+  const unsigned char OUT_MASK_VALUE = 0xFF;
+
+  const AffineTransform2D& RadiographyMaskLayer::GetTransform() const
+  {
+    return dicomLayer_.GetTransform();
+  }
+
+  const AffineTransform2D& RadiographyMaskLayer::GetTransformInverse() const
+  {
+    return dicomLayer_.GetTransformInverse();
+  }
+
+  bool RadiographyMaskLayer::GetPixel(unsigned int& imageX,
+                        unsigned int& imageY,
+                        double sceneX,
+                        double sceneY) const
+  {
+    return dicomLayer_.GetPixel(imageX, imageY, sceneX, sceneY);
+  }
+
+  std::string RadiographyMaskLayer::GetInstanceId() const
+  {
+    return dicomLayer_.GetInstanceId();
+  }
+
+  void RadiographyMaskLayer::SetCorner(const Orthanc::ImageProcessing::ImagePoint& corner, size_t index)
+  {
+    if (index < corners_.size())
+      corners_[index] = corner;
+    else
+      corners_.push_back(corner);
+    invalidated_ = true;
+
+    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
+  }
+
+  void RadiographyMaskLayer::SetCorners(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners)
+  {
+    corners_ = corners;
+    invalidated_ = true;
+
+    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
+  }
+
+  Extent2D RadiographyMaskLayer::GetSceneExtent(bool minimal) const
+  {
+    if (!minimal)
+    {
+      return RadiographyLayer::GetSceneExtent(minimal);
+    }
+    else
+    { // get the extent of the in-mask area
+      Extent2D sceneExtent;
+
+      for (std::vector<Orthanc::ImageProcessing::ImagePoint>::const_iterator corner = corners_.begin(); corner != corners_.end(); ++corner)
+      {
+        double x = static_cast<double>(corner->GetX());
+        double y = static_cast<double>(corner->GetY());
+
+        dicomLayer_.GetTransform().Apply(x, y);
+        sceneExtent.AddPoint(x, y);
+      }
+      return sceneExtent;
+    }
+  }
+
+
+
+  void RadiographyMaskLayer::Render(Orthanc::ImageAccessor& buffer,
+                                    const AffineTransform2D& viewTransform,
+                                    ImageInterpolation interpolation,
+                                    float windowCenter,
+                                    float windowWidth,
+                                    bool applyWindowing) const
+  {
+    if (dicomLayer_.GetWidth() == 0 || dicomLayer_.GetSourceImage() == NULL) // nothing to do if the DICOM layer is not displayed (or not loaded)
+      return;
+
+    if (invalidated_)
+    {
+      mask_.reset(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, dicomLayer_.GetWidth(), dicomLayer_.GetHeight(), false));
+
+      DrawMask();
+
+      invalidated_ = false;
+    }
+
+    {// rendering
+      if (buffer.GetFormat() != Orthanc::PixelFormat_Float32)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+      }
+
+      unsigned int cropX, cropY, cropWidth, cropHeight;
+      dicomLayer_.GetCrop(cropX, cropY, cropWidth, cropHeight);
+
+      const AffineTransform2D t = AffineTransform2D::Combine(
+            viewTransform, dicomLayer_.GetTransform(),
+            AffineTransform2D::CreateOffset(cropX, cropY));
+
+      Orthanc::ImageAccessor cropped;
+      mask_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
+
+      Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false);
+
+
+      unsigned int x1, y1, x2, y2;
+      if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2,
+                                                      t.GetHomogeneousMatrix(),
+                                                      cropped.GetWidth(),
+                                                      cropped.GetHeight(),
+                                                      buffer.GetWidth(),
+                                                      buffer.GetHeight()))
+      {
+        return;  // layer is outside the buffer
+      }
+
+      t.Apply(tmp, cropped, ImageInterpolation_Nearest, true /* clear */);
+
+      // we have observed vertical lines at the image border (probably due to bilinear filtering of the DICOM image when it is not aligned with the buffer pixels)
+      // -> draw the mask one line further on each side
+      if (x1 >= 1)
+      {
+        x1 = x1 - 1;
+      }
+      if (x2 < buffer.GetWidth() - 2)
+      {
+        x2 = x2 + 1;
+      }
+
+      // Blit
+      for (unsigned int y = y1; y <= y2; y++)
+      {
+        float *q = reinterpret_cast<float*>(buffer.GetRow(y)) + x1;
+        const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y)) + x1;
+
+        for (unsigned int x = x1; x <= x2; x++, p++, q++)
+        {
+          if (*p != IN_MASK_VALUE)
+            *q = foreground_;
+          // else keep the underlying pixel value
+        }
+      }
+
+    }
+  }
+
+  void RadiographyMaskLayer::DrawMask() const
+  {
+    // first fill the complete image
+    Orthanc::ImageProcessing::Set(*mask_, OUT_MASK_VALUE);
+
+    // clip corners
+    std::vector<Orthanc::ImageProcessing::ImagePoint> clippedCorners;
+    for (size_t i = 0; i < corners_.size(); i++)
+    {
+      clippedCorners.push_back(corners_[i]);
+      clippedCorners[i].ClipTo(0, mask_->GetWidth() - 1, 0, mask_->GetHeight() - 1);
+    }
+
+    // fill mask
+    Orthanc::ImageProcessing::FillPolygon(*mask_, clippedCorners, IN_MASK_VALUE);
+
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyMaskLayer.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,146 @@
+/**
+ * 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 "RadiographyLayer.h"
+
+#include <Core/Compatibility.h>
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+
+namespace OrthancStone
+{
+  class RadiographyScene;
+  class RadiographyDicomLayer;
+
+  class RadiographyMaskLayer : public RadiographyLayer
+  {
+  private:
+    std::vector<Orthanc::ImageProcessing::ImagePoint>            corners_;
+    const RadiographyDicomLayer&      dicomLayer_;
+    mutable bool                      invalidated_;
+    float                             foreground_;
+
+    mutable std::unique_ptr<Orthanc::ImageAccessor>  mask_;
+  public:
+    RadiographyMaskLayer(const RadiographyScene& scene, const RadiographyDicomLayer& dicomLayer,
+                         float foreground) :
+      RadiographyLayer(scene),
+      dicomLayer_(dicomLayer),
+      invalidated_(true),
+      foreground_(foreground)
+    {
+    }
+
+    virtual size_t GetApproximateMemoryUsage() const
+    {
+      size_t size = 0;
+      if (mask_.get() != NULL)
+      {
+        size += mask_->GetPitch() * mask_->GetHeight();
+      }
+
+      return size;
+    }
+
+
+    void SetCorners(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners);
+    void SetCorner(const Orthanc::ImageProcessing::ImagePoint& corner, size_t index);
+
+    const std::vector<Orthanc::ImageProcessing::ImagePoint>& GetCorners() const
+    {
+      return corners_;
+    }
+
+    float GetForeground() const
+    {
+      return foreground_;
+    }
+
+    virtual void Render(Orthanc::ImageAccessor& buffer,
+                        const AffineTransform2D& viewTransform,
+                        ImageInterpolation interpolation,
+                        float windowCenter,
+                        float windowWidth,
+                        bool applyWindowing) const;
+
+    std::string GetInstanceId() const;
+
+    virtual size_t GetControlPointCount() const
+    {
+      return corners_.size();
+    }
+
+    virtual void GetControlPoint(ControlPoint& cpScene,
+                                         size_t index) const
+    {
+      ControlPoint cp(corners_[index].GetX(), corners_[index].GetY(), index);
+
+      // transforms image coordinates into scene coordinates
+      GetTransform().Apply(cp.x, cp.y);
+      cpScene = cp;
+    }
+
+    virtual Extent2D GetSceneExtent(bool minimal) const;
+
+    virtual bool GetDefaultWindowing(float& center,
+                                     float& width) const
+    {
+      return false;
+    }
+
+    virtual bool GetRange(float& minValue,
+                          float& maxValue) const
+    {
+      minValue = 0;
+      maxValue = 0;
+
+      if (foreground_ < 0)
+      {
+        minValue = foreground_;
+      }
+
+      if (foreground_ > 0)
+      {
+        maxValue = foreground_;
+      }
+
+      return true;
+
+    }
+
+    virtual bool GetPixel(unsigned int& imageX,
+                          unsigned int& imageY,
+                          double sceneX,
+                          double sceneY) const;
+
+  protected:
+    virtual const AffineTransform2D& GetTransform() const;
+
+    virtual const AffineTransform2D& GetTransformInverse() const;
+
+
+  private:
+    void DrawMask() const;
+
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyScene.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,968 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographyScene.h"
+
+#include "RadiographyAlphaLayer.h"
+#include "RadiographyDicomLayer.h"
+#include "RadiographyTextLayer.h"
+#include "RadiographyMaskLayer.h"
+#include "../Deprecated/Toolbox/DicomFrameConverter.h"
+#include "../Scene2D/CairoCompositor.h"
+#include "../Scene2D/FloatTextureSceneLayer.h"
+#include "../Scene2D/TextSceneLayer.h"
+
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/PamReader.h>
+#include <Core/Images/PamWriter.h>
+#include <Core/Images/PngWriter.h>
+#include <Core/OrthancException.h>
+#include <Core/Toolbox.h>
+#include <Plugins/Samples/Common/DicomDatasetReader.h>
+#include <Plugins/Samples/Common/FullOrthancDataset.h>
+
+#include <boost/math/special_functions/round.hpp>
+
+
+namespace OrthancStone
+{
+  RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene,
+                                                 size_t index) :
+    scene_(scene),
+    index_(index)
+  {
+    Layers::iterator layer = scene.layers_.find(index);
+    if (layer == scene.layers_.end())
+    {
+      layer_ = NULL;
+    }
+    else
+    {
+      assert(layer->second != NULL);
+      layer_ = layer->second;
+    }
+  }
+
+
+  RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene,
+                                                 double x,
+                                                 double y) :
+    scene_(scene),
+    index_(0)  // Dummy initialization
+  {
+    if (scene.LookupLayer(index_, x, y))
+    {
+      Layers::iterator layer = scene.layers_.find(index_);
+
+      if (layer == scene.layers_.end())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+      else
+      {
+        assert(layer->second != NULL);
+        layer_ = layer->second;
+      }
+    }
+    else
+    {
+      layer_ = NULL;
+    }
+  }
+
+
+  RadiographyScene& RadiographyScene::LayerAccessor::GetScene() const
+  {
+    if (IsValid())
+    {
+      return scene_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+
+  size_t RadiographyScene::LayerAccessor::GetIndex() const
+  {
+    if (IsValid())
+    {
+      return index_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+  
+  RadiographyLayer& RadiographyScene::LayerAccessor::GetLayer() const
+  {
+    if (IsValid())
+    {
+      return *layer_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+  void RadiographyScene::_RegisterLayer(RadiographyLayer* layer)
+  {
+    std::unique_ptr<RadiographyLayer> raii(layer);
+
+    // LOG(INFO) << "Registering layer: " << countLayers_;
+
+    size_t index = nextLayerIndex_++;
+    raii->SetIndex(index);
+    layers_[index] = raii.release();
+  }
+
+  RadiographyLayer& RadiographyScene::RegisterLayer(RadiographyLayer* layer)
+  {
+    if (layer == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+
+    _RegisterLayer(layer);
+
+    BroadcastMessage(GeometryChangedMessage(*this, *layer));
+    BroadcastMessage(ContentChangedMessage(*this, *layer));
+    Register<RadiographyLayer::LayerEditedMessage>(*layer, &RadiographyScene::OnLayerEdited);
+
+    return *layer;
+  }
+
+  size_t RadiographyScene::GetApproximateMemoryUsage() const
+  {
+    size_t size = 0;
+    for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++)
+    {
+      size += it->second->GetApproximateMemoryUsage();
+    }
+    return size;
+  }
+
+  void RadiographyScene::OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message)
+  {
+    BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, message.GetOrigin()));
+  }
+
+  
+  RadiographyScene::RadiographyScene() :
+    nextLayerIndex_(0),
+    hasWindowing_(false),
+    windowingCenter_(0),  // Dummy initialization
+    windowingWidth_(0)    // Dummy initialization
+  {
+  }
+
+
+  RadiographyScene::~RadiographyScene()
+  {
+    for (Layers::iterator it = layers_.begin(); it != layers_.end(); it++)
+    {
+      assert(it->second != NULL);
+      delete it->second;
+    }
+  }
+
+  RadiographyPhotometricDisplayMode RadiographyScene::GetPreferredPhotomotricDisplayMode() const
+  {
+    // return the mode of the first layer who "cares" about its display mode (normaly, the one and only layer that is a DicomLayer)
+    for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++)
+    {
+      if (it->second->GetPreferredPhotomotricDisplayMode() != RadiographyPhotometricDisplayMode_Default)
+      {
+        return it->second->GetPreferredPhotomotricDisplayMode();
+      }
+    }
+
+    return RadiographyPhotometricDisplayMode_Default;
+  }
+
+
+  void RadiographyScene::GetLayersIndexes(std::vector<size_t>& output) const
+  {
+    for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++)
+    {
+      output.push_back(it->first);
+    }
+  }
+
+  void RadiographyScene::RemoveLayer(size_t layerIndex)
+  {
+    LOG(INFO) << "Removing layer: " << layerIndex;
+
+    Layers::iterator found = layers_.find(layerIndex);
+
+    if (found == layers_.end())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      assert(found->second != NULL);
+      delete found->second;
+      
+      layers_.erase(found);
+      
+      LOG(INFO) << "Removing layer, there are now : " << layers_.size() << " layers";
+
+      _OnLayerRemoved();
+
+      BroadcastMessage(RadiographyScene::LayerRemovedMessage(*this, layerIndex));
+    }
+  }
+
+  const RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) const
+  {
+    Layers::const_iterator found = layers_.find(layerIndex);
+    
+    if (found == layers_.end())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      assert(found->second != NULL);
+      return *found->second;
+    }
+  }
+
+  RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex)
+  {
+    Layers::const_iterator found = layers_.find(layerIndex);
+
+    if (found == layers_.end())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      assert(found->second != NULL);
+      return *found->second;
+    }
+  }
+
+  bool RadiographyScene::GetWindowing(float& center,
+                                      float& width) const
+  {
+    if (hasWindowing_)
+    {
+      center = windowingCenter_;
+      width = windowingWidth_;
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+
+  void RadiographyScene::GetWindowingWithDefault(float& center,
+                                                 float& width) const
+  {
+    if (!GetWindowing(center, width))
+    {
+      center = 128;
+      width = 256;
+    }
+  }
+
+
+  void RadiographyScene::SetWindowing(float center,
+                                      float width)
+  {
+    hasWindowing_ = true;
+    windowingCenter_ = center;
+    windowingWidth_ = width;
+
+    BroadcastMessage(RadiographyScene::WindowingChangedMessage(*this));
+  }
+
+
+  RadiographyLayer& RadiographyScene::UpdateText(size_t layerIndex,
+                                                 const std::string& utf8,
+                                                 const std::string& font,
+                                                 unsigned int fontSize,
+                                                 uint8_t foreground)
+  {
+    RadiographyTextLayer& textLayer = dynamic_cast<RadiographyTextLayer&>(GetLayer(layerIndex));
+    textLayer.SetText(utf8, font, fontSize, foreground);
+
+    BroadcastMessage(RadiographyScene::ContentChangedMessage(*this, textLayer));
+    BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, textLayer));
+    return textLayer;
+  }
+
+
+  RadiographyLayer& RadiographyScene::LoadText(const std::string& utf8,
+                                               const std::string& font,
+                                               unsigned int fontSize,
+                                               uint8_t foreground,
+                                               RadiographyLayer::Geometry* centerGeometry,
+                                               bool isCenterGeometry)
+  {
+    std::unique_ptr<RadiographyTextLayer>  alpha(new RadiographyTextLayer(*this));
+    alpha->SetText(utf8, font, fontSize, foreground);
+    if (centerGeometry != NULL)
+    {
+      if (isCenterGeometry)
+      {
+        // modify geometry to reference the top left corner
+        double tlx = centerGeometry->GetPanX();
+        double tly = centerGeometry->GetPanY();
+        Extent2D textExtent = alpha->GetSceneExtent(false);
+        tlx = tlx - (textExtent.GetWidth() / 2) * centerGeometry->GetPixelSpacingX();
+        tly = tly - (textExtent.GetHeight() / 2) * centerGeometry->GetPixelSpacingY();
+        centerGeometry->SetPan(tlx, tly);
+      }
+      alpha->SetGeometry(*centerGeometry);
+    }
+
+    RadiographyLayer& registeredLayer = RegisterLayer(alpha.release());
+
+    BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, registeredLayer));
+    return registeredLayer;
+  }
+
+
+  RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width,
+                                                    unsigned int height,
+                                                    RadiographyLayer::Geometry* geometry)
+  {
+    std::unique_ptr<Orthanc::Image>  block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false));
+
+    for (unsigned int padding = 0;
+         (width > 2 * padding) && (height > 2 * padding);
+         padding++)
+    {
+      uint8_t color;
+      if (255 > 10 * padding)
+      {
+        color = 255 - 10 * padding;
+      }
+      else
+      {
+        color = 0;
+      }
+
+      Orthanc::ImageAccessor region;
+      block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding);
+      Orthanc::ImageProcessing::Set(region, color);
+    }
+
+    return LoadAlphaBitmap(block.release(), geometry);
+  }
+
+  RadiographyLayer& RadiographyScene::LoadMask(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners,
+                                               const RadiographyDicomLayer& dicomLayer,
+                                               float foreground,
+                                               RadiographyLayer::Geometry* geometry)
+  {
+    std::unique_ptr<RadiographyMaskLayer>  mask(new RadiographyMaskLayer(*this, dicomLayer, foreground));
+    mask->SetCorners(corners);
+    if (geometry != NULL)
+    {
+      mask->SetGeometry(*geometry);
+    }
+
+    return RegisterLayer(mask.release());
+  }
+
+
+  RadiographyLayer& RadiographyScene::LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, RadiographyLayer::Geometry *geometry)
+  {
+    std::unique_ptr<RadiographyAlphaLayer>  alpha(new RadiographyAlphaLayer(*this));
+    alpha->SetAlpha(bitmap);
+    if (geometry != NULL)
+    {
+      alpha->SetGeometry(*geometry);
+    }
+
+    return RegisterLayer(alpha.release());
+  }
+
+  RadiographyLayer& RadiographyScene::LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership
+                                                     const std::string& instance,
+                                                     unsigned int frame,
+                                                     Deprecated::DicomFrameConverter* converter,  // takes ownership
+                                                     RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode,
+                                                     RadiographyLayer::Geometry* geometry)
+  {
+    RadiographyDicomLayer& layer = dynamic_cast<RadiographyDicomLayer&>(RegisterLayer(new RadiographyDicomLayer(*this)));
+
+    layer.SetInstance(instance, frame);
+
+    if (geometry != NULL)
+    {
+      layer.SetGeometry(*geometry);
+    }
+
+    layer.SetDicomFrameConverter(converter);
+    layer.SetSourceImage(dicomImage);
+    layer.SetPreferredPhotomotricDisplayMode(preferredPhotometricDisplayMode);
+
+    return layer;
+  }
+
+  RadiographyLayer& RadiographyScene::LoadDicomFrame(Deprecated::OrthancApiClient& orthanc,
+                                                     const std::string& instance,
+                                                     unsigned int frame,
+                                                     bool httpCompression,
+                                                     RadiographyLayer::Geometry* geometry)
+  {
+    RadiographyDicomLayer& layer = dynamic_cast<RadiographyDicomLayer&>(RegisterLayer(new RadiographyDicomLayer( *this)));
+    layer.SetInstance(instance, frame);
+
+    if (geometry != NULL)
+    {
+      layer.SetGeometry(*geometry);
+    }
+
+    {
+      Deprecated::IWebService::HttpHeaders headers;
+      std::string uri = "/instances/" + instance + "/tags";
+
+      orthanc.GetBinaryAsync(
+            uri, headers,
+            new Deprecated::DeprecatedCallable<RadiographyScene, Deprecated::OrthancApiClient::BinaryResponseReadyMessage>
+            (GetSharedObserver(), &RadiographyScene::OnTagsReceived), NULL,
+            new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
+    }
+
+    {
+      Deprecated::IWebService::HttpHeaders headers;
+      headers["Accept"] = "image/x-portable-arbitrarymap";
+
+      if (httpCompression)
+      {
+        headers["Accept-Encoding"] = "gzip";
+      }
+
+      std::string uri = ("/instances/" + instance + "/frames/" +
+                         boost::lexical_cast<std::string>(frame) + "/image-uint16");
+
+      orthanc.GetBinaryAsync(
+            uri, headers,
+            new Deprecated::DeprecatedCallable<RadiographyScene, Deprecated::OrthancApiClient::BinaryResponseReadyMessage>
+            (GetSharedObserver(), &RadiographyScene::OnFrameReceived), NULL,
+            new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
+    }
+
+    return layer;
+  }
+
+
+  RadiographyLayer& RadiographyScene::LoadDicomWebFrame(Deprecated::IWebService& web)
+  {
+    RadiographyLayer& layer = RegisterLayer(new RadiographyDicomLayer(*this));
+
+
+    return layer;
+  }
+
+
+
+  void RadiographyScene::OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message)
+  {
+    size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>
+        (message.GetPayload()).GetValue();
+
+    VLOG(1) << "JSON received: " << message.GetUri().c_str()
+            << " (" << message.GetAnswerSize() << " bytes) for layer " << index;
+
+    Layers::iterator layer = layers_.find(index);
+    if (layer != layers_.end())
+    {
+      assert(layer->second != NULL);
+
+      OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize());
+      dynamic_cast<RadiographyDicomLayer*>(layer->second)->SetDicomTags(dicom);
+
+      float c, w;
+      if (!hasWindowing_ &&
+          layer->second->GetDefaultWindowing(c, w))
+      {
+        hasWindowing_ = true;
+        windowingCenter_ = c;
+        windowingWidth_ = w;
+      }
+
+      BroadcastMessage(GeometryChangedMessage(*this, *(layer->second)));
+    }
+  }
+
+
+  void RadiographyScene::OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message)
+  {
+    size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue();
+
+    VLOG(1) << "DICOM frame received: " << message.GetUri().c_str()
+            << " (" << message.GetAnswerSize() << " bytes) for layer " << index;
+
+    Layers::iterator layer = layers_.find(index);
+    if (layer != layers_.end())
+    {
+      assert(layer->second != NULL);
+
+      std::string content;
+      if (message.GetAnswerSize() > 0)
+      {
+        content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize());
+      }
+
+      std::unique_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader);
+      reader->ReadFromMemory(content);
+      dynamic_cast<RadiographyDicomLayer*>(layer->second)->SetSourceImage(reader.release());
+
+      BroadcastMessage(ContentChangedMessage(*this, *(layer->second)));
+    }
+  }
+
+
+  Extent2D RadiographyScene::GetSceneExtent(bool minimal) const
+  {
+    Extent2D extent;
+
+    for (Layers::const_iterator it = layers_.begin();
+         it != layers_.end(); ++it)
+    {
+      assert(it->second != NULL);
+      extent.Union(it->second->GetSceneExtent(minimal));
+    }
+
+    return extent;
+  }
+
+
+  void RadiographyScene::Render(Orthanc::ImageAccessor& buffer,
+                                const AffineTransform2D& viewTransform,
+                                ImageInterpolation interpolation,
+                                bool applyWindowing) const
+  {
+    // Render layers in the background-to-foreground order
+    for (size_t index = 0; index < nextLayerIndex_; index++)
+    {
+      try
+      {
+        Layers::const_iterator it = layers_.find(index);
+        if (it != layers_.end())
+        {
+          assert(it->second != NULL);
+          it->second->Render(buffer, viewTransform, interpolation, windowingCenter_, windowingWidth_, applyWindowing);
+        }
+      }
+      catch (Orthanc::OrthancException& ex)
+      {
+        LOG(ERROR) << "RadiographyScene::Render: " << index << ", OrthancException: " << ex.GetDetails();
+        throw ex; // rethrow because we want it to crash to see there's a problem !
+      }
+      catch (...)
+      {
+        LOG(ERROR) << "RadiographyScene::Render: " << index << ", unkown exception: ";
+        throw; // rethrow because we want it to crash to see there's a problem !
+      }
+    }
+  }
+
+
+  bool RadiographyScene::LookupLayer(size_t& index /* out */,
+                                     double x,
+                                     double y) const
+  {
+    // Render layers in the foreground-to-background order
+    for (size_t i = nextLayerIndex_; i > 0; i--)
+    {
+      index = i - 1;
+      Layers::const_iterator it = layers_.find(index);
+      if (it != layers_.end())
+      {
+        assert(it->second != NULL);
+        if (it->second->Contains(x, y))
+        {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+
+  void RadiographyScene::DrawBorder(CairoContext& context,
+                                    unsigned int layer,
+                                    double zoom)
+  {
+    Layers::const_iterator found = layers_.find(layer);
+
+    if (found != layers_.end())
+    {
+      context.SetSourceColor(255, 0, 0);
+      found->second->DrawBorders(context, zoom);
+    }
+  }
+
+
+  void RadiographyScene::GetRange(float& minValue,
+                                  float& maxValue) const
+  {
+    bool first = true;
+
+    for (Layers::const_iterator it = layers_.begin();
+         it != layers_.end(); it++)
+    {
+      assert(it->second != NULL);
+
+      float a, b;
+      if (it->second->GetRange(a, b))
+      {
+        if (first)
+        {
+          minValue = a;
+          maxValue = b;
+          first = false;
+        }
+        else
+        {
+          minValue = std::min(a, minValue);
+          maxValue = std::max(b, maxValue);
+        }
+      }
+    }
+
+    if (first)
+    {
+      minValue = 0;
+      maxValue = 0;
+    }
+  }
+
+  void RadiographyScene::ExtractLayerFromRenderedScene(Orthanc::ImageAccessor& layer,
+                                                       const Orthanc::ImageAccessor& renderedScene,
+                                                       size_t layerIndex,
+                                                       bool isCropped,
+                                                       ImageInterpolation interpolation)
+  {
+    Extent2D sceneExtent = GetSceneExtent(isCropped);
+
+    double pixelSpacingX = sceneExtent.GetWidth() / renderedScene.GetWidth();
+    double pixelSpacingY = sceneExtent.GetHeight() / renderedScene.GetHeight();
+
+    AffineTransform2D view = AffineTransform2D::Combine(
+          AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY),
+          AffineTransform2D::CreateOffset(-sceneExtent.GetX1(), -sceneExtent.GetY1()));
+
+    AffineTransform2D layerToSceneTransform = AffineTransform2D::Combine(
+          view,
+          GetLayer(layerIndex).GetTransform());
+
+    AffineTransform2D sceneToLayerTransform = AffineTransform2D::Invert(layerToSceneTransform);
+    sceneToLayerTransform.Apply(layer, renderedScene, interpolation, false);
+  }
+
+  Orthanc::Image* RadiographyScene::ExportToImage(double pixelSpacingX,
+                                                  double pixelSpacingY,
+                                                  ImageInterpolation interpolation,
+                                                  bool invert,
+                                                  int64_t maxValue /* for inversion */,
+                                                  bool autoCrop,
+                                                  bool applyWindowing)
+  {
+    if (pixelSpacingX <= 0 ||
+        pixelSpacingY <= 0)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    Extent2D extent = GetSceneExtent(autoCrop);
+
+    int w = boost::math::iround(extent.GetWidth() / pixelSpacingX);
+    int h = boost::math::iround(extent.GetHeight() / pixelSpacingY);
+
+    if (w < 0 || h < 0)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+
+    Orthanc::Image layers(Orthanc::PixelFormat_Float32,
+                          static_cast<unsigned int>(w),
+                          static_cast<unsigned int>(h), false);
+
+    AffineTransform2D view = AffineTransform2D::Combine(
+          AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY),
+          AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1()));
+
+    // wipe background before rendering
+    if (GetPreferredPhotomotricDisplayMode() == RadiographyPhotometricDisplayMode_Monochrome1)
+    {
+      Orthanc::ImageProcessing::Set(layers, 65535);
+    }
+    else
+    {
+      Orthanc::ImageProcessing::Set(layers, 0);
+    }
+
+    Render(layers, view, interpolation, applyWindowing);
+
+    std::unique_ptr<Orthanc::Image> rendered(new Orthanc::Image(Orthanc::PixelFormat_Grayscale16,
+                                                              layers.GetWidth(), layers.GetHeight(), false));
+
+    Orthanc::ImageProcessing::Convert(*rendered, layers);
+    if (invert)
+      Orthanc::ImageProcessing::Invert(*rendered, maxValue);
+
+    return rendered.release();
+  }
+
+
+  Orthanc::Image* RadiographyScene::ExportToCreateDicomRequestAndImage(Json::Value& createDicomRequestContent,
+                                                                       const Json::Value& dicomTags,
+                                                                       const std::string& parentOrthancId,
+                                                                       double pixelSpacingX,
+                                                                       double pixelSpacingY,
+                                                                       bool invert,
+                                                                       bool autoCrop,
+                                                                       ImageInterpolation interpolation)
+  {
+    LOG(INFO) << "Exporting RadiographyScene to DICOM";
+
+    std::unique_ptr<Orthanc::Image> rendered(ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, autoCrop, false)); // note: we don't invert the image in the pixels data because we'll set the PhotometricDisplayMode correctly in the DICOM tags
+
+    createDicomRequestContent["Tags"] = dicomTags;
+
+    RadiographyPhotometricDisplayMode photometricMode = GetPreferredPhotomotricDisplayMode();
+    if ((invert && photometricMode != RadiographyPhotometricDisplayMode_Monochrome2) ||
+        (!invert && photometricMode == RadiographyPhotometricDisplayMode_Monochrome1))
+    {
+      createDicomRequestContent["Tags"]["PhotometricInterpretation"] = "MONOCHROME1";
+    }
+    else
+    {
+      createDicomRequestContent["Tags"]["PhotometricInterpretation"] = "MONOCHROME2";
+    }
+
+    // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to
+    // avoid floating-point numbers to grow over 16 characters,
+    // which would be invalid according to DICOM standard
+    // ("dciodvfy" would complain).
+    char buf[32];
+    sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX);
+
+    createDicomRequestContent["Tags"]["PixelSpacing"] = buf;
+
+    float center, width;
+    if (GetWindowing(center, width))
+    {
+      createDicomRequestContent["Tags"]["WindowCenter"] =
+          boost::lexical_cast<std::string>(boost::math::iround(center));
+
+      createDicomRequestContent["Tags"]["WindowWidth"] =
+          boost::lexical_cast<std::string>(boost::math::iround(width));
+    }
+
+    if (!parentOrthancId.empty())
+    {
+      createDicomRequestContent["Parent"] = parentOrthancId;
+    }
+
+    return rendered.release();
+  }
+
+
+  void RadiographyScene::ExportToCreateDicomRequest(Json::Value& createDicomRequestContent,
+                                                    const Json::Value& dicomTags,
+                                                    const std::string& parentOrthancId,
+                                                    double pixelSpacingX,
+                                                    double pixelSpacingY,
+                                                    bool invert,
+                                                    bool autoCrop,
+                                                    ImageInterpolation interpolation,
+                                                    bool usePam)
+  {
+    LOG(INFO) << "Exporting RadiographyScene to DICOM";
+    VLOG(1) << "Exporting RadiographyScene to: export to image";
+
+    std::unique_ptr<Orthanc::Image> rendered(ExportToCreateDicomRequestAndImage(createDicomRequestContent, dicomTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation));
+
+    // convert the image into base64 for inclusing in the createDicomRequest
+    std::string base64;
+
+    {
+      std::string content;
+
+      if (usePam)
+      {
+        VLOG(1) << "Exporting RadiographyScene: convert to PAM";
+        Orthanc::PamWriter writer;
+        writer.WriteToMemory(content, *rendered);
+      }
+      else
+      {
+        Orthanc::PngWriter writer;
+        writer.WriteToMemory(content, *rendered);
+      }
+
+      VLOG(1) << "Exporting RadiographyScene: encoding to base64";
+      Orthanc::Toolbox::EncodeBase64(base64, content);
+    }
+
+    // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme
+    createDicomRequestContent["Content"] = ("data:" +
+                                            std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) +
+                                            ";base64," + base64);
+
+    VLOG(1) << "Exporting RadiographyScene: create-dicom request is ready";
+  }
+
+
+  void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc,
+                                     const Json::Value& dicomTags,
+                                     const std::string& parentOrthancId,
+                                     double pixelSpacingX,
+                                     double pixelSpacingY,
+                                     bool invert,
+                                     bool autoCrop,
+                                     ImageInterpolation interpolation,
+                                     bool usePam)
+  {
+    Json::Value createDicomRequestContent;
+
+    ExportToCreateDicomRequest(createDicomRequestContent, dicomTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation, usePam);
+
+    orthanc.PostJsonAsyncExpectJson(
+          "/tools/create-dicom", createDicomRequestContent,
+          new Deprecated::DeprecatedCallable<RadiographyScene, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
+          (GetSharedObserver(), &RadiographyScene::OnDicomExported),
+          NULL, NULL);
+
+  }
+
+
+  // Export using PAM is faster than using PNG, but requires Orthanc
+  // core >= 1.4.3
+  void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc,
+                                     const Orthanc::DicomMap& dicom,
+                                     const std::string& parentOrthancId,
+                                     double pixelSpacingX,
+                                     double pixelSpacingY,
+                                     bool invert,
+                                     bool autoCrop,
+                                     ImageInterpolation interpolation,
+                                     bool usePam)
+  {
+    std::set<Orthanc::DicomTag> tags;
+    dicom.GetTags(tags);
+
+    Json::Value jsonTags = Json::objectValue;
+
+    for (std::set<Orthanc::DicomTag>::const_iterator
+         tag = tags.begin(); tag != tags.end(); ++tag)
+    {
+      const Orthanc::DicomValue& value = dicom.GetValue(*tag);
+      if (!value.IsNull() &&
+          !value.IsBinary())
+      {
+        jsonTags[tag->Format()] = value.GetContent();
+      }
+    }
+
+    ExportDicom(orthanc, jsonTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation, usePam);
+  }
+
+  void RadiographyScene::OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
+  {
+    LOG(INFO) << "DICOM export was successful: "
+              << message.GetJson().toStyledString();
+  }
+
+
+  void RadiographyScene::OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message)
+  {
+    LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes";
+
+    const Deprecated::IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders();
+    for (Deprecated::IWebService::HttpHeaders::const_iterator
+         it = h.begin(); it != h.end(); ++it)
+    {
+      printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str());
+    }
+  }
+
+  void RadiographyScene::ExportToScene2D(Scene2D& output) const
+  {
+    int depth = 0;
+    for (Layers::const_iterator it = layers_.begin();
+         it != layers_.end(); ++it)
+    {
+      assert(it->second != NULL);
+
+      std::unique_ptr<ISceneLayer> layer;
+      if (dynamic_cast<RadiographyDicomLayer*>(it->second))
+      {
+        RadiographyDicomLayer* oldLayer = dynamic_cast<RadiographyDicomLayer*>(it->second);
+
+        std::unique_ptr<FloatTextureSceneLayer> newLayer(new FloatTextureSceneLayer(*(oldLayer->GetSourceImage())));
+
+        newLayer->SetOrigin(oldLayer->GetGeometry().GetPanX(),
+                            oldLayer->GetGeometry().GetPanY()
+                            );
+        newLayer->SetAngle(oldLayer->GetGeometry().GetAngle());
+
+        layer.reset(newLayer.release());
+
+        // TODO: windowing dynamic_cast
+      }
+      else if (dynamic_cast<RadiographyTextLayer*>(it->second))
+      {
+        RadiographyTextLayer* oldLayer = dynamic_cast<RadiographyTextLayer*>(it->second);
+
+        std::unique_ptr<TextSceneLayer> newLayer(new TextSceneLayer());
+
+        newLayer->SetText(oldLayer->GetText());
+        newLayer->SetColor(oldLayer->GetForegroundGreyLevel(),
+                           oldLayer->GetForegroundGreyLevel(),
+                           oldLayer->GetForegroundGreyLevel()
+                           );
+        newLayer->SetPosition(oldLayer->GetGeometry().GetPanX(),
+                              oldLayer->GetGeometry().GetPanY()
+                              );
+        newLayer->SetFontIndex(1);
+        newLayer->SetAnchor(BitmapAnchor_TopLeft);
+        //newLayer->SetAngle(oldLayer->GetGeometry().GetAngle());
+
+        layer.reset(newLayer.release());
+      }
+
+      output.SetLayer(depth++, layer.release());
+
+    }
+
+  }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyScene.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,370 @@
+/**
+ * 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 "RadiographyLayer.h"
+#include "../Messages/ObserverBase.h"
+#include "../Deprecated/Toolbox/DicomFrameConverter.h"
+#include "../Deprecated/Toolbox/OrthancApiClient.h"
+#include "../StoneEnumerations.h"
+#include "Core/Images/Image.h"
+#include "Core/Images/ImageProcessing.h"
+
+#include "../Scene2D/Scene2D.h"
+
+namespace OrthancStone
+{
+  class RadiographyDicomLayer;
+
+  class RadiographyScene :
+    public ObserverBase<RadiographyScene>,
+    public IObservable
+  {
+    friend class RadiographySceneGeometryReader;
+  public:
+    class GeometryChangedMessage : public OriginMessage<RadiographyScene>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+
+    private:
+      RadiographyLayer&        layer_;
+
+    public:
+      GeometryChangedMessage(const RadiographyScene& origin,
+                             RadiographyLayer& layer) :
+        OriginMessage(origin),
+        layer_(layer)
+      {
+      }
+
+      RadiographyLayer& GetLayer() const
+      {
+        return layer_;
+      }
+    };
+
+    class ContentChangedMessage : public OriginMessage<RadiographyScene>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+
+    private:
+      RadiographyLayer&        layer_;
+
+    public:
+      ContentChangedMessage(const RadiographyScene& origin,
+                            RadiographyLayer& layer) :
+        OriginMessage(origin),
+        layer_(layer)
+      {
+      }
+
+      RadiographyLayer& GetLayer() const
+      {
+        return layer_;
+      }
+    };
+
+    class LayerEditedMessage : public OriginMessage<RadiographyScene>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+
+    private:
+      const RadiographyLayer&        layer_;
+
+    public:
+      LayerEditedMessage(const RadiographyScene& origin,
+                         const RadiographyLayer& layer) :
+        OriginMessage(origin),
+        layer_(layer)
+      {
+      }
+
+      const RadiographyLayer& GetLayer() const
+      {
+        return layer_;
+      }
+    };
+
+    class LayerRemovedMessage : public OriginMessage<RadiographyScene>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+
+    private:
+      size_t&        layerIndex_;
+
+    public:
+      LayerRemovedMessage(const RadiographyScene& origin,
+                          size_t& layerIndex) :
+        OriginMessage(origin),
+        layerIndex_(layerIndex)
+      {
+      }
+
+      size_t& GetLayerIndex() const
+      {
+        return layerIndex_;
+      }
+    };
+
+
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, WindowingChangedMessage, RadiographyScene);
+
+    
+    class LayerAccessor : public boost::noncopyable
+    {
+    private:
+      RadiographyScene&  scene_;
+      size_t             index_;
+      RadiographyLayer*  layer_;
+
+    public:
+      LayerAccessor(RadiographyScene& scene,
+                    size_t index);
+
+      LayerAccessor(RadiographyScene& scene,
+                    double x,
+                    double y);
+
+      void Invalidate()
+      {
+        layer_ = NULL;
+      }
+
+      bool IsValid() const
+      {
+        return layer_ != NULL;
+      }
+
+      RadiographyScene& GetScene() const;
+
+      size_t GetIndex() const;
+
+      RadiographyLayer& GetLayer() const;
+    };
+
+
+  protected:
+    typedef std::map<size_t, RadiographyLayer*>  Layers;
+
+    size_t  nextLayerIndex_;
+    bool    hasWindowing_;
+    float   windowingCenter_;
+    float   windowingWidth_;
+    Layers  layers_;
+
+  public:
+    RadiographyLayer& RegisterLayer(RadiographyLayer* layer);
+
+  protected:
+    virtual void _RegisterLayer(RadiographyLayer* layer);
+    virtual void _OnLayerRemoved() {}
+
+    void SetLayerIndex(RadiographyLayer* layer, size_t index)
+    {
+      layer->SetIndex(index);
+    }
+
+    virtual void OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message);
+
+    virtual void OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message);
+    
+    void OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message);
+
+    void OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message);
+
+    virtual void OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message);
+
+  public:
+    RadiographyScene();
+    
+    virtual ~RadiographyScene();
+
+    virtual size_t GetApproximateMemoryUsage() const;
+
+    bool GetWindowing(float& center,
+                      float& width) const;
+
+    void GetWindowingWithDefault(float& center,
+                                 float& width) const;
+
+    virtual void SetWindowing(float center,
+                              float width);
+
+    RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const;
+
+    RadiographyLayer& LoadText(const std::string& utf8,
+                               const std::string& font,
+                               unsigned int fontSize,
+                               uint8_t foreground,
+                               RadiographyLayer::Geometry* geometry,
+                               bool isCenterGeometry);
+
+    RadiographyLayer& UpdateText(size_t layerIndex,
+                                 const std::string& font,
+                                 const std::string& utf8,
+                                 unsigned int fontSize,
+                                 uint8_t foreground);
+
+    RadiographyLayer& LoadTestBlock(unsigned int width,
+                                    unsigned int height,
+                                    RadiographyLayer::Geometry* geometry);
+
+    RadiographyLayer& LoadMask(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners,
+                               const RadiographyDicomLayer& dicomLayer,
+                               float foreground,
+                               RadiographyLayer::Geometry* geometry);
+
+    RadiographyLayer& LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap,  // takes ownership
+                                      RadiographyLayer::Geometry* geometry);
+
+    virtual RadiographyLayer& LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership
+                                             const std::string& instance,
+                                             unsigned int frame,
+                                             Deprecated::DicomFrameConverter* converter,  // takes ownership
+                                             RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode,
+                                             RadiographyLayer::Geometry* geometry);
+
+    virtual RadiographyLayer& LoadDicomFrame(Deprecated::OrthancApiClient& orthanc,
+                                             const std::string& instance,
+                                             unsigned int frame,
+                                             bool httpCompression,
+                                             RadiographyLayer::Geometry* geometry); // pass NULL if you want default geometry
+
+    RadiographyLayer& LoadDicomWebFrame(Deprecated::IWebService& web);
+
+    void RemoveLayer(size_t layerIndex);
+
+    RadiographyLayer& GetLayer(size_t layerIndex);
+
+    const RadiographyLayer& GetLayer(size_t layerIndex) const;
+
+    template <typename TypeLayer>
+    TypeLayer* GetTypedLayer(size_t indexOfType = 0)
+    {
+      std::vector<size_t> layerIndexes;
+      GetLayersIndexes(layerIndexes);
+
+      size_t count = 0;
+
+      for (size_t i = 0; i < layerIndexes.size(); ++i)
+      {
+        TypeLayer* typedLayer = dynamic_cast<TypeLayer*>(layers_[layerIndexes[i]]);
+        if (typedLayer != NULL)
+        {
+          if (count == indexOfType)
+          {
+            return typedLayer;
+          }
+          count++;
+        }
+      }
+
+      return NULL;
+    }
+
+    void GetLayersIndexes(std::vector<size_t>& output) const;
+
+    virtual Extent2D GetSceneExtent(bool minimal) const;
+
+    virtual void Render(Orthanc::ImageAccessor& buffer,
+                        const AffineTransform2D& viewTransform,
+                        ImageInterpolation interpolation,
+                        bool applyWindowing) const;
+
+    bool LookupLayer(size_t& index /* out */,
+                     double x,
+                     double y) const;
+    
+    void DrawBorder(CairoContext& context,
+                    unsigned int layer,
+                    double zoom);
+
+    void GetRange(float& minValue,
+                  float& maxValue) const;
+
+    void ExportToScene2D(Scene2D& output) const;
+
+    // Export using PAM is faster than using PNG, but requires Orthanc
+    // core >= 1.4.3
+    void ExportDicom(Deprecated::OrthancApiClient& orthanc,
+                     const Orthanc::DicomMap& dicom,
+                     const std::string& parentOrthancId,
+                     double pixelSpacingX,
+                     double pixelSpacingY,
+                     bool invert,
+                     bool autoCrop,
+                     ImageInterpolation interpolation,
+                     bool usePam);
+
+    void ExportDicom(Deprecated::OrthancApiClient& orthanc,
+                     const Json::Value& dicomTags,
+                     const std::string& parentOrthancId,
+                     double pixelSpacingX,
+                     double pixelSpacingY,
+                     bool invert,
+                     bool autoCrop,
+                     ImageInterpolation interpolation,
+                     bool usePam);
+
+    void ExportToCreateDicomRequest(Json::Value& createDicomRequestContent,
+                                    const Json::Value& dicomTags,
+                                    const std::string& parentOrthancId,
+                                    double pixelSpacingX,
+                                    double pixelSpacingY,
+                                    bool invert,
+                                    bool autoCrop,
+                                    ImageInterpolation interpolation,
+                                    bool usePam);
+
+    Orthanc::Image* ExportToCreateDicomRequestAndImage(Json::Value& createDicomRequestContent,
+                                                       const Json::Value& dicomTags,
+                                                       const std::string& parentOrthancId,
+                                                       double pixelSpacingX,
+                                                       double pixelSpacingY,
+                                                       bool invert,
+                                                       bool autoCrop,
+                                                       ImageInterpolation interpolation);
+
+    Orthanc::Image* ExportToImage(double pixelSpacingX,
+                                  double pixelSpacingY,
+                                  ImageInterpolation interpolation,
+                                  bool autoCrop,
+                                  bool applyWindowing)
+    {
+      return ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, false, 0, autoCrop, applyWindowing);
+    }
+
+    Orthanc::Image* ExportToImage(double pixelSpacingX,
+                                  double pixelSpacingY,
+                                  ImageInterpolation interpolation,
+                                  bool invert,
+                                  int64_t maxValue /* for inversion */,
+                                  bool autoCrop,
+                                  bool applyWindowing);
+
+    void ExtractLayerFromRenderedScene(Orthanc::ImageAccessor& layer,
+                                       const Orthanc::ImageAccessor& renderedScene,
+                                       size_t layerIndex,
+                                       bool isCropped,
+                                       ImageInterpolation interpolation);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographySceneCommand.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,62 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographySceneCommand.h"
+
+
+namespace OrthancStone
+{
+  RadiographySceneCommand::RadiographySceneCommand(RadiographyScene& scene,
+                                                   size_t layer) :
+    scene_(scene),
+    layer_(layer)
+  {
+  }
+
+  
+  RadiographySceneCommand::RadiographySceneCommand(const RadiographyScene::LayerAccessor& accessor) :
+    scene_(accessor.GetScene()),
+    layer_(accessor.GetIndex())
+  {
+  }
+
+  
+  void RadiographySceneCommand::Undo() const
+  {
+    RadiographyScene::LayerAccessor accessor(scene_, layer_);
+
+    if (accessor.IsValid())
+    {
+      UndoInternal(accessor.GetLayer());
+    }
+  }
+
+  
+  void RadiographySceneCommand::Redo() const
+  {
+    RadiographyScene::LayerAccessor accessor(scene_, layer_);
+
+    if (accessor.IsValid())
+    {
+      RedoInternal(accessor.GetLayer());
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographySceneCommand.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,50 @@
+/**
+ * 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 "../Toolbox/UndoRedoStack.h"
+#include "RadiographyScene.h"
+
+namespace OrthancStone
+{
+  class RadiographySceneCommand : public UndoRedoStack::ICommand
+  {
+  private:
+    RadiographyScene&  scene_;
+    size_t             layer_;
+
+  protected:
+    virtual void UndoInternal(RadiographyLayer& layer) const = 0;
+
+    virtual void RedoInternal(RadiographyLayer& layer) const = 0;
+
+  public:
+    RadiographySceneCommand(RadiographyScene& scene,
+                            size_t layer);
+
+    RadiographySceneCommand(const RadiographyScene::LayerAccessor& accessor);
+
+    virtual void Undo() const;
+
+    virtual void Redo() const;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographySceneReader.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,189 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographySceneReader.h"
+
+#include "../Deprecated/Toolbox/DicomFrameConverter.h"
+
+#include <Core/Images/FontRegistry.h>
+#include <Core/Images/PngReader.h>
+#include <Core/OrthancException.h>
+#include <Core/Toolbox.h>
+
+namespace OrthancStone
+{
+
+  void RadiographySceneBuilder::Read(const Json::Value& input, Orthanc::ImageAccessor* dicomImage /* takes ownership */,
+                                     Deprecated::DicomFrameConverter* dicomFrameConverter  /* takes ownership */,
+                                     RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode
+                                     )
+  {
+    dicomImage_.reset(dicomImage);
+    dicomFrameConverter_.reset(dicomFrameConverter);
+    preferredPhotometricDisplayMode_ = preferredPhotometricDisplayMode;
+    Read(input);
+  }
+
+  RadiographyDicomLayer* RadiographySceneBuilder::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry)
+  {
+    return dynamic_cast<RadiographyDicomLayer*>(&(scene_.LoadDicomImage(dicomImage_.release(), instanceId, frame, dicomFrameConverter_.release(), preferredPhotometricDisplayMode_, geometry)));
+  }
+
+
+  RadiographyDicomLayer* RadiographySceneReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry)
+  {
+    return dynamic_cast<RadiographyDicomLayer*>(&(scene_.LoadDicomFrame(orthancApiClient_, instanceId, frame, false, geometry)));
+  }
+
+  RadiographyDicomLayer* RadiographySceneGeometryReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry)
+  {
+    std::unique_ptr<RadiographyPlaceholderLayer>  layer(new RadiographyPlaceholderLayer(scene_));
+    layer->SetGeometry(*geometry);
+    layer->SetSize(dicomImageWidth_, dicomImageHeight_);
+    scene_.RegisterLayer(layer.get());
+
+    return layer.release();
+  }
+
+  void RadiographySceneBuilder::Read(const Json::Value& input)
+  {
+    unsigned int version = input["version"].asUInt();
+
+    if (version != 1)
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+
+    if (input.isMember("hasWindowing") && input["hasWindowing"].asBool())
+    {
+      scene_.SetWindowing(input["windowCenter"].asFloat(), input["windowWidth"].asFloat());
+    }
+
+    RadiographyDicomLayer* dicomLayer = NULL;
+    for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++)
+    {
+      const Json::Value& jsonLayer = input["layers"][(int)layerIndex];
+      RadiographyLayer::Geometry geometry;
+
+      if (jsonLayer["type"].asString() == "dicom")
+      {
+        ReadLayerGeometry(geometry, jsonLayer);
+        dicomLayer = LoadDicom(jsonLayer["instanceId"].asString(), jsonLayer["frame"].asUInt(), &geometry);
+      }
+      else if (jsonLayer["type"].asString() == "mask")
+      {
+        if (dicomLayer == NULL)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); // we always assumed the dicom layer was read before the mask
+        }
+        ReadLayerGeometry(geometry, jsonLayer);
+
+        float foreground = jsonLayer["foreground"].asFloat();
+        std::vector<Orthanc::ImageProcessing::ImagePoint> corners;
+        for (size_t i = 0; i < jsonLayer["corners"].size(); i++)
+        {
+          Orthanc::ImageProcessing::ImagePoint corner(jsonLayer["corners"][(int)i]["x"].asInt(),
+              jsonLayer["corners"][(int)i]["y"].asInt());
+          corners.push_back(corner);
+        }
+
+        scene_.LoadMask(corners, *dicomLayer, foreground, &geometry);
+      }
+      else if (jsonLayer["type"].asString() == "text")
+      {
+        ReadLayerGeometry(geometry, jsonLayer);
+        scene_.LoadText(jsonLayer["text"].asString(), jsonLayer["font"].asString(), jsonLayer["fontSize"].asUInt(), static_cast<uint8_t>(jsonLayer["foreground"].asUInt()), &geometry, false);
+      }
+      else if (jsonLayer["type"].asString() == "alpha")
+      {
+        ReadLayerGeometry(geometry, jsonLayer);
+
+        const std::string& pngContentBase64 = jsonLayer["content"].asString();
+        std::string pngContent;
+        std::string mimeType;
+        Orthanc::Toolbox::DecodeDataUriScheme(mimeType, pngContent, pngContentBase64);
+
+        std::unique_ptr<Orthanc::ImageAccessor>  image;
+        if (mimeType == "image/png")
+        {
+          image.reset(new Orthanc::PngReader());
+          dynamic_cast<Orthanc::PngReader*>(image.get())->ReadFromMemory(pngContent);
+        }
+        else
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+
+        RadiographyAlphaLayer& layer = dynamic_cast<RadiographyAlphaLayer&>(scene_.LoadAlphaBitmap(image.release(), &geometry));
+
+        if (!jsonLayer["isUsingWindowing"].asBool())
+        {
+          layer.SetForegroundValue((float)(jsonLayer["foreground"].asDouble()));
+        }
+      }
+      else
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+  }
+
+
+
+
+  void RadiographySceneBuilder::ReadDicomLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input)
+  {
+    for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++)
+    {
+      const Json::Value& jsonLayer = input["layers"][(int)layerIndex];
+      if (jsonLayer["type"].asString() == "dicom")
+      {
+        ReadLayerGeometry(geometry, jsonLayer);
+        return;
+      }
+    }
+  }
+
+  void RadiographySceneBuilder::ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& jsonLayer)
+  {
+    {// crop
+      unsigned int x, y, width, height;
+      if (jsonLayer["crop"]["hasCrop"].asBool())
+      {
+        x = jsonLayer["crop"]["x"].asUInt();
+        y = jsonLayer["crop"]["y"].asUInt();
+        width = jsonLayer["crop"]["width"].asUInt();
+        height = jsonLayer["crop"]["height"].asUInt();
+        geometry.SetCrop(x, y, width, height);
+      }
+    }
+
+    geometry.SetAngle(jsonLayer["angle"].asDouble());
+    geometry.SetResizeable(jsonLayer["isResizable"].asBool());
+    geometry.SetPan(jsonLayer["pan"]["x"].asDouble(), jsonLayer["pan"]["y"].asDouble());
+    geometry.SetPixelSpacing(jsonLayer["pixelSpacing"]["x"].asDouble(), jsonLayer["pixelSpacing"]["y"].asDouble());
+
+    // these fields were introduced later -> they might not exist
+    if (jsonLayer.isMember("flipVertical"))
+    {
+      geometry.SetFlipVertical(jsonLayer["flipVertical"].asBool());
+    }
+    if (jsonLayer.isMember("flipHorizontal"))
+    {
+      geometry.SetFlipHorizontal(jsonLayer["flipHorizontal"].asBool());
+    }
+
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographySceneReader.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,113 @@
+/**
+ * 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 "RadiographyScene.h"
+#include "RadiographyAlphaLayer.h"
+#include "RadiographyDicomLayer.h"
+#include "RadiographyMaskLayer.h"
+#include "RadiographyTextLayer.h"
+#include "../Deprecated/Toolbox/OrthancApiClient.h"
+
+#include <json/value.h>
+#include <Core/Images/FontRegistry.h>
+
+namespace OrthancStone
+{
+  // a layer containing only the geometry of a DICOM layer (bit hacky !)
+  class RadiographyPlaceholderLayer : public RadiographyDicomLayer
+  {
+  public:
+    RadiographyPlaceholderLayer(const RadiographyScene& scene) :
+      RadiographyDicomLayer(scene)
+    {
+    }
+
+  };
+
+
+  // HACK: I had to introduce this builder class in order to be able to recreate a RadiographyScene
+  // from a serialized scene that is passed to web-workers.
+  // It needs some architecturing...
+  class RadiographySceneBuilder : public boost::noncopyable
+  {
+  protected:
+    RadiographyScene&                               scene_;
+    std::unique_ptr<Orthanc::ImageAccessor>           dicomImage_;
+    std::unique_ptr<Deprecated::DicomFrameConverter>  dicomFrameConverter_;
+    RadiographyPhotometricDisplayMode               preferredPhotometricDisplayMode_;
+
+  public:
+    RadiographySceneBuilder(RadiographyScene& scene) :
+      scene_(scene)
+    {
+    }
+
+    void Read(const Json::Value& input);
+    void Read(const Json::Value& input,
+              Orthanc::ImageAccessor* dicomImage, // takes ownership
+              Deprecated::DicomFrameConverter* dicomFrameConverter, // takes ownership
+              RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode
+              );
+
+    static void ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input);
+    static void ReadDicomLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input);
+
+  protected:
+    virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry);
+
+  };
+
+
+  class RadiographySceneReader : public RadiographySceneBuilder
+  {
+    Deprecated::OrthancApiClient&             orthancApiClient_;
+
+  public:
+    RadiographySceneReader(RadiographyScene& scene, Deprecated::OrthancApiClient& orthancApiClient) :
+      RadiographySceneBuilder(scene),
+      orthancApiClient_(orthancApiClient)
+    {
+    }
+
+  protected:
+    virtual RadiographyDicomLayer*  LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry);
+  };
+
+  // reads the whole scene but the DICOM image such that we have the full geometry
+  class RadiographySceneGeometryReader : public RadiographySceneBuilder
+  {
+    unsigned int dicomImageWidth_;
+    unsigned int dicomImageHeight_;
+
+  public:
+    RadiographySceneGeometryReader(RadiographyScene& scene, unsigned int dicomImageWidth, unsigned int dicomImageHeight) :
+      RadiographySceneBuilder(scene),
+      dicomImageWidth_(dicomImageWidth),
+      dicomImageHeight_(dicomImageHeight)
+    {
+    }
+
+  protected:
+    virtual RadiographyDicomLayer*  LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographySceneWriter.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,168 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographySceneWriter.h"
+
+#include <Core/OrthancException.h>
+#include <Core/Images/PngWriter.h>
+#include <Core/Toolbox.h>
+
+namespace OrthancStone
+{
+  void RadiographySceneWriter::Write(Json::Value& output, const RadiographyScene& scene)
+  {
+    output["version"] = 1;
+    float windowCenter, windowWidth;
+    bool hasWindowing = scene.GetWindowing(windowCenter, windowWidth);
+    output["hasWindowing"] = hasWindowing;
+    if (hasWindowing)
+    {
+      output["windowCenter"] = windowCenter;
+      output["windowWidth"] = windowWidth;
+    }
+    output["layers"] = Json::arrayValue;
+
+    std::vector<size_t> layersIndexes;
+    scene.GetLayersIndexes(layersIndexes);
+
+    for (std::vector<size_t>::iterator itLayerIndex = layersIndexes.begin(); itLayerIndex < layersIndexes.end(); itLayerIndex++)
+    {
+      Json::Value layer;
+      WriteLayer(layer, scene.GetLayer(*itLayerIndex));
+      output["layers"].append(layer);
+    }
+  }
+
+  void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyDicomLayer& layer)
+  {
+    output["type"] = "dicom";
+    output["instanceId"] = layer.GetInstanceId();
+    output["frame"] = layer.GetFrame();
+  }
+
+  void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyTextLayer& layer)
+  {
+    output["type"] = "text";
+    output["text"] = layer.GetText();
+    output["font"] = layer.GetFont();
+    output["fontSize"] = layer.GetFontSize();
+    output["foreground"] = layer.GetForegroundGreyLevel();
+  }
+
+  void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyMaskLayer& layer)
+  {
+    output["type"] = "mask";
+    output["instanceId"] = layer.GetInstanceId(); // the dicom layer it's being linked to
+    output["foreground"] = layer.GetForeground();
+    output["corners"] = Json::arrayValue;
+    const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners = layer.GetCorners();
+    for (size_t i = 0; i < corners.size(); i++)
+    {
+      Json::Value corner;
+      corner["x"] = corners[i].GetX();
+      corner["y"] = corners[i].GetY();
+      output["corners"].append(corner);
+    }
+  }
+
+  void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyAlphaLayer& layer)
+  {
+    output["type"] = "alpha";
+
+    //output["bitmap"] =
+    const Orthanc::ImageAccessor& alpha = layer.GetAlpha();
+
+    Orthanc::PngWriter pngWriter;
+    std::string pngContent;
+    std::string pngContentBase64;
+    pngWriter.WriteToMemory(pngContent, alpha);
+
+    Orthanc::Toolbox::EncodeDataUriScheme(pngContentBase64, "image/png", pngContent);
+    output["content"] = pngContentBase64;
+    output["foreground"] = layer.GetForegroundValue();
+  }
+
+  void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyLayer& layer)
+  {
+    const RadiographyLayer::Geometry& geometry = layer.GetGeometry();
+
+    {// crop
+      Json::Value crop;
+      if (geometry.HasCrop())
+      {
+        unsigned int x, y, width, height;
+        geometry.GetCrop(x, y, width, height);
+        crop["hasCrop"] = true;
+        crop["x"] = x;
+        crop["y"] = y;
+        crop["width"] = width;
+        crop["height"] = height;
+      }
+      else
+      {
+        crop["hasCrop"] = false;
+      }
+
+      output["crop"] = crop;
+    }
+
+    output["angle"] = geometry.GetAngle();
+    output["isResizable"] = geometry.IsResizeable();
+
+    {// pan
+      Json::Value pan;
+      pan["x"] = geometry.GetPanX();
+      pan["y"] = geometry.GetPanY();
+      output["pan"] = pan;
+    }
+
+    {// pixelSpacing
+      Json::Value pan;
+      pan["x"] = geometry.GetPixelSpacingX();
+      pan["y"] = geometry.GetPixelSpacingY();
+      output["pixelSpacing"] = pan;
+    }
+
+    output["flipVertical"] = geometry.GetFlipVertical();
+    output["flipHorizontal"] = geometry.GetFlipHorizontal();
+
+    if (dynamic_cast<const RadiographyTextLayer*>(&layer) != NULL)
+    {
+      WriteLayer(output, dynamic_cast<const RadiographyTextLayer&>(layer));
+    }
+    else if (dynamic_cast<const RadiographyDicomLayer*>(&layer) != NULL)
+    {
+      WriteLayer(output, dynamic_cast<const RadiographyDicomLayer&>(layer));
+    }
+    else if (dynamic_cast<const RadiographyAlphaLayer*>(&layer) != NULL)
+    {
+      WriteLayer(output, dynamic_cast<const RadiographyAlphaLayer&>(layer));
+    }
+    else if (dynamic_cast<const RadiographyMaskLayer*>(&layer) != NULL)
+    {
+      WriteLayer(output, dynamic_cast<const RadiographyMaskLayer&>(layer));
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographySceneWriter.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,52 @@
+/**
+ * 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 "RadiographyScene.h"
+#include "RadiographyAlphaLayer.h"
+#include "RadiographyDicomLayer.h"
+#include "RadiographyTextLayer.h"
+#include "RadiographyMaskLayer.h"
+#include <json/value.h>
+
+namespace OrthancStone
+{
+  class RadiographyScene;
+
+  class RadiographySceneWriter : public boost::noncopyable
+  {
+
+  public:
+    RadiographySceneWriter()
+    {
+    }
+
+    void Write(Json::Value& output, const RadiographyScene& scene);
+
+  private:
+    void WriteLayer(Json::Value& output, const RadiographyLayer& layer);
+    void WriteLayer(Json::Value& output, const RadiographyDicomLayer& layer);
+    void WriteLayer(Json::Value& output, const RadiographyTextLayer& layer);
+    void WriteLayer(Json::Value& output, const RadiographyAlphaLayer& layer);
+    void WriteLayer(Json::Value& output, const RadiographyMaskLayer& layer);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyTextLayer.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,53 @@
+/**
+ * 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/>.
+ **/
+
+#include "RadiographyTextLayer.h"
+
+#include "Core/OrthancException.h"
+#include "RadiographyScene.h"
+#include "../Toolbox/TextRenderer.h"
+
+namespace OrthancStone
+{
+  std::map<std::string, Orthanc::EmbeddedResources::FileResourceId> RadiographyTextLayer::fonts_;
+
+  void RadiographyTextLayer::SetText(const std::string& utf8,
+                                     const std::string& font,
+                                     unsigned int fontSize,
+                                     uint8_t foregroundGreyLevel)
+  {
+    if (fonts_.find(font) == fonts_.end())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, "The font has not been registered");
+    }
+
+    text_ = utf8;
+    font_ = font;
+    fontSize_ = fontSize;
+    foregroundGreyLevel_ = foregroundGreyLevel;
+
+    SetAlpha(TextRenderer::Render(fonts_[font_],
+                                  fontSize_,
+                                  text_));
+
+    SetForegroundValue(foregroundGreyLevel * 256.0f);
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyTextLayer.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,72 @@
+/**
+ * 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 "RadiographyAlphaLayer.h"
+
+namespace OrthancStone
+{
+  class RadiographyScene;
+
+  class RadiographyTextLayer : public RadiographyAlphaLayer
+  {
+  private:
+    std::string                 text_;
+    std::string                 font_;
+    unsigned int                fontSize_;
+    uint8_t                     foregroundGreyLevel_;
+
+    static std::map<std::string, Orthanc::EmbeddedResources::FileResourceId>  fonts_;
+  public:
+    RadiographyTextLayer(const RadiographyScene& scene) :
+      RadiographyAlphaLayer(scene)
+    {
+    }
+
+    void SetText(const std::string& utf8, const std::string& font, unsigned int fontSize, uint8_t foregroundGreyLevel);
+
+    const std::string& GetText() const
+    {
+      return text_;
+    }
+
+    const std::string& GetFont() const
+    {
+      return font_;
+    }
+
+    unsigned int GetFontSize() const
+    {
+      return fontSize_;
+    }
+
+    uint8_t GetForegroundGreyLevel() const
+    {
+      return foregroundGreyLevel_;
+    }
+
+    static void RegisterFont(const std::string& name, Orthanc::EmbeddedResources::FileResourceId fontResourceId)
+    {
+      fonts_[name] = fontResourceId;
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyWidget.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,284 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographyWidget.h"
+
+#include <Core/OrthancException.h>
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+
+#include "RadiographyMaskLayer.h"
+
+namespace OrthancStone
+{
+
+  bool RadiographyWidget::IsInvertedInternal() const
+  {
+    // MONOCHROME1 images must be inverted and the user can invert the 
+    // image, too -> XOR the two
+    return (scene_->GetPreferredPhotomotricDisplayMode() == 
+            RadiographyPhotometricDisplayMode_Monochrome1) ^ invert_; 
+  }
+
+  void RadiographyWidget::RenderBackground(
+    Orthanc::ImageAccessor& image, float minValue, float maxValue)
+  {
+    // wipe background before rendering
+    float backgroundValue = minValue;
+
+    switch (scene_->GetPreferredPhotomotricDisplayMode())
+    {
+    case RadiographyPhotometricDisplayMode_Monochrome1:
+    case RadiographyPhotometricDisplayMode_Default:
+      if (IsInvertedInternal())
+        backgroundValue = maxValue;
+      else
+        backgroundValue = minValue;
+      break;
+    case RadiographyPhotometricDisplayMode_Monochrome2:
+      if (IsInvertedInternal())
+        backgroundValue = minValue;
+      else
+        backgroundValue = maxValue;
+      break;
+    default:
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+
+    Orthanc::ImageProcessing::Set(image, static_cast<int64_t>(backgroundValue));
+  }
+
+  bool RadiographyWidget::RenderInternal(unsigned int width,
+                                         unsigned int height,
+                                         ImageInterpolation interpolation)
+  {
+    if (floatBuffer_.get() == NULL ||
+        floatBuffer_->GetWidth() != width ||
+        floatBuffer_->GetHeight() != height)
+    {
+      floatBuffer_.reset(new Orthanc::Image(
+        Orthanc::PixelFormat_Float32, width, height, false));
+
+      if (floatBuffer_.get() == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "RadiographyWidget::RenderInternal: unable to allocate float buffer");
+      }
+    }
+
+    if (cairoBuffer_.get() == NULL ||
+        cairoBuffer_->GetWidth() != width ||
+        cairoBuffer_->GetHeight() != height)
+    {
+      cairoBuffer_.reset(new CairoSurface(width, height, false /* no alpha */));
+
+      if (cairoBuffer_.get() == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "RadiographyWidget::RenderInternal: unable to allocate cairo buffer");
+      }
+    }
+
+    RenderBackground(*floatBuffer_, 0.0, 65535.0);
+
+    scene_->Render(*floatBuffer_, GetView().GetMatrix(), interpolation, true);
+
+    // Conversion from Float32 to BGRA32 (cairo). Very similar to
+    // GrayscaleFrameRenderer => TODO MERGE?
+    Orthanc::ImageAccessor target;
+    cairoBuffer_->GetWriteableAccessor(target);
+
+    bool invert = IsInvertedInternal();
+
+    for (unsigned int y = 0; y < height; y++)
+    {
+      const float* p = reinterpret_cast<const float*>(floatBuffer_->GetConstRow(y));
+      uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
+
+      for (unsigned int x = 0; x < width; x++, p++, q += 4)
+      {
+        uint8_t v = 0;
+        if (*p >= 65535.0)
+        {
+          v = 255;
+        }
+        else if (*p <= 0.0)
+        {
+          v = 0;
+        }
+        else
+        {
+          v = static_cast<uint8_t>(*p / 256.0);
+        }
+
+        if (invert)
+        {
+          v = 255 - v;
+        }
+
+        q[0] = v;
+        q[1] = v;
+        q[2] = v;
+        q[3] = 255;
+      }
+    }
+
+    return true;
+  }
+
+
+  bool RadiographyWidget::RenderScene(CairoContext& context,
+                                      const Deprecated::ViewportGeometry& view)
+  {
+    cairo_t* cr = context.GetObject();
+
+    if (RenderInternal(context.GetWidth(), context.GetHeight(), interpolation_))
+    {
+      // https://www.cairographics.org/FAQ/#paint_from_a_surface
+      cairo_save(cr);
+      cairo_identity_matrix(cr);
+      cairo_set_source_surface(cr, cairoBuffer_->GetObject(), 0, 0);
+      cairo_paint(cr);
+      cairo_restore(cr);
+    }
+    else
+    {
+      // https://www.cairographics.org/FAQ/#clear_a_surface
+      context.SetSourceColor(0, 0, 0);
+      cairo_paint(cr);
+    }
+
+    if (hasSelection_)
+    {
+      scene_->DrawBorder(
+        context, static_cast<unsigned int>(selectedLayer_), view.GetZoom());
+    }
+
+    return true;
+  }
+
+
+  RadiographyWidget::RadiographyWidget(boost::shared_ptr<RadiographyScene> scene,
+                                       const std::string& name) :
+    WorldSceneWidget(name),
+    invert_(false),
+    interpolation_(ImageInterpolation_Nearest),
+    hasSelection_(false),
+    selectedLayer_(0)    // Dummy initialization
+  {
+    SetScene(scene);
+  }
+
+
+  void RadiographyWidget::Select(size_t layer)
+  {
+    hasSelection_ = true;
+    selectedLayer_ = layer;
+
+    NotifyContentChanged();
+    BroadcastMessage(SelectionChangedMessage(*this));
+  }
+
+  void RadiographyWidget::Unselect()
+  {
+    hasSelection_ = false;
+
+    NotifyContentChanged();
+    BroadcastMessage(SelectionChangedMessage(*this));
+  }
+
+  bool RadiographyWidget::LookupSelectedLayer(size_t& layer) const
+  {
+    if (hasSelection_)
+    {
+      layer = selectedLayer_;
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  
+  void RadiographyWidget::OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message)
+  {
+//    LOG(INFO) << "Scene geometry has changed";
+    FitContent();
+  }
+
+  
+  void RadiographyWidget::OnContentChanged(const RadiographyScene::ContentChangedMessage& message)
+  {
+//    LOG(INFO) << "Scene content has changed";
+    NotifyContentChanged();
+  }
+
+  void RadiographyWidget::OnLayerRemoved(const RadiographyScene::LayerRemovedMessage& message)
+  {
+    size_t removedLayerIndex = message.GetLayerIndex();
+    if (hasSelection_ && selectedLayer_ == removedLayerIndex)
+    {
+      Unselect();
+    }
+    NotifyContentChanged();
+  }
+  
+  void RadiographyWidget::SetInvert(bool invert)
+  {
+    if (invert_ != invert)
+    {
+      invert_ = invert;
+      NotifyContentChanged();
+    }
+  }
+
+  
+    void RadiographyWidget::SwitchInvert()
+  {
+    invert_ = !invert_;
+    NotifyContentChanged();
+  }
+
+
+  void RadiographyWidget::SetInterpolation(ImageInterpolation interpolation)
+  {
+    if (interpolation_ != interpolation)
+    {
+      interpolation_ = interpolation;
+      NotifyContentChanged();
+    }
+  }
+
+  void RadiographyWidget::SetScene(boost::shared_ptr<RadiographyScene> scene)
+  {
+    scene_ = scene;
+
+    Register<RadiographyScene::GeometryChangedMessage>(*scene_, &RadiographyWidget::OnGeometryChanged);
+    Register<RadiographyScene::ContentChangedMessage>(*scene_, &RadiographyWidget::OnContentChanged);
+    Register<RadiographyScene::LayerRemovedMessage>(*scene_, &RadiographyWidget::OnLayerRemoved);
+
+    Unselect();
+
+    NotifyContentChanged();
+
+    // force redraw
+    FitContent();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyWidget.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,131 @@
+/**
+ * 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 "../Deprecated/Widgets/WorldSceneWidget.h"
+#include "../Messages/ObserverBase.h"
+#include "RadiographyScene.h"
+
+
+namespace OrthancStone
+{
+  class RadiographyMaskLayer;
+
+  class RadiographyWidget :
+    public Deprecated::WorldSceneWidget,
+    public ObserverBase<RadiographyWidget>,
+    public IObservable
+  {
+  public:
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SelectionChangedMessage, RadiographyWidget);
+
+  private:
+    boost::shared_ptr<RadiographyScene>    scene_;
+    std::unique_ptr<Orthanc::ImageAccessor>  floatBuffer_;
+    std::unique_ptr<CairoSurface>            cairoBuffer_;
+    bool                                   invert_;
+    ImageInterpolation                     interpolation_;
+    bool                                   hasSelection_;
+    size_t                                 selectedLayer_;
+
+    bool RenderInternal(unsigned int width,
+                        unsigned int height,
+                        ImageInterpolation interpolation);
+
+  protected:
+    virtual Extent2D GetSceneExtent()
+    {
+      return scene_->GetSceneExtent(false);
+    }
+
+    virtual bool RenderScene(CairoContext& context,
+                             const Deprecated::ViewportGeometry& view);
+
+    virtual void RenderBackground(Orthanc::ImageAccessor& image, float minValue, float maxValue);
+
+    bool IsInvertedInternal() const;
+
+  public:
+    RadiographyWidget(boost::shared_ptr<RadiographyScene> scene,  // TODO: check how we can avoid boost::shared_ptr here since we don't want them in the public API (app is keeping a boost::shared_ptr to this right now)
+                      const std::string& name);
+
+    RadiographyScene& GetScene() const
+    {
+      return *scene_;
+    }
+
+    void SetScene(boost::shared_ptr<RadiographyScene> scene);
+
+    void Select(size_t layer);
+
+    void Unselect();
+
+    template<typename LayerType> bool SelectLayerByType(size_t index = 0);
+
+    bool LookupSelectedLayer(size_t& layer) const;
+
+    void OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message);
+
+    void OnContentChanged(const RadiographyScene::ContentChangedMessage& message);
+
+    void OnLayerRemoved(const RadiographyScene::LayerRemovedMessage& message);
+
+    void SetInvert(bool invert);
+
+    void SwitchInvert();
+
+    bool IsInverted() const
+    {
+      return invert_;
+    }
+
+    void SetInterpolation(ImageInterpolation interpolation);
+
+    ImageInterpolation GetInterpolation() const
+    {
+      return interpolation_;
+    }
+  };
+
+  template<typename LayerType> bool RadiographyWidget::SelectLayerByType(size_t index)
+  {
+    std::vector<size_t> layerIndexes;
+    size_t count = 0;
+    scene_->GetLayersIndexes(layerIndexes);
+
+    for (size_t i = 0; i < layerIndexes.size(); ++i)
+    {
+      const LayerType* typedLayer = dynamic_cast<const LayerType*>(&(scene_->GetLayer(layerIndexes[i])));
+      if (typedLayer != NULL)
+      {
+        if (count == index)
+        {
+          Select(layerIndexes[i]);
+          return true;
+        }
+        count++;
+      }
+    }
+
+    return false;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyWindowingTracker.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,192 @@
+/**
+ * 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/>.
+ **/
+
+
+#include "RadiographyWindowingTracker.h"
+#include "RadiographyWidget.h"
+
+#include <Core/OrthancException.h>
+
+
+namespace OrthancStone
+{
+  class RadiographyWindowingTracker::UndoRedoCommand : public UndoRedoStack::ICommand
+  {
+  private:
+    RadiographyScene&  scene_;
+    float              sourceCenter_;
+    float              sourceWidth_;
+    float              targetCenter_;
+    float              targetWidth_;
+
+  public:
+    UndoRedoCommand(const RadiographyWindowingTracker& tracker) :
+      scene_(tracker.scene_),
+      sourceCenter_(tracker.sourceCenter_),
+      sourceWidth_(tracker.sourceWidth_)
+    {
+      scene_.GetWindowingWithDefault(targetCenter_, targetWidth_);
+    }
+
+    virtual void Undo() const
+    {
+      scene_.SetWindowing(sourceCenter_, sourceWidth_);
+    }
+      
+    virtual void Redo() const
+    {
+      scene_.SetWindowing(targetCenter_, targetWidth_);
+    }
+  };
+
+
+  void RadiographyWindowingTracker::ComputeAxisEffect(int& deltaCenter,
+                                                      int& deltaWidth,
+                                                      int delta,
+                                                      Action actionNegative,
+                                                      Action actionPositive)
+  {
+    if (delta < 0)
+    {
+      switch (actionNegative)
+      {
+        case Action_IncreaseWidth:
+          deltaWidth = -delta;
+          break;
+
+        case Action_DecreaseWidth:
+          deltaWidth = delta;
+          break;
+
+        case Action_IncreaseCenter:
+          deltaCenter = -delta;
+          break;
+
+        case Action_DecreaseCenter:
+          deltaCenter = delta;
+          break;
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+    }
+    else if (delta > 0)
+    {
+      switch (actionPositive)
+      {
+        case Action_IncreaseWidth:
+          deltaWidth = delta;
+          break;
+
+        case Action_DecreaseWidth:
+          deltaWidth = -delta;
+          break;
+
+        case Action_IncreaseCenter:
+          deltaCenter = delta;
+          break;
+
+        case Action_DecreaseCenter:
+          deltaCenter = -delta;
+          break;
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+    }
+  }
+    
+    
+  RadiographyWindowingTracker::RadiographyWindowingTracker(UndoRedoStack& undoRedoStack,
+                                                           RadiographyScene& scene,
+                                                           RadiographyWidget& widget,
+                                                           ImageInterpolation interpolationDuringTracking,
+                                                           int x,
+                                                           int y,
+                                                           Action leftAction,
+                                                           Action rightAction,
+                                                           Action upAction,
+                                                           Action downAction) :
+    undoRedoStack_(undoRedoStack),
+    scene_(scene),
+    widget_(widget),
+    initialWidgetInterpolation_(widget.GetInterpolation()),
+    clickX_(x),
+    clickY_(y),
+    leftAction_(leftAction),
+    rightAction_(rightAction),
+    upAction_(upAction),
+    downAction_(downAction)
+  {
+    scene_.GetWindowingWithDefault(sourceCenter_, sourceWidth_);
+    widget_.SetInterpolation(interpolationDuringTracking);
+
+    float minValue, maxValue;
+    scene.GetRange(minValue, maxValue);
+
+    assert(minValue <= maxValue);
+
+    float delta = (maxValue - minValue);
+    strength_ = delta / 1000.0f; // 1px move will change the ww/wc by 0.1%
+
+    if (strength_ < 1)
+    {
+      strength_ = 1;
+    }
+  }
+
+
+  void RadiographyWindowingTracker::Render(CairoContext& context,
+                                           double zoom)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+  
+
+  void RadiographyWindowingTracker::MouseUp()
+  {
+    widget_.SetInterpolation(initialWidgetInterpolation_);
+    undoRedoStack_.Add(new UndoRedoCommand(*this));
+  }
+
+
+  void RadiographyWindowingTracker::MouseMove(int displayX,
+                                              int displayY,
+                                              double sceneX,
+                                              double sceneY,
+                                              const std::vector<Deprecated::Touch>& displayTouches,
+                                              const std::vector<Deprecated::Touch>& sceneTouches)
+  {
+    // This follows the behavior of the Osimis Web viewer:
+    // https://bitbucket.org/osimis/osimis-webviewer-plugin/src/master/frontend/src/app/viewport/image-plugins/windowing-viewport-tool.class.js
+
+    static const float SCALE = 1.0;
+      
+    int deltaCenter = 0;
+    int deltaWidth = 0;
+
+    ComputeAxisEffect(deltaCenter, deltaWidth, displayX - clickX_, leftAction_, rightAction_);
+    ComputeAxisEffect(deltaCenter, deltaWidth, displayY - clickY_, upAction_, downAction_);
+
+    float newCenter = sourceCenter_ + (deltaCenter / SCALE * strength_);
+    float newWidth = sourceWidth_ + (deltaWidth / SCALE * strength_);
+    scene_.SetWindowing(newCenter, newWidth);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Radiography/RadiographyWindowingTracker.h	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,96 @@
+/**
+ * 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 "../Toolbox/UndoRedoStack.h"
+#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
+#include "RadiographyScene.h"
+
+namespace OrthancStone
+{
+
+  class RadiographyWidget;
+
+  class RadiographyWindowingTracker : public Deprecated::IWorldSceneMouseTracker
+  {   
+  public:
+    enum Action
+    {
+      Action_IncreaseWidth,
+      Action_DecreaseWidth,
+      Action_IncreaseCenter,
+      Action_DecreaseCenter
+    };
+    
+  private:
+    class UndoRedoCommand;
+
+    UndoRedoStack&      undoRedoStack_;
+    RadiographyScene&   scene_;
+    RadiographyWidget&  widget_;
+    ImageInterpolation  initialWidgetInterpolation_;
+    int                 clickX_;
+    int                 clickY_;
+    Action              leftAction_;
+    Action              rightAction_;
+    Action              upAction_;
+    Action              downAction_;
+    float               strength_;
+    float               sourceCenter_;
+    float               sourceWidth_;
+
+    static void ComputeAxisEffect(int& deltaCenter,
+                                  int& deltaWidth,
+                                  int delta,
+                                  Action actionNegative,
+                                  Action actionPositive);    
+    
+  public:
+    RadiographyWindowingTracker(UndoRedoStack& undoRedoStack,
+                                RadiographyScene& scene,
+                                RadiographyWidget& widget,
+                                ImageInterpolation interpolationDuringTracking,
+                                int x,
+                                int y,
+                                Action leftAction,
+                                Action rightAction,
+                                Action upAction,
+                                Action downAction);
+    
+    virtual bool HasRender() const
+    {
+      return false;
+    }
+
+    virtual void Render(CairoContext& context,
+                        double zoom);
+
+    virtual void MouseUp();
+
+    virtual void MouseMove(int displayX,
+                           int displayY,
+                           double sceneX,
+                           double sceneY,
+                           const std::vector<Deprecated::Touch>& displayTouches,
+                           const std::vector<Deprecated::Touch>& sceneTouches);
+  };
+}
--- a/Framework/OpenGL/OpenGLIncludes.h	Wed Apr 29 22:06:24 2020 +0200
+++ b/Framework/OpenGL/OpenGLIncludes.h	Wed Apr 29 22:06:58 2020 +0200
@@ -42,15 +42,6 @@
 #  include <GL/glext.h>
 #endif
 
-#if ORTHANC_ENABLE_QT == 1
-// TODO: currently there are no checks in QT
-
-#   define ORTHANC_OPENGL_CHECK(name)
-#   define ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT(msg)
-#   define ORTHANC_CHECK_CURRENT_CONTEXT(context)
-
-#endif
-
 #if ORTHANC_ENABLE_SDL == 1
 # include <SDL_video.h>
 
--- a/Framework/Radiography/RadiographyAlphaLayer.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographyAlphaLayer.h"
-
-#include "RadiographyScene.h"
-
-#include <Core/Compatibility.h>
-#include <Core/Images/Image.h>
-#include <Core/OrthancException.h>
-
-#include "../Toolbox/ImageGeometry.h"
-
-namespace OrthancStone
-{
-
-  void RadiographyAlphaLayer::SetAlpha(Orthanc::ImageAccessor* image)
-  {
-    std::unique_ptr<Orthanc::ImageAccessor> raii(image);
-
-    if (image == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-    }
-
-    if (image->GetFormat() != Orthanc::PixelFormat_Grayscale8)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
-    }
-
-    SetSize(image->GetWidth(), image->GetHeight());
-
-#if __cplusplus < 201103L
-    alpha_.reset(raii.release());
-#else
-    alpha_ = std::move(raii);
-#endif
-
-    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
-  }
-
-  void RadiographyAlphaLayer::Render(Orthanc::ImageAccessor& buffer,
-                                     const AffineTransform2D& viewTransform,
-                                     ImageInterpolation interpolation,
-                                     float windowCenter,
-                                     float windowWidth,
-                                     bool applyWindowing) const
-  {
-    if (alpha_.get() == NULL)
-    {
-      return;
-    }
-
-    if (buffer.GetFormat() != Orthanc::PixelFormat_Float32)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
-    }
-
-    unsigned int cropX, cropY, cropWidth, cropHeight;
-    GetCrop(cropX, cropY, cropWidth, cropHeight);
-
-    const AffineTransform2D t = AffineTransform2D::Combine(
-          viewTransform, GetTransform(),
-          AffineTransform2D::CreateOffset(cropX, cropY));
-
-    Orthanc::ImageAccessor cropped;
-    alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
-
-    Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false);
-
-    unsigned int x1, y1, x2, y2;
-
-    if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2,
-                                                    t.GetHomogeneousMatrix(),
-                                                    cropped.GetWidth(),
-                                                    cropped.GetHeight(),
-                                                    buffer.GetWidth(),
-                                                    buffer.GetHeight()))
-    {
-      return;  // layer is outside the buffer
-    }
-
-    t.Apply(tmp, cropped, interpolation, true /* clear */);
-
-    float value = foreground_;
-
-    if (!applyWindowing) // if applying the windowing, it means we are ie rendering the image for a realtime visualization -> the foreground_ value is the value we want to see on the screen -> don't change it
-    {
-      // if not applying the windowing, it means ie that we are saving a dicom image to file and the windowing will be applied by a viewer later on -> we want the "foreground" value to be correct once the windowing will be applied
-      value = windowCenter - windowWidth/2 + (foreground_ / 65535.0f) * windowWidth;
-
-      if (value < 0.0f)
-      {
-        value = 0.0f;
-      }
-      if (value > 65535.0f)
-      {
-        value = 65535.0f;
-      }
-    }
-
-    for (unsigned int y = y1; y <= y2; y++)
-    {
-      float *q = reinterpret_cast<float*>(buffer.GetRow(y)) + x1;
-      const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y)) + x1;
-
-      for (unsigned int x = x1; x <= x2; x++, p++, q++)
-      {
-        float a = static_cast<float>(*p) / 255.0f;
-
-        *q = (a * value + (1.0f - a) * (*q));
-      }
-    }
-  }
-
-  bool RadiographyAlphaLayer::GetRange(float& minValue,
-                                       float& maxValue) const
-  {
-    minValue = 0;
-    maxValue = 0;
-
-    if (foreground_ < 0)
-    {
-      minValue = foreground_;
-    }
-
-    if (foreground_ > 0)
-    {
-      maxValue = foreground_;
-    }
-
-    return true;
-  }
-}
--- a/Framework/Radiography/RadiographyAlphaLayer.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/**
- * 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 "RadiographyLayer.h"
-
-#include <Core/Compatibility.h>
-
-namespace OrthancStone
-{
-  class RadiographyScene;
-
-  // creates a transparent layer whose alpha channel is provided as a UINT8 image to SetAlpha.
-  // The color of the "mask" is either defined by a ForegroundValue or by the center value of the
-  // windowing from the scene.
-  class RadiographyAlphaLayer : public RadiographyLayer
-  {
-  private:
-    std::unique_ptr<Orthanc::ImageAccessor>  alpha_;       // Grayscale8 in the range [0, 255]  0 = transparent, 255 = opaque -> the foreground value will be displayed
-    float                                  foreground_;  // in the range [0.0, 65535.0]
-
-  public:
-    RadiographyAlphaLayer(const RadiographyScene& scene) :
-      RadiographyLayer(scene),
-      foreground_(0)
-    {
-    }
-
-
-    void SetForegroundValue(float foreground)
-    {
-      foreground_ = foreground;
-    }
-
-    float GetForegroundValue() const
-    {
-      return foreground_;
-    }
-
-    void SetAlpha(Orthanc::ImageAccessor* image);
-
-    virtual bool GetDefaultWindowing(float& center,
-                                     float& width) const
-    {
-      return false;
-    }
-
-
-    virtual void Render(Orthanc::ImageAccessor& buffer,
-                        const AffineTransform2D& viewTransform,
-                        ImageInterpolation interpolation,
-                        float windowCenter,
-                        float windowWidth,
-                        bool applyWindowing) const;
-
-    virtual bool GetRange(float& minValue,
-                          float& maxValue) const;
-
-    const Orthanc::ImageAccessor& GetAlpha() const
-    {
-      return *(alpha_.get());
-    }
-
-
-  };
-}
--- a/Framework/Radiography/RadiographyDicomLayer.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,264 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographyDicomLayer.h"
-
-#include "RadiographyScene.h"
-#include "../Deprecated/Toolbox/DicomFrameConverter.h"
-
-#include <Core/OrthancException.h>
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Plugins/Samples/Common/DicomDatasetReader.h>
-#include "../Toolbox/ImageGeometry.h"
-
-static OrthancPlugins::DicomTag  ConvertTag(const Orthanc::DicomTag& tag)
-{
-  return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement());
-}
-
-namespace OrthancStone
-{
-
-  void RadiographyDicomLayer::ApplyConverter()
-  {
-    if (source_.get() != NULL &&
-        converter_.get() != NULL)
-    {
-      converted_.reset(converter_->ConvertFrame(*source_));
-    }
-  }
-
-
-  RadiographyDicomLayer::RadiographyDicomLayer(const RadiographyScene& scene) :
-    RadiographyLayer(scene)
-  {
-
-  }
-
-  void RadiographyDicomLayer::SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset)
-  {
-    converter_.reset(new Deprecated::DicomFrameConverter);
-    converter_->ReadParameters(dataset);
-    ApplyConverter();
-
-    std::string tmp;
-    Vector pixelSpacing;
-
-    if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) &&
-        LinearAlgebra::ParseVector(pixelSpacing, tmp) &&
-        pixelSpacing.size() == 2)
-    {
-      SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]);
-    }
-
-    OrthancPlugins::DicomDatasetReader reader(dataset);
-
-    unsigned int width, height;
-    if (!reader.GetUnsignedIntegerValue(width, ConvertTag(Orthanc::DICOM_TAG_COLUMNS)) ||
-        !reader.GetUnsignedIntegerValue(height, ConvertTag(Orthanc::DICOM_TAG_ROWS)))
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-    }
-    else
-    {
-      SetSize(width, height);
-    }
-
-    if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION)))
-    {
-      if (tmp == "MONOCHROME1")
-      {
-        SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome1);
-      }
-      else if (tmp == "MONOCHROME2")
-      {
-        SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome2);
-      }
-    }
-  }
-
-  void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image)   // Takes ownership
-  {
-    std::unique_ptr<Orthanc::ImageAccessor> raii(image);
-
-    if (image == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-    }
-
-    SetSize(image->GetWidth(), image->GetHeight());
-
-#if __cplusplus < 201103L
-      source_.reset(raii.release());
-#else
-      source_ = std::move(raii);
-#endif
-
-    ApplyConverter();
-
-    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
-  }
-
-  void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY, bool emitLayerEditedEvent)   // Takes ownership
-  {
-    std::unique_ptr<Orthanc::ImageAccessor> raii(image);
-
-    if (image == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-    }
-
-    SetSize(image->GetWidth(), image->GetHeight(), false);
-
-#if __cplusplus < 201103L
-    source_.reset(raii.release());
-#else
-    source_ = std::move(raii);
-#endif
-
-    ApplyConverter();
-
-    SetPixelSpacing(newPixelSpacingX, newPixelSpacingY, false);
-
-    if (emitLayerEditedEvent)
-    {
-      BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
-    }
-  }
-
-
-  void RadiographyDicomLayer::SetDicomFrameConverter(Deprecated::DicomFrameConverter* converter)
-  {
-    converter_.reset(converter);
-  }
-
-  void RadiographyDicomLayer::Render(Orthanc::ImageAccessor& buffer,
-                                     const AffineTransform2D& viewTransform,
-                                     ImageInterpolation interpolation,
-                                     float windowCenter,
-                                     float windowWidth,
-                                     bool applyWindowing) const
-  {
-    if (converted_.get() != NULL)
-    {
-      if (converted_->GetFormat() != Orthanc::PixelFormat_Float32)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-
-      unsigned int cropX, cropY, cropWidth, cropHeight;
-      GetCrop(cropX, cropY, cropWidth, cropHeight);
-
-      AffineTransform2D t = AffineTransform2D::Combine(
-            viewTransform, GetTransform(),
-            AffineTransform2D::CreateOffset(cropX, cropY));
-
-      Orthanc::ImageAccessor cropped;
-      converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
-
-      unsigned int x1, y1, x2, y2;
-      if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2,
-                                                      t.GetHomogeneousMatrix(),
-                                                      cropped.GetWidth(),
-                                                      cropped.GetHeight(),
-                                                      buffer.GetWidth(),
-                                                      buffer.GetHeight()))
-      {
-        return;  // layer is outside the buffer
-      }
-
-      t.Apply(buffer, cropped, interpolation, false);
-
-      if (applyWindowing)
-      {
-        // apply windowing but stay in the range [0.0, 65535.0]
-        float w0 = windowCenter - windowWidth / 2.0f;
-        float w1 = windowCenter + windowWidth / 2.0f;
-
-        if (windowWidth >= 0.001f)  // Avoid division by zero at (*)
-        {
-          float scaling = 1.0f / (w1 - w0) * 65535.0f;
-          for (unsigned int y = y1; y <= y2; y++)
-          {
-            float* p = reinterpret_cast<float*>(buffer.GetRow(y)) + x1;
-
-            for (unsigned int x = x1; x <= x2; x++, p++)
-            {
-              if (*p >= w1)
-              {
-                *p = 65535.0;
-              }
-              else if (*p <= w0)
-              {
-                *p = 0;
-              }
-              else
-              {
-                // https://en.wikipedia.org/wiki/Linear_interpolation
-                *p = scaling * (*p - w0);  // (*)
-              }
-            }
-          }
-        }
-      }
-
-    }
-  }
-
-
-  bool RadiographyDicomLayer::GetDefaultWindowing(float& center,
-                                                  float& width) const
-  {
-    if (converter_.get() != NULL &&
-        converter_->HasDefaultWindow())
-    {
-      center = static_cast<float>(converter_->GetDefaultWindowCenter());
-      width = static_cast<float>(converter_->GetDefaultWindowWidth());
-      return true;
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-
-  bool RadiographyDicomLayer::GetRange(float& minValue,
-                                       float& maxValue) const
-  {
-    if (converted_.get() != NULL)
-    {
-      if (converted_->GetFormat() != Orthanc::PixelFormat_Float32)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-
-      Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, *converted_);
-      return true;
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-}
--- a/Framework/Radiography/RadiographyDicomLayer.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/**
- * 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 "../Deprecated/Toolbox/DicomFrameConverter.h"
-#include "RadiographyLayer.h"
-
-#include <Plugins/Samples/Common/FullOrthancDataset.h>
-
-namespace OrthancStone
-{
-  class RadiographyScene;
-
-  class RadiographyDicomLayer : public RadiographyLayer
-  {
-  private:
-    std::unique_ptr<Orthanc::ImageAccessor>  source_;  // Content of PixelData
-    std::unique_ptr<Deprecated::DicomFrameConverter>     converter_;
-    std::unique_ptr<Orthanc::ImageAccessor>  converted_;  // Float32
-    std::string                            instanceId_;
-    unsigned int                           frame_;
-
-    void ApplyConverter();
-
-  public:
-    RadiographyDicomLayer(const RadiographyScene& scene);
-
-    void SetInstance(const std::string& instanceId, unsigned int frame)
-    {
-      instanceId_ = instanceId;
-      frame_ = frame;
-    }
-
-    std::string GetInstanceId() const
-    {
-      return instanceId_;
-    }
-
-    unsigned int GetFrame() const
-    {
-      return frame_;
-    }
-
-    virtual size_t GetApproximateMemoryUsage() const
-    {
-      size_t size = 0;
-      if (source_.get() != NULL)
-      {
-        size += source_->GetPitch() * source_->GetHeight();
-      }
-      if (converted_.get() != NULL)
-      {
-        size += converted_->GetPitch() * converted_->GetHeight();
-      }
-
-      return size;
-    }
-
-
-    void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset);
-
-    void SetSourceImage(Orthanc::ImageAccessor* image);   // Takes ownership
-
-    void SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY, bool emitLayerEditedEvent = true);   // Takes ownership
-
-    const Orthanc::ImageAccessor* GetSourceImage() const {return source_.get();}  // currently need this access to serialize scene in plain old data to send to a WASM worker
-
-    const Deprecated::DicomFrameConverter& GetDicomFrameConverter() const {return *converter_;} // currently need this access to serialize scene in plain old data to send to a WASM worker
-    
-     // Takes ownership
-    void SetDicomFrameConverter(Deprecated::DicomFrameConverter* converter);
-
-    virtual void Render(Orthanc::ImageAccessor& buffer,
-                        const AffineTransform2D& viewTransform,
-                        ImageInterpolation interpolation,
-                        float windowCenter,
-                        float windowWidth,
-                        bool applyWindowing) const;
-
-    virtual bool GetDefaultWindowing(float& center,
-                                     float& width) const;
-
-    virtual bool GetRange(float& minValue,
-                          float& maxValue) const;
-  };
-}
--- a/Framework/Radiography/RadiographyLayer.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,402 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographyLayer.h"
-
-#include <Core/OrthancException.h>
-
-
-namespace OrthancStone
-{
-  static double Square(double x)
-  {
-    return x * x;
-  }
-
-
-  RadiographyLayer::Geometry::Geometry() :
-    hasCrop_(false),
-    flipVertical_(false),
-    flipHorizontal_(false),
-    panX_(0),
-    panY_(0),
-    angle_(0),
-    resizeable_(false),
-    pixelSpacingX_(1),
-    pixelSpacingY_(1)
-  {
-
-  }
-
-  void RadiographyLayer::Geometry::GetCrop(unsigned int &x, unsigned int &y, unsigned int &width, unsigned int &height) const
-  {
-    if (!hasCrop_)
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);  // you should probably use RadiographyLayer::GetCrop() or at least call HasCrop() before
-
-    x = cropX_;
-    y = cropY_;
-    width = cropWidth_;
-    height = cropHeight_;
-  }
-
-  void RadiographyLayer::UpdateTransform()
-  {
-    // important to update transform_ before getting the center to use the right scaling !!!
-    transform_ = AffineTransform2D::CreateScaling(geometry_.GetScalingX(), geometry_.GetScalingY());
-
-    double centerX, centerY;
-    GetCenter(centerX, centerY);
-
-    transform_ = AffineTransform2D::Combine(
-          AffineTransform2D::CreateOffset(geometry_.GetPanX(), geometry_.GetPanY()),
-          AffineTransform2D::CreateRotation(geometry_.GetAngle(), centerX, centerY),
-          transform_);
-
-    transformInverse_ = AffineTransform2D::Invert(transform_);
-  }
-
-
-  void RadiographyLayer::AddToExtent(Extent2D& extent,
-                                     double x,
-                                     double y) const
-  {
-    GetTransform().Apply(x, y);
-    extent.AddPoint(x, y);
-  }
-
-  bool RadiographyLayer::Contains(double x,
-                                  double y) const
-  {
-    GetTransformInverse().Apply(x, y);
-
-    unsigned int cropX, cropY, cropWidth, cropHeight;
-    GetCrop(cropX, cropY, cropWidth, cropHeight);
-
-    return (x >= cropX && x <= cropX + cropWidth &&
-            y >= cropY && y <= cropY + cropHeight);
-  }
-
-
-  void RadiographyLayer::DrawBorders(CairoContext& context,
-                                     double zoom)
-  {
-    if (GetControlPointCount() < 3 )
-      return;
-
-    cairo_t* cr = context.GetObject();
-    cairo_set_line_width(cr, 2.0 / zoom);
-
-    ControlPoint cp;
-    GetControlPoint(cp, 0);
-    cairo_move_to(cr, cp.x, cp.y);
-
-    for (size_t i = 0; i < GetControlPointCount(); i++)
-    {
-      GetControlPoint(cp, i);
-      cairo_line_to(cr, cp.x, cp.y);
-    }
-
-    cairo_close_path(cr);
-    cairo_stroke(cr);
-  }
-
-
-  RadiographyLayer::RadiographyLayer(const RadiographyScene& scene) :
-    index_(0),
-    hasSize_(false),
-    width_(0),
-    height_(0),
-    prefferedPhotometricDisplayMode_(RadiographyPhotometricDisplayMode_Default),
-    scene_(scene)
-  {
-    UpdateTransform();
-  }
-
-  void RadiographyLayer::ResetCrop()
-  {
-    geometry_.ResetCrop();
-    UpdateTransform();
-  }
-
-  void RadiographyLayer::SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode  prefferedPhotometricDisplayMode)
-  {
-    prefferedPhotometricDisplayMode_ = prefferedPhotometricDisplayMode;
-
-    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
-  }
-
-  void RadiographyLayer::SetCrop(unsigned int x,
-                                 unsigned int y,
-                                 unsigned int width,
-                                 unsigned int height)
-  {
-    if (!hasSize_)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-
-    if (x + width > width_ ||
-        y + height > height_)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    geometry_.SetCrop(x, y, width, height);
-    UpdateTransform();
-
-    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
-  }
-
-  void RadiographyLayer::SetGeometry(const Geometry& geometry)
-  {
-    geometry_ = geometry;
-
-    if (hasSize_)
-    {
-      UpdateTransform();
-    }
-
-    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
-  }
-
-
-  void RadiographyLayer::GetCrop(unsigned int& x,
-                                 unsigned int& y,
-                                 unsigned int& width,
-                                 unsigned int& height) const
-  {
-    if (GetGeometry().HasCrop())
-    {
-      GetGeometry().GetCrop(x, y, width, height);
-    }
-    else
-    {
-      x = 0;
-      y = 0;
-      width = width_;
-      height = height_;
-    }
-  }
-
-  
-  void RadiographyLayer::SetAngle(double angle)
-  {
-    geometry_.SetAngle(angle);
-    UpdateTransform();
-
-    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
-  }
-
-  void RadiographyLayer::SetFlipVertical(bool flip)
-  {
-    geometry_.SetFlipVertical(flip);
-    UpdateTransform();
-
-    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
-  }
-
-  void RadiographyLayer::SetFlipHorizontal(bool flip)
-  {
-    geometry_.SetFlipHorizontal(flip);
-    UpdateTransform();
-
-    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
-  }
-
-  void RadiographyLayer::SetSize(unsigned int width,
-                                 unsigned int height,
-                                 bool emitLayerEditedEvent)
-  {
-    hasSize_ = true;
-    width_ = width;
-    height_ = height;
-
-    UpdateTransform();
-
-    if (emitLayerEditedEvent)
-    {
-      BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
-    }
-  }
-
-  Extent2D RadiographyLayer::GetSceneExtent(bool /*minimal*/) const
-  {
-    Extent2D extent;
-
-    unsigned int x, y, width, height;
-    GetCrop(x, y, width, height);
-
-    double dx = static_cast<double>(x);
-    double dy = static_cast<double>(y);
-    double dwidth = static_cast<double>(width);
-    double dheight = static_cast<double>(height);
-
-    // AddToExtent transforms the coordinates from image to scene
-    AddToExtent(extent, dx, dy);
-    AddToExtent(extent, dx + dwidth, dy);
-    AddToExtent(extent, dx, dy + dheight);
-    AddToExtent(extent, dx + dwidth, dy + dheight);
-
-    return extent;
-  }
-
-
-  bool RadiographyLayer::GetPixel(unsigned int& imageX,
-                                  unsigned int& imageY,
-                                  double sceneX,
-                                  double sceneY) const
-  {
-    if (width_ == 0 ||
-        height_ == 0)
-    {
-      return false;
-    }
-    else
-    {
-      GetTransformInverse().Apply(sceneX, sceneY);
-
-      int x = static_cast<int>(std::floor(sceneX));
-      int y = static_cast<int>(std::floor(sceneY));
-
-      if (x < 0)
-      {
-        imageX = 0;
-      }
-      else if (x >= static_cast<int>(width_))
-      {
-        imageX = width_;
-      }
-      else
-      {
-        imageX = static_cast<unsigned int>(x);
-      }
-
-      if (y < 0)
-      {
-        imageY = 0;
-      }
-      else if (y >= static_cast<int>(height_))
-      {
-        imageY = height_;
-      }
-      else
-      {
-        imageY = static_cast<unsigned int>(y);
-      }
-
-      return true;
-    }
-  }
-
-
-  void RadiographyLayer::SetPan(double x,
-                                double y)
-  {
-    geometry_.SetPan(x, y);
-    UpdateTransform();
-    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
-  }
-
-
-  void RadiographyLayer::SetPixelSpacing(double x,
-                                         double y,
-                                         bool emitLayerEditedEvent)
-  {
-    geometry_.SetPixelSpacing(x, y);
-    UpdateTransform();
-    if (emitLayerEditedEvent)
-    {
-      BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
-    }
-  }
-
-
-  void RadiographyLayer::GetCenter(double& centerX,
-                                   double& centerY) const
-  {
-    centerX = static_cast<double>(width_) / 2.0;
-    centerY = static_cast<double>(height_) / 2.0;
-    GetTransform().Apply(centerX, centerY);
-  }
-
-
-
-  size_t RadiographyLayer::GetControlPointCount() const {return 4;}
-
-  void RadiographyLayer::GetControlPoint(ControlPoint& cpScene /* out in scene coordinates */,
-                                         size_t index) const
-  {
-    unsigned int cropX, cropY, cropWidth, cropHeight;
-    GetCrop(cropX, cropY, cropWidth, cropHeight);
-
-    ControlPoint cp;
-    switch (index)
-    {
-      case RadiographyControlPointType_TopLeftCorner:
-        cp = ControlPoint(cropX, cropY, RadiographyControlPointType_TopLeftCorner);
-        break;
-
-      case RadiographyControlPointType_TopRightCorner:
-        cp = ControlPoint(cropX + cropWidth, cropY, RadiographyControlPointType_TopRightCorner);
-        break;
-
-      case RadiographyControlPointType_BottomLeftCorner:
-        cp = ControlPoint(cropX, cropY + cropHeight, RadiographyControlPointType_BottomLeftCorner);
-        break;
-
-      case RadiographyControlPointType_BottomRightCorner:
-        cp = ControlPoint(cropX + cropWidth, cropY + cropHeight, RadiographyControlPointType_BottomRightCorner);
-        break;
-
-    default:
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    // transforms image coordinates into scene coordinates
-    GetTransform().Apply(cp.x, cp.y);
-    cpScene = cp;
-  }
-
-  bool RadiographyLayer::LookupControlPoint(ControlPoint& cpScene /* out */,
-                                            double x,
-                                            double y,
-                                            double zoom,
-                                            double viewportDistance) const
-  {
-    double threshold = Square(viewportDistance / zoom);
-
-    for (size_t i = 0; i < GetControlPointCount(); i++)
-    {
-      ControlPoint cp;
-      GetControlPoint(cp, i);
-
-      double d = Square(cp.x - x) + Square(cp.y - y);
-
-      if (d <= threshold)
-      {
-        cpScene = cp;
-        return true;
-      }
-    }
-
-    return false;
-  }
-}
--- a/Framework/Radiography/RadiographyLayer.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,395 +0,0 @@
-/**
- * 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 <algorithm>
-
-#include "../Toolbox/AffineTransform2D.h"
-#include "../Toolbox/Extent2D.h"
-#include "../Wrappers/CairoContext.h"
-#include "../Messages/IMessage.h"
-#include "../Messages/IObservable.h"
-
-namespace OrthancStone
-{
-  class RadiographyScene;
-
-  enum RadiographyControlPointType
-  {
-    RadiographyControlPointType_TopLeftCorner = 0,
-    RadiographyControlPointType_TopRightCorner = 1,
-    RadiographyControlPointType_BottomRightCorner = 2,
-    RadiographyControlPointType_BottomLeftCorner = 3
-  };
-
-  enum RadiographyPhotometricDisplayMode
-  {
-    RadiographyPhotometricDisplayMode_Default,
-
-    RadiographyPhotometricDisplayMode_Monochrome1,
-    RadiographyPhotometricDisplayMode_Monochrome2
-  };
-
-  
-  struct ControlPoint
-  {
-    double x;
-    double y;
-    size_t index;
-
-    ControlPoint(double x, double y, size_t index)
-      : x(x),
-        y(y),
-        index(index)
-    {}
-
-    ControlPoint()
-      : x(0),
-        y(0),
-        index(std::numeric_limits<size_t>::max())
-    {}
-  };
-
-  class RadiographyLayer : public IObservable
-  {
-    friend class RadiographyScene;
-
-  public:
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, LayerEditedMessage, RadiographyLayer);
-
-    class Geometry
-    {
-      bool               hasCrop_;
-      unsigned int       cropX_;
-      unsigned int       cropY_;
-      unsigned int       cropWidth_;
-      unsigned int       cropHeight_;
-      bool               flipVertical_;
-      bool               flipHorizontal_;
-      double             panX_;
-      double             panY_;
-      double             angle_;
-      bool               resizeable_;
-      double             pixelSpacingX_;
-      double             pixelSpacingY_;
-
-    public:
-      Geometry();
-
-      void ResetCrop()
-      {
-        hasCrop_ = false;
-      }
-
-      void SetCrop(unsigned int x,
-                   unsigned int y,
-                   unsigned int width,
-                   unsigned int height)
-      {
-        hasCrop_ = true;
-        cropX_ = x;
-        cropY_ = y;
-        cropWidth_ = width;
-        cropHeight_ = height;
-      }
-
-      bool HasCrop() const
-      {
-        return hasCrop_;
-      }
-
-      void GetCrop(unsigned int& x,
-                   unsigned int& y,
-                   unsigned int& width,
-                   unsigned int& height) const;
-
-      void SetAngle(double angle)
-      {
-        angle_ = angle;
-      }
-
-      double GetAngle() const
-      {
-        return angle_;
-      }
-
-      void SetPan(double x,
-                  double y)
-      {
-        panX_ = x;
-        panY_ = y;
-      }
-
-      double GetPanX() const
-      {
-        return panX_;
-      }
-
-      double GetPanY() const
-      {
-        return panY_;
-      }
-
-      bool IsResizeable() const
-      {
-        return resizeable_;
-      }
-
-      void SetResizeable(bool resizeable)
-      {
-        resizeable_ = resizeable;
-      }
-
-      void SetPixelSpacing(double x,
-                           double y)
-      {
-        pixelSpacingX_ = x;
-        pixelSpacingY_ = y;
-      }
-
-      double GetPixelSpacingX() const
-      {
-        return pixelSpacingX_;
-      }
-
-      double GetPixelSpacingY() const
-      {
-        return pixelSpacingY_;
-      }
-
-      void SetFlipVertical(bool flip) //  mirrors image around an horizontal axis (note: flip is applied before the rotation !)
-      {
-        flipVertical_ = flip;
-      }
-
-      void SetFlipHorizontal(bool flip) //  mirrors image around a vertical axis (note: flip is applied before the rotation !)
-      {
-        flipHorizontal_ = flip;
-      }
-
-      bool GetFlipVertical() const
-      {
-        return flipVertical_;
-      }
-
-      bool GetFlipHorizontal() const
-      {
-        return flipHorizontal_;
-      }
-
-      double GetScalingX() const
-      {
-        return (flipHorizontal_ ? - pixelSpacingX_: pixelSpacingX_);
-      }
-
-      double GetScalingY() const
-      {
-        return (flipVertical_ ? - pixelSpacingY_: pixelSpacingY_);
-      }
-    };
-
-  private:
-    size_t             index_;
-    bool               hasSize_;
-    unsigned int       width_;
-    unsigned int       height_;
-    AffineTransform2D  transform_;
-    AffineTransform2D  transformInverse_;
-    Geometry           geometry_;
-    RadiographyPhotometricDisplayMode  prefferedPhotometricDisplayMode_;
-    const RadiographyScene&   scene_;
-
-  protected:
-    void SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode  prefferedPhotometricDisplayMode);
-
-  private:
-    void UpdateTransform();
-
-    void AddToExtent(Extent2D& extent,
-                     double x,
-                     double y) const;
-
-    void SetIndex(size_t index)
-    {
-      index_ = index;
-    }
-
-    bool Contains(double x,
-                  double y) const;
-
-    void DrawBorders(CairoContext& context,
-                     double zoom);
-
-  public:
-    RadiographyLayer(const RadiographyScene& scene);
-
-    virtual ~RadiographyLayer()
-    {
-    }
-
-    virtual const AffineTransform2D& GetTransform() const
-    {
-      return transform_;
-    }
-
-    virtual const AffineTransform2D& GetTransformInverse() const
-    {
-      return transformInverse_;
-    }
-
-    size_t GetIndex() const
-    {
-      return index_;
-    }
-
-    const RadiographyScene& GetScene() const
-    {
-      return scene_;
-    }
-
-    const Geometry& GetGeometry() const
-    {
-      return geometry_;
-    }
-
-    void SetGeometry(const Geometry& geometry);
-
-    void ResetCrop();
-
-    void SetCrop(unsigned int x,       // those are pixel coordinates/size
-                 unsigned int y,
-                 unsigned int width,
-                 unsigned int height);
-
-    void SetCrop(const Extent2D& sceneExtent)
-    {
-      Extent2D imageCrop;
-
-      {
-        double x = sceneExtent.GetX1();
-        double y = sceneExtent.GetY1();
-        GetTransformInverse().Apply(x, y);
-        imageCrop.AddPoint(x, y);
-      }
-
-      {
-        double x = sceneExtent.GetX2();
-        double y = sceneExtent.GetY2();
-        GetTransformInverse().Apply(x, y);
-        imageCrop.AddPoint(x, y);
-      }
-
-      SetCrop(static_cast<unsigned int>(std::max(0.0, std::floor(imageCrop.GetX1()))),
-              static_cast<unsigned int>(std::max(0.0, std::floor(imageCrop.GetY1()))),
-              std::min(width_, static_cast<unsigned int>(std::ceil(imageCrop.GetWidth()))),
-              std::min(height_, static_cast<unsigned int>(std::ceil(imageCrop.GetHeight())))
-              );
-    }
-
-
-    void GetCrop(unsigned int& x,
-                 unsigned int& y,
-                 unsigned int& width,
-                 unsigned int& height) const;
-
-    void SetAngle(double angle);
-
-    void SetPan(double x,
-                double y);
-
-    void SetFlipVertical(bool flip); //  mirrors image around an horizontal axis (note: flip is applied before the rotation !)
-
-    void SetFlipHorizontal(bool flip); //  mirrors image around a vertical axis (note: flip is applied before the rotation !)
-
-    void SetResizeable(bool resizeable)
-    {
-      geometry_.SetResizeable(resizeable);
-    }
-
-    void SetSize(unsigned int width,
-                 unsigned int height,
-                 bool emitLayerEditedEvent = true);
-
-    bool HasSize() const
-    {
-      return hasSize_;
-    }
-
-    unsigned int GetWidth() const
-    {
-      return width_;
-    }
-
-    unsigned int GetHeight() const
-    {
-      return height_;
-    }
-
-    virtual Extent2D GetSceneExtent(bool minimal) const;
-
-    virtual bool GetPixel(unsigned int& imageX,
-                          unsigned int& imageY,
-                          double sceneX,
-                          double sceneY) const;
-
-    void SetPixelSpacing(double x,
-                         double y,
-                         bool emitLayerEditedEvent = true);
-
-    void GetCenter(double& centerX,
-                   double& centerY) const;
-
-    virtual void GetControlPoint(ControlPoint& cpScene /* out in scene coordinates */,
-                                 size_t index) const;
-
-    virtual size_t GetControlPointCount() const;
-
-    bool LookupControlPoint(ControlPoint& cpScene /* out */,
-                            double x,
-                            double y,
-                            double zoom,
-                            double viewportDistance) const;
-
-    virtual bool GetDefaultWindowing(float& center,
-                                     float& width) const = 0;
-
-    RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const
-    {
-      return prefferedPhotometricDisplayMode_;
-    }
-
-    virtual void Render(Orthanc::ImageAccessor& buffer,
-                        const AffineTransform2D& viewTransform,
-                        ImageInterpolation interpolation,
-                        float windowCenter,
-                        float windowWidth,
-                        bool applyWindowing) const = 0;
-
-    virtual bool GetRange(float& minValue,
-                          float& maxValue) const = 0;
-
-    virtual size_t GetApproximateMemoryUsage() const // this is used to limit the number of scenes loaded in RAM when resources are limited (we actually only count the size used by the images, not the C structs)
-    {
-      return 0;
-    }
-  };
-}
--- a/Framework/Radiography/RadiographyLayerCropTracker.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographyLayerCropTracker.h"
-
-#include "RadiographySceneCommand.h"
-
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  class RadiographyLayerCropTracker::UndoRedoCommand : public RadiographySceneCommand
-  {
-  private:
-    unsigned int  sourceCropX_;
-    unsigned int  sourceCropY_;
-    unsigned int  sourceCropWidth_;
-    unsigned int  sourceCropHeight_;
-    unsigned int  targetCropX_;
-    unsigned int  targetCropY_;
-    unsigned int  targetCropWidth_;
-    unsigned int  targetCropHeight_;
-
-  protected:
-    virtual void UndoInternal(RadiographyLayer& layer) const
-    {
-      layer.SetCrop(sourceCropX_, sourceCropY_, sourceCropWidth_, sourceCropHeight_);
-    }
-
-    virtual void RedoInternal(RadiographyLayer& layer) const
-    {
-      layer.SetCrop(targetCropX_, targetCropY_, targetCropWidth_, targetCropHeight_);
-    }
-
-  public:
-    UndoRedoCommand(const RadiographyLayerCropTracker& tracker) :
-      RadiographySceneCommand(tracker.accessor_),
-      sourceCropX_(tracker.cropX_),
-      sourceCropY_(tracker.cropY_),
-      sourceCropWidth_(tracker.cropWidth_),
-      sourceCropHeight_(tracker.cropHeight_)
-    {
-      tracker.accessor_.GetLayer().GetCrop(targetCropX_, targetCropY_,
-                                           targetCropWidth_, targetCropHeight_);
-    }
-  };
-
-
-  RadiographyLayerCropTracker::RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack,
-                                                           RadiographyScene& scene,
-                                                           const Deprecated::ViewportGeometry& view,
-                                                           size_t layer,
-                                                           const ControlPoint& startControlPoint) :
-    undoRedoStack_(undoRedoStack),
-    accessor_(scene, layer),
-    startControlPoint_(startControlPoint)
-  {
-    if (accessor_.IsValid())
-    {
-      accessor_.GetLayer().GetCrop(cropX_, cropY_, cropWidth_, cropHeight_);
-    }
-  }
-
-
-  void RadiographyLayerCropTracker::Render(CairoContext& context,
-                                           double zoom)
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-  }
-
-
-  void RadiographyLayerCropTracker::MouseUp()
-  {
-    if (accessor_.IsValid())
-    {
-      undoRedoStack_.Add(new UndoRedoCommand(*this));
-    }
-  }
-
-  
-  void RadiographyLayerCropTracker::MouseMove(int displayX,
-                                              int displayY,
-                                              double sceneX,
-                                              double sceneY,
-                                              const std::vector<Deprecated::Touch>& displayTouches,
-                                              const std::vector<Deprecated::Touch>& sceneTouches)
-  {
-    if (accessor_.IsValid())
-    {
-      unsigned int x, y;
-        
-      RadiographyLayer& layer = accessor_.GetLayer();
-      if (layer.GetPixel(x, y, sceneX, sceneY))
-      {
-        unsigned int targetX, targetWidth;
-
-        if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner ||
-            startControlPoint_.index == RadiographyControlPointType_BottomLeftCorner)
-        {
-          targetX = std::min(x, cropX_ + cropWidth_);
-          targetWidth = cropX_ + cropWidth_ - targetX;
-        }
-        else
-        {
-          targetX = cropX_;
-          targetWidth = std::max(x, cropX_) - cropX_;
-        }
-
-        unsigned int targetY, targetHeight;
-
-        if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner ||
-            startControlPoint_.index == RadiographyControlPointType_TopRightCorner)
-        {
-          targetY = std::min(y, cropY_ + cropHeight_);
-          targetHeight = cropY_ + cropHeight_ - targetY;
-        }
-        else
-        {
-          targetY = cropY_;
-          targetHeight = std::max(y, cropY_) - cropY_;
-        }
-
-        layer.SetCrop(targetX, targetY, targetWidth, targetHeight);
-      }
-    }
-  }
-}
--- a/Framework/Radiography/RadiographyLayerCropTracker.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/**
- * 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 "../Toolbox/UndoRedoStack.h"
-#include "../Deprecated/Toolbox/ViewportGeometry.h"
-#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
-#include "RadiographyScene.h"
-
-namespace OrthancStone
-{
-  class RadiographyLayerCropTracker : public Deprecated::IWorldSceneMouseTracker
-  {
-  private:
-    class UndoRedoCommand;
-
-    UndoRedoStack&                   undoRedoStack_;
-    RadiographyScene::LayerAccessor  accessor_;
-    ControlPoint                     startControlPoint_;
-    unsigned int                     cropX_;
-    unsigned int                     cropY_;
-    unsigned int                     cropWidth_;
-    unsigned int                     cropHeight_;
-
-  public:
-    RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack,
-                                RadiographyScene& scene,
-                                const Deprecated::ViewportGeometry& view,
-                                size_t layer,
-                                const ControlPoint& startControlPoint);
-
-    virtual bool HasRender() const
-    {
-      return false;
-    }
-
-    virtual void Render(CairoContext& context,
-                        double zoom);
-
-    virtual void MouseUp();
-
-    virtual void MouseMove(int displayX,
-                           int displayY,
-                           double sceneX,
-                           double sceneY,
-                           const std::vector<Deprecated::Touch>& displayTouches,
-                           const std::vector<Deprecated::Touch>& sceneTouches);
-  };
-}
--- a/Framework/Radiography/RadiographyLayerMaskTracker.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographyLayerMaskTracker.h"
-#include "RadiographyMaskLayer.h"
-
-#include "RadiographySceneCommand.h"
-
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  class RadiographyLayerMaskTracker::UndoRedoCommand : public RadiographySceneCommand
-  {
-  private:
-    ControlPoint sourceSceneCp_;
-    ControlPoint targetSceneCp_;
-
-  protected:
-    virtual void UndoInternal(RadiographyLayer& layer) const
-    {
-      RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&layer);
-      if (maskLayer == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-      unsigned int ix, iy; // image coordinates
-      if (maskLayer->GetPixel(ix, iy, sourceSceneCp_.x, sourceSceneCp_.y))
-      {
-        maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), sourceSceneCp_.index);
-      }
-    }
-
-    virtual void RedoInternal(RadiographyLayer& layer) const
-    {
-      RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&layer);
-      if (maskLayer == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-      unsigned int ix, iy; // image coordinates
-      if (maskLayer->GetPixel(ix, iy, targetSceneCp_.x, targetSceneCp_.y))
-      {
-        maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), targetSceneCp_.index);
-      }
-    }
-
-  public:
-    UndoRedoCommand(const RadiographyLayerMaskTracker& tracker) :
-      RadiographySceneCommand(tracker.accessor_),
-      sourceSceneCp_(tracker.startSceneCp_),
-      targetSceneCp_(tracker.endSceneCp_)
-    {
-      RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&(tracker.accessor_.GetLayer()));
-      if (maskLayer == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-      unsigned int ix, iy; // image coordinates
-      if (maskLayer->GetPixel(ix, iy, targetSceneCp_.x, targetSceneCp_.y))
-      {
-        maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), targetSceneCp_.index);
-      }
-    }
-  };
-
-
-  RadiographyLayerMaskTracker::RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack,
-                                                           RadiographyScene& scene,
-                                                           const Deprecated::ViewportGeometry& view,
-                                                           size_t layer,
-                                                           const ControlPoint& startSceneControlPoint) :
-    undoRedoStack_(undoRedoStack),
-    accessor_(scene, layer),
-    startSceneCp_(startSceneControlPoint),
-    endSceneCp_(startSceneControlPoint)
-  {
-  }
-
-
-  void RadiographyLayerMaskTracker::Render(CairoContext& context,
-                                           double zoom)
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-  }
-
-
-  void RadiographyLayerMaskTracker::MouseUp()
-  {
-    if (accessor_.IsValid() && startSceneCp_.x != endSceneCp_.x && startSceneCp_.y != endSceneCp_.y)
-    {
-      undoRedoStack_.Add(new UndoRedoCommand(*this));
-    }
-  }
-
-  
-  void RadiographyLayerMaskTracker::MouseMove(int displayX,
-                                              int displayY,
-                                              double sceneX,
-                                              double sceneY,
-                                              const std::vector<Deprecated::Touch>& displayTouches,
-                                              const std::vector<Deprecated::Touch>& sceneTouches)
-  {
-    if (accessor_.IsValid())
-    {
-      unsigned int ix, iy; // image coordinates
-
-      RadiographyLayer& layer = accessor_.GetLayer();
-      if (layer.GetPixel(ix, iy, sceneX, sceneY))
-      {
-        endSceneCp_ = ControlPoint(sceneX, sceneY, startSceneCp_.index);
-
-        RadiographyMaskLayer* maskLayer = dynamic_cast<RadiographyMaskLayer*>(&layer);
-        if (maskLayer == NULL)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-        }
-        maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), startSceneCp_.index);
-      }
-    }
-  }
-}
--- a/Framework/Radiography/RadiographyLayerMaskTracker.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/**
- * 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 "../Toolbox/UndoRedoStack.h"
-#include "../Deprecated/Toolbox/ViewportGeometry.h"
-#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
-#include "RadiographyScene.h"
-
-namespace OrthancStone
-{
-  class RadiographyLayerMaskTracker : public Deprecated::IWorldSceneMouseTracker
-  {
-  private:
-    class UndoRedoCommand;
-
-    UndoRedoStack&                   undoRedoStack_;
-    RadiographyScene::LayerAccessor  accessor_;
-    ControlPoint                     startSceneCp_;
-    ControlPoint                     endSceneCp_;
-
-  public:
-    RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack,
-                                RadiographyScene& scene,
-                                const Deprecated::ViewportGeometry& view,
-                                size_t layer,
-                                const ControlPoint& startSceneControlPoint);
-
-    virtual bool HasRender() const
-    {
-      return false;
-    }
-
-    virtual void Render(CairoContext& context,
-                        double zoom);
-
-    virtual void MouseUp();
-
-    virtual void MouseMove(int displayX,
-                           int displayY,
-                           double sceneX,
-                           double sceneY,
-                           const std::vector<Deprecated::Touch>& displayTouches,
-                           const std::vector<Deprecated::Touch>& sceneTouches);
-  };
-}
--- a/Framework/Radiography/RadiographyLayerMoveTracker.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographyLayerMoveTracker.h"
-
-#include "RadiographySceneCommand.h"
-
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  class RadiographyLayerMoveTracker::UndoRedoCommand : public RadiographySceneCommand
-  {
-  private:
-    double  sourceX_;
-    double  sourceY_;
-    double  targetX_;
-    double  targetY_;
-
-  protected:
-    virtual void UndoInternal(RadiographyLayer& layer) const
-    {
-      layer.SetPan(sourceX_, sourceY_);
-    }
-
-    virtual void RedoInternal(RadiographyLayer& layer) const
-    {
-      layer.SetPan(targetX_, targetY_);
-    }
-
-  public:
-    UndoRedoCommand(const RadiographyLayerMoveTracker& tracker) :
-      RadiographySceneCommand(tracker.accessor_),
-      sourceX_(tracker.panX_),
-      sourceY_(tracker.panY_),
-      targetX_(tracker.accessor_.GetLayer().GetGeometry().GetPanX()),
-      targetY_(tracker.accessor_.GetLayer().GetGeometry().GetPanY())
-    {
-    }
-  };
-
-
-  RadiographyLayerMoveTracker::RadiographyLayerMoveTracker(UndoRedoStack& undoRedoStack,
-                                                           RadiographyScene& scene,
-                                                           size_t layer,
-                                                           double x,
-                                                           double y,
-                                                           bool oneAxis) :
-    undoRedoStack_(undoRedoStack),
-    accessor_(scene, layer),
-    clickX_(x),
-    clickY_(y),
-    oneAxis_(oneAxis)
-  {
-    if (accessor_.IsValid())
-    {
-      panX_ = accessor_.GetLayer().GetGeometry().GetPanX();
-      panY_ = accessor_.GetLayer().GetGeometry().GetPanY();
-    }
-  }
-
-
-  void RadiographyLayerMoveTracker::Render(CairoContext& context,
-                                           double zoom)
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-  }
-
-  
-  void RadiographyLayerMoveTracker::MouseUp()
-  {
-    if (accessor_.IsValid())
-    {
-      undoRedoStack_.Add(new UndoRedoCommand(*this));
-    }
-  }
-
-  
-  void RadiographyLayerMoveTracker::MouseMove(int displayX,
-                                              int displayY,
-                                              double sceneX,
-                                              double sceneY,
-                                              const std::vector<Deprecated::Touch>& displayTouches,
-                                              const std::vector<Deprecated::Touch>& sceneTouches)
-  {
-    if (accessor_.IsValid())
-    {
-      double dx = sceneX - clickX_;
-      double dy = sceneY - clickY_;
-
-      if (oneAxis_)
-      {
-        if (fabs(dx) > fabs(dy))
-        {
-          accessor_.GetLayer().SetPan(dx + panX_, panY_);
-        }
-        else
-        {
-          accessor_.GetLayer().SetPan(panX_, dy + panY_);
-        }
-      }
-      else
-      {
-        accessor_.GetLayer().SetPan(dx + panX_, dy + panY_);
-      }
-    }
-  }
-}
--- a/Framework/Radiography/RadiographyLayerMoveTracker.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/**
- * 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 "../Toolbox/UndoRedoStack.h"
-#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
-#include "RadiographyScene.h"
-
-namespace OrthancStone
-{
-  class RadiographyLayerMoveTracker : public Deprecated::IWorldSceneMouseTracker
-  {
-  private:
-    class UndoRedoCommand;
-    
-    UndoRedoStack&                   undoRedoStack_;
-    RadiographyScene::LayerAccessor  accessor_;
-    double                           clickX_;
-    double                           clickY_;
-    double                           panX_;
-    double                           panY_;
-    bool                             oneAxis_;
-
-  public:
-    RadiographyLayerMoveTracker(UndoRedoStack& undoRedoStack,
-                                RadiographyScene& scene,
-                                size_t layer,
-                                double x,
-                                double y,
-                                bool oneAxis);
-
-    virtual bool HasRender() const
-    {
-      return false;
-    }
-
-    virtual void Render(CairoContext& context,
-                        double zoom);
-
-    virtual void MouseUp();
-
-    virtual void MouseMove(int displayX,
-                           int displayY,
-                           double sceneX,
-                           double sceneY,
-                           const std::vector<Deprecated::Touch>& displayTouches,
-                           const std::vector<Deprecated::Touch>& sceneTouches);
-  };
-}
--- a/Framework/Radiography/RadiographyLayerResizeTracker.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,188 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographyLayerResizeTracker.h"
-
-#include "RadiographySceneCommand.h"
-
-#include <Core/OrthancException.h>
-
-#include <boost/math/special_functions/round.hpp>
-
-
-namespace OrthancStone
-{
-  static double ComputeDistance(double x1,
-                                double y1,
-                                double x2,
-                                double y2)
-  {
-    double dx = x1 - x2;
-    double dy = y1 - y2;
-    return sqrt(dx * dx + dy * dy);
-  }
-      
-
-  class RadiographyLayerResizeTracker::UndoRedoCommand : public RadiographySceneCommand
-  {
-  private:
-    double   sourceSpacingX_;
-    double   sourceSpacingY_;
-    double   sourcePanX_;
-    double   sourcePanY_;
-    double   targetSpacingX_;
-    double   targetSpacingY_;
-    double   targetPanX_;
-    double   targetPanY_;
-
-  protected:
-    virtual void UndoInternal(RadiographyLayer& layer) const
-    {
-      layer.SetPixelSpacing(sourceSpacingX_, sourceSpacingY_);
-      layer.SetPan(sourcePanX_, sourcePanY_);
-    }
-
-    virtual void RedoInternal(RadiographyLayer& layer) const
-    {
-      layer.SetPixelSpacing(targetSpacingX_, targetSpacingY_);
-      layer.SetPan(targetPanX_, targetPanY_);
-    }
-
-  public:
-    UndoRedoCommand(const RadiographyLayerResizeTracker& tracker) :
-      RadiographySceneCommand(tracker.accessor_),
-      sourceSpacingX_(tracker.originalSpacingX_),
-      sourceSpacingY_(tracker.originalSpacingY_),
-      sourcePanX_(tracker.originalPanX_),
-      sourcePanY_(tracker.originalPanY_),
-      targetSpacingX_(tracker.accessor_.GetLayer().GetGeometry().GetPixelSpacingX()),
-      targetSpacingY_(tracker.accessor_.GetLayer().GetGeometry().GetPixelSpacingY()),
-      targetPanX_(tracker.accessor_.GetLayer().GetGeometry().GetPanX()),
-      targetPanY_(tracker.accessor_.GetLayer().GetGeometry().GetPanY())
-    {
-    }
-  };
-
-
-  RadiographyLayerResizeTracker::RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack,
-                                                               RadiographyScene& scene,
-                                                               size_t layer,
-                                                               const ControlPoint& startControlPoint,
-                                                               bool roundScaling) :
-    undoRedoStack_(undoRedoStack),
-    accessor_(scene, layer),
-    roundScaling_(roundScaling)
-  {
-    if (accessor_.IsValid() &&
-        accessor_.GetLayer().GetGeometry().IsResizeable())
-    {
-      originalSpacingX_ = accessor_.GetLayer().GetGeometry().GetPixelSpacingX();
-      originalSpacingY_ = accessor_.GetLayer().GetGeometry().GetPixelSpacingY();
-      originalPanX_ = accessor_.GetLayer().GetGeometry().GetPanX();
-      originalPanY_ = accessor_.GetLayer().GetGeometry().GetPanY();
-
-      size_t oppositeControlPointType;
-      switch (startControlPoint.index)
-      {
-        case RadiographyControlPointType_TopLeftCorner:
-          oppositeControlPointType = RadiographyControlPointType_BottomRightCorner;
-          break;
-
-        case RadiographyControlPointType_TopRightCorner:
-          oppositeControlPointType = RadiographyControlPointType_BottomLeftCorner;
-          break;
-
-        case RadiographyControlPointType_BottomLeftCorner:
-          oppositeControlPointType = RadiographyControlPointType_TopRightCorner;
-          break;
-
-        case RadiographyControlPointType_BottomRightCorner:
-          oppositeControlPointType = RadiographyControlPointType_TopLeftCorner;
-          break;
-
-        default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-
-      accessor_.GetLayer().GetControlPoint(startOppositeControlPoint_, oppositeControlPointType);
-
-      double d = ComputeDistance(startControlPoint.x, startControlPoint.y, startOppositeControlPoint_.x, startOppositeControlPoint_.y);
-      if (d >= std::numeric_limits<float>::epsilon())
-      {
-        baseScaling_ = 1.0 / d;
-      }
-      else
-      {
-        // Avoid division by zero in extreme cases
-        accessor_.Invalidate();
-      }
-    }
-  }
-
-
-  void RadiographyLayerResizeTracker::Render(CairoContext& context,
-                                             double zoom)
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-  }
-
-
-  void RadiographyLayerResizeTracker::MouseUp()
-  {
-    if (accessor_.IsValid() &&
-        accessor_.GetLayer().GetGeometry().IsResizeable())
-    {
-      undoRedoStack_.Add(new UndoRedoCommand(*this));
-    }
-  }
-
-  
-  void RadiographyLayerResizeTracker::MouseMove(int displayX,
-                                                int displayY,
-                                                double sceneX,
-                                                double sceneY,
-                                                const std::vector<Deprecated::Touch>& displayTouches,
-                                                const std::vector<Deprecated::Touch>& sceneTouches)
-  {
-    static const double ROUND_SCALING = 0.1;
-        
-    if (accessor_.IsValid() &&
-        accessor_.GetLayer().GetGeometry().IsResizeable())
-    {
-      double scaling = ComputeDistance(startOppositeControlPoint_.x, startOppositeControlPoint_.y, sceneX, sceneY) * baseScaling_;
-
-      if (roundScaling_)
-      {
-        scaling = boost::math::round<double>((scaling / ROUND_SCALING) * ROUND_SCALING);
-      }
-          
-      RadiographyLayer& layer = accessor_.GetLayer();
-      layer.SetPixelSpacing(scaling * originalSpacingX_,
-                            scaling * originalSpacingY_);
-
-      // Keep the opposite corner at a fixed location
-      ControlPoint currentOppositeCorner;
-      layer.GetControlPoint(currentOppositeCorner, startOppositeControlPoint_.index);
-      layer.SetPan(layer.GetGeometry().GetPanX() + startOppositeControlPoint_.x - currentOppositeCorner.x,
-                   layer.GetGeometry().GetPanY() + startOppositeControlPoint_.y - currentOppositeCorner.y);
-    }
-  }
-}
--- a/Framework/Radiography/RadiographyLayerResizeTracker.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/**
- * 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 "../Toolbox/UndoRedoStack.h"
-#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
-#include "RadiographyScene.h"
-
-namespace OrthancStone
-{
-  class RadiographyLayerResizeTracker : public Deprecated::IWorldSceneMouseTracker
-  {
-  private:
-    class UndoRedoCommand;
-
-    UndoRedoStack&                   undoRedoStack_;
-    RadiographyScene::LayerAccessor  accessor_;
-    bool                             roundScaling_;
-    double                           originalSpacingX_;
-    double                           originalSpacingY_;
-    double                           originalPanX_;
-    double                           originalPanY_;
-    ControlPoint                     startOppositeControlPoint_;
-    double                           baseScaling_;
-
-  public:
-    RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack,
-                                  RadiographyScene& scene,
-                                  size_t layer,
-                                  const ControlPoint& startControlPoint,
-                                  bool roundScaling);
-
-    virtual bool HasRender() const
-    {
-      return false;
-    }
-
-    virtual void Render(CairoContext& context,
-                        double zoom);
-
-    virtual void MouseUp();
-
-    virtual void MouseMove(int displayX,
-                           int displayY,
-                           double sceneX,
-                           double sceneY,
-                           const std::vector<Deprecated::Touch>& displayTouches,
-                           const std::vector<Deprecated::Touch>& sceneTouches);
-  };
-}
--- a/Framework/Radiography/RadiographyLayerRotateTracker.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,156 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographyLayerRotateTracker.h"
-
-#include "RadiographySceneCommand.h"
-
-#include <Core/OrthancException.h>
-
-#include <boost/math/constants/constants.hpp>
-#include <boost/math/special_functions/round.hpp>
-
-namespace OrthancStone
-{
-  class RadiographyLayerRotateTracker::UndoRedoCommand : public RadiographySceneCommand
-  {
-  private:
-    double  sourceAngle_;
-    double  targetAngle_;
-
-    static int ToDegrees(double angle)
-    {
-      return boost::math::iround(angle * 180.0 / boost::math::constants::pi<double>());
-    }
-      
-  protected:
-    virtual void UndoInternal(RadiographyLayer& layer) const
-    {
-      LOG(INFO) << "Undo - Set angle to " << ToDegrees(sourceAngle_) << " degrees";
-      layer.SetAngle(sourceAngle_);
-    }
-
-    virtual void RedoInternal(RadiographyLayer& layer) const
-    {
-      LOG(INFO) << "Redo - Set angle to " << ToDegrees(sourceAngle_) << " degrees";
-      layer.SetAngle(targetAngle_);
-    }
-
-  public:
-    UndoRedoCommand(const RadiographyLayerRotateTracker& tracker) :
-      RadiographySceneCommand(tracker.accessor_),
-      sourceAngle_(tracker.originalAngle_),
-      targetAngle_(tracker.accessor_.GetLayer().GetGeometry().GetAngle())
-    {
-    }
-  };
-
-
-  bool RadiographyLayerRotateTracker::ComputeAngle(double& angle /* out */,
-                                                   double sceneX,
-                                                   double sceneY) const
-  {
-    Vector u;
-    LinearAlgebra::AssignVector(u, sceneX - centerX_, sceneY - centerY_);
-
-    double nu = boost::numeric::ublas::norm_2(u);
-
-    if (!LinearAlgebra::IsCloseToZero(nu))
-    {
-      u /= nu;
-      angle = atan2(u[1], u[0]);
-      return true;
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-
-  RadiographyLayerRotateTracker::RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack,
-                                                               RadiographyScene& scene,
-                                                               const Deprecated::ViewportGeometry& view,
-                                                               size_t layer,
-                                                               double x,
-                                                               double y,
-                                                               bool roundAngles) :
-    undoRedoStack_(undoRedoStack),
-    accessor_(scene, layer),
-    roundAngles_(roundAngles)
-  {
-    if (accessor_.IsValid())
-    {
-      accessor_.GetLayer().GetCenter(centerX_, centerY_);
-      originalAngle_ = accessor_.GetLayer().GetGeometry().GetAngle();
-
-      double sceneX, sceneY;
-      view.MapDisplayToScene(sceneX, sceneY, x, y);
-
-      if (!ComputeAngle(clickAngle_, x, y))
-      {
-        accessor_.Invalidate();
-      }
-    }
-  }
-
-
-  void RadiographyLayerRotateTracker::Render(CairoContext& context,
-                                             double zoom)
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-  }
-
-
-  void RadiographyLayerRotateTracker::MouseUp()
-  {
-    if (accessor_.IsValid())
-    {
-      undoRedoStack_.Add(new UndoRedoCommand(*this));
-    }
-  }
-
-  
-  void RadiographyLayerRotateTracker::MouseMove(int displayX,
-                                                int displayY,
-                                                double sceneX,
-                                                double sceneY,
-                                                const std::vector<Deprecated::Touch>& displayTouches,
-                                                const std::vector<Deprecated::Touch>& sceneTouches)
-  {
-    static const double ROUND_ANGLE = 15.0 / 180.0 * boost::math::constants::pi<double>(); 
-        
-    double angle;
-        
-    if (accessor_.IsValid() &&
-        ComputeAngle(angle, sceneX, sceneY))
-    {
-      angle = angle - clickAngle_ + originalAngle_;
-
-      if (roundAngles_)
-      {
-        angle = boost::math::round<double>((angle / ROUND_ANGLE) * ROUND_ANGLE);
-      }
-          
-      accessor_.GetLayer().SetAngle(angle);
-    }
-  }
-}
--- a/Framework/Radiography/RadiographyLayerRotateTracker.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-/**
- * 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 "../Toolbox/UndoRedoStack.h"
-#include "../Deprecated/Toolbox/ViewportGeometry.h"
-#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
-#include "RadiographyScene.h"
-
-
-namespace OrthancStone
-{
-  class RadiographyLayerRotateTracker : public Deprecated::IWorldSceneMouseTracker
-  {
-  private:
-    class UndoRedoCommand;
-
-    UndoRedoStack&                   undoRedoStack_;
-    RadiographyScene::LayerAccessor  accessor_;
-    double                           centerX_;
-    double                           centerY_;
-    double                           originalAngle_;
-    double                           clickAngle_;
-    bool                             roundAngles_;
-
-    bool ComputeAngle(double& angle /* out */,
-                      double sceneX,
-                      double sceneY) const;
-
-  public:
-    RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack,
-                                  RadiographyScene& scene,
-                                  const Deprecated::ViewportGeometry& view,
-                                  size_t layer,
-                                  double x,
-                                  double y,
-                                  bool roundAngles);
-
-    virtual bool HasRender() const
-    {
-      return false;
-    }
-
-    virtual void Render(CairoContext& context,
-                        double zoom);
-
-    virtual void MouseUp();
-
-    virtual void MouseMove(int displayX,
-                           int displayY,
-                           double sceneX,
-                           double sceneY,
-                           const std::vector<Deprecated::Touch>& displayTouches,
-                           const std::vector<Deprecated::Touch>& sceneTouches);
-  };
-}
--- a/Framework/Radiography/RadiographyMaskLayer.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,199 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographyMaskLayer.h"
-#include "RadiographyDicomLayer.h"
-
-#include "RadiographyScene.h"
-#include "Core/Images/Image.h"
-#include "Core/Images/ImageProcessing.h"
-#include <Core/OrthancException.h>
-#include "../Toolbox/ImageGeometry.h"
-
-namespace OrthancStone
-{
-  const unsigned char IN_MASK_VALUE = 0x77;
-  const unsigned char OUT_MASK_VALUE = 0xFF;
-
-  const AffineTransform2D& RadiographyMaskLayer::GetTransform() const
-  {
-    return dicomLayer_.GetTransform();
-  }
-
-  const AffineTransform2D& RadiographyMaskLayer::GetTransformInverse() const
-  {
-    return dicomLayer_.GetTransformInverse();
-  }
-
-  bool RadiographyMaskLayer::GetPixel(unsigned int& imageX,
-                        unsigned int& imageY,
-                        double sceneX,
-                        double sceneY) const
-  {
-    return dicomLayer_.GetPixel(imageX, imageY, sceneX, sceneY);
-  }
-
-  std::string RadiographyMaskLayer::GetInstanceId() const
-  {
-    return dicomLayer_.GetInstanceId();
-  }
-
-  void RadiographyMaskLayer::SetCorner(const Orthanc::ImageProcessing::ImagePoint& corner, size_t index)
-  {
-    if (index < corners_.size())
-      corners_[index] = corner;
-    else
-      corners_.push_back(corner);
-    invalidated_ = true;
-
-    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
-  }
-
-  void RadiographyMaskLayer::SetCorners(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners)
-  {
-    corners_ = corners;
-    invalidated_ = true;
-
-    BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
-  }
-
-  Extent2D RadiographyMaskLayer::GetSceneExtent(bool minimal) const
-  {
-    if (!minimal)
-    {
-      return RadiographyLayer::GetSceneExtent(minimal);
-    }
-    else
-    { // get the extent of the in-mask area
-      Extent2D sceneExtent;
-
-      for (std::vector<Orthanc::ImageProcessing::ImagePoint>::const_iterator corner = corners_.begin(); corner != corners_.end(); ++corner)
-      {
-        double x = static_cast<double>(corner->GetX());
-        double y = static_cast<double>(corner->GetY());
-
-        dicomLayer_.GetTransform().Apply(x, y);
-        sceneExtent.AddPoint(x, y);
-      }
-      return sceneExtent;
-    }
-  }
-
-
-
-  void RadiographyMaskLayer::Render(Orthanc::ImageAccessor& buffer,
-                                    const AffineTransform2D& viewTransform,
-                                    ImageInterpolation interpolation,
-                                    float windowCenter,
-                                    float windowWidth,
-                                    bool applyWindowing) const
-  {
-    if (dicomLayer_.GetWidth() == 0 || dicomLayer_.GetSourceImage() == NULL) // nothing to do if the DICOM layer is not displayed (or not loaded)
-      return;
-
-    if (invalidated_)
-    {
-      mask_.reset(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, dicomLayer_.GetWidth(), dicomLayer_.GetHeight(), false));
-
-      DrawMask();
-
-      invalidated_ = false;
-    }
-
-    {// rendering
-      if (buffer.GetFormat() != Orthanc::PixelFormat_Float32)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
-      }
-
-      unsigned int cropX, cropY, cropWidth, cropHeight;
-      dicomLayer_.GetCrop(cropX, cropY, cropWidth, cropHeight);
-
-      const AffineTransform2D t = AffineTransform2D::Combine(
-            viewTransform, dicomLayer_.GetTransform(),
-            AffineTransform2D::CreateOffset(cropX, cropY));
-
-      Orthanc::ImageAccessor cropped;
-      mask_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
-
-      Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false);
-
-
-      unsigned int x1, y1, x2, y2;
-      if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2,
-                                                      t.GetHomogeneousMatrix(),
-                                                      cropped.GetWidth(),
-                                                      cropped.GetHeight(),
-                                                      buffer.GetWidth(),
-                                                      buffer.GetHeight()))
-      {
-        return;  // layer is outside the buffer
-      }
-
-      t.Apply(tmp, cropped, ImageInterpolation_Nearest, true /* clear */);
-
-      // we have observed vertical lines at the image border (probably due to bilinear filtering of the DICOM image when it is not aligned with the buffer pixels)
-      // -> draw the mask one line further on each side
-      if (x1 >= 1)
-      {
-        x1 = x1 - 1;
-      }
-      if (x2 < buffer.GetWidth() - 2)
-      {
-        x2 = x2 + 1;
-      }
-
-      // Blit
-      for (unsigned int y = y1; y <= y2; y++)
-      {
-        float *q = reinterpret_cast<float*>(buffer.GetRow(y)) + x1;
-        const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y)) + x1;
-
-        for (unsigned int x = x1; x <= x2; x++, p++, q++)
-        {
-          if (*p != IN_MASK_VALUE)
-            *q = foreground_;
-          // else keep the underlying pixel value
-        }
-      }
-
-    }
-  }
-
-  void RadiographyMaskLayer::DrawMask() const
-  {
-    // first fill the complete image
-    Orthanc::ImageProcessing::Set(*mask_, OUT_MASK_VALUE);
-
-    // clip corners
-    std::vector<Orthanc::ImageProcessing::ImagePoint> clippedCorners;
-    for (size_t i = 0; i < corners_.size(); i++)
-    {
-      clippedCorners.push_back(corners_[i]);
-      clippedCorners[i].ClipTo(0, mask_->GetWidth() - 1, 0, mask_->GetHeight() - 1);
-    }
-
-    // fill mask
-    Orthanc::ImageProcessing::FillPolygon(*mask_, clippedCorners, IN_MASK_VALUE);
-
-  }
-
-}
--- a/Framework/Radiography/RadiographyMaskLayer.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-/**
- * 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 "RadiographyLayer.h"
-
-#include <Core/Compatibility.h>
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-
-namespace OrthancStone
-{
-  class RadiographyScene;
-  class RadiographyDicomLayer;
-
-  class RadiographyMaskLayer : public RadiographyLayer
-  {
-  private:
-    std::vector<Orthanc::ImageProcessing::ImagePoint>            corners_;
-    const RadiographyDicomLayer&      dicomLayer_;
-    mutable bool                      invalidated_;
-    float                             foreground_;
-
-    mutable std::unique_ptr<Orthanc::ImageAccessor>  mask_;
-  public:
-    RadiographyMaskLayer(const RadiographyScene& scene, const RadiographyDicomLayer& dicomLayer,
-                         float foreground) :
-      RadiographyLayer(scene),
-      dicomLayer_(dicomLayer),
-      invalidated_(true),
-      foreground_(foreground)
-    {
-    }
-
-    virtual size_t GetApproximateMemoryUsage() const
-    {
-      size_t size = 0;
-      if (mask_.get() != NULL)
-      {
-        size += mask_->GetPitch() * mask_->GetHeight();
-      }
-
-      return size;
-    }
-
-
-    void SetCorners(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners);
-    void SetCorner(const Orthanc::ImageProcessing::ImagePoint& corner, size_t index);
-
-    const std::vector<Orthanc::ImageProcessing::ImagePoint>& GetCorners() const
-    {
-      return corners_;
-    }
-
-    float GetForeground() const
-    {
-      return foreground_;
-    }
-
-    virtual void Render(Orthanc::ImageAccessor& buffer,
-                        const AffineTransform2D& viewTransform,
-                        ImageInterpolation interpolation,
-                        float windowCenter,
-                        float windowWidth,
-                        bool applyWindowing) const;
-
-    std::string GetInstanceId() const;
-
-    virtual size_t GetControlPointCount() const
-    {
-      return corners_.size();
-    }
-
-    virtual void GetControlPoint(ControlPoint& cpScene,
-                                         size_t index) const
-    {
-      ControlPoint cp(corners_[index].GetX(), corners_[index].GetY(), index);
-
-      // transforms image coordinates into scene coordinates
-      GetTransform().Apply(cp.x, cp.y);
-      cpScene = cp;
-    }
-
-    virtual Extent2D GetSceneExtent(bool minimal) const;
-
-    virtual bool GetDefaultWindowing(float& center,
-                                     float& width) const
-    {
-      return false;
-    }
-
-    virtual bool GetRange(float& minValue,
-                          float& maxValue) const
-    {
-      minValue = 0;
-      maxValue = 0;
-
-      if (foreground_ < 0)
-      {
-        minValue = foreground_;
-      }
-
-      if (foreground_ > 0)
-      {
-        maxValue = foreground_;
-      }
-
-      return true;
-
-    }
-
-    virtual bool GetPixel(unsigned int& imageX,
-                          unsigned int& imageY,
-                          double sceneX,
-                          double sceneY) const;
-
-  protected:
-    virtual const AffineTransform2D& GetTransform() const;
-
-    virtual const AffineTransform2D& GetTransformInverse() const;
-
-
-  private:
-    void DrawMask() const;
-
-  };
-}
--- a/Framework/Radiography/RadiographyScene.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,968 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographyScene.h"
-
-#include "RadiographyAlphaLayer.h"
-#include "RadiographyDicomLayer.h"
-#include "RadiographyTextLayer.h"
-#include "RadiographyMaskLayer.h"
-#include "../Deprecated/Toolbox/DicomFrameConverter.h"
-#include "../Scene2D/CairoCompositor.h"
-#include "../Scene2D/FloatTextureSceneLayer.h"
-#include "../Scene2D/TextSceneLayer.h"
-
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Images/PamReader.h>
-#include <Core/Images/PamWriter.h>
-#include <Core/Images/PngWriter.h>
-#include <Core/OrthancException.h>
-#include <Core/Toolbox.h>
-#include <Plugins/Samples/Common/DicomDatasetReader.h>
-#include <Plugins/Samples/Common/FullOrthancDataset.h>
-
-#include <boost/math/special_functions/round.hpp>
-
-
-namespace OrthancStone
-{
-  RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene,
-                                                 size_t index) :
-    scene_(scene),
-    index_(index)
-  {
-    Layers::iterator layer = scene.layers_.find(index);
-    if (layer == scene.layers_.end())
-    {
-      layer_ = NULL;
-    }
-    else
-    {
-      assert(layer->second != NULL);
-      layer_ = layer->second;
-    }
-  }
-
-
-  RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene,
-                                                 double x,
-                                                 double y) :
-    scene_(scene),
-    index_(0)  // Dummy initialization
-  {
-    if (scene.LookupLayer(index_, x, y))
-    {
-      Layers::iterator layer = scene.layers_.find(index_);
-
-      if (layer == scene.layers_.end())
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-      else
-      {
-        assert(layer->second != NULL);
-        layer_ = layer->second;
-      }
-    }
-    else
-    {
-      layer_ = NULL;
-    }
-  }
-
-
-  RadiographyScene& RadiographyScene::LayerAccessor::GetScene() const
-  {
-    if (IsValid())
-    {
-      return scene_;
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-  }
-
-
-  size_t RadiographyScene::LayerAccessor::GetIndex() const
-  {
-    if (IsValid())
-    {
-      return index_;
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-  }
-
-  
-  RadiographyLayer& RadiographyScene::LayerAccessor::GetLayer() const
-  {
-    if (IsValid())
-    {
-      return *layer_;
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-  }
-
-  void RadiographyScene::_RegisterLayer(RadiographyLayer* layer)
-  {
-    std::unique_ptr<RadiographyLayer> raii(layer);
-
-    // LOG(INFO) << "Registering layer: " << countLayers_;
-
-    size_t index = nextLayerIndex_++;
-    raii->SetIndex(index);
-    layers_[index] = raii.release();
-  }
-
-  RadiographyLayer& RadiographyScene::RegisterLayer(RadiographyLayer* layer)
-  {
-    if (layer == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-    }
-
-    _RegisterLayer(layer);
-
-    BroadcastMessage(GeometryChangedMessage(*this, *layer));
-    BroadcastMessage(ContentChangedMessage(*this, *layer));
-    Register<RadiographyLayer::LayerEditedMessage>(*layer, &RadiographyScene::OnLayerEdited);
-
-    return *layer;
-  }
-
-  size_t RadiographyScene::GetApproximateMemoryUsage() const
-  {
-    size_t size = 0;
-    for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++)
-    {
-      size += it->second->GetApproximateMemoryUsage();
-    }
-    return size;
-  }
-
-  void RadiographyScene::OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message)
-  {
-    BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, message.GetOrigin()));
-  }
-
-  
-  RadiographyScene::RadiographyScene() :
-    nextLayerIndex_(0),
-    hasWindowing_(false),
-    windowingCenter_(0),  // Dummy initialization
-    windowingWidth_(0)    // Dummy initialization
-  {
-  }
-
-
-  RadiographyScene::~RadiographyScene()
-  {
-    for (Layers::iterator it = layers_.begin(); it != layers_.end(); it++)
-    {
-      assert(it->second != NULL);
-      delete it->second;
-    }
-  }
-
-  RadiographyPhotometricDisplayMode RadiographyScene::GetPreferredPhotomotricDisplayMode() const
-  {
-    // return the mode of the first layer who "cares" about its display mode (normaly, the one and only layer that is a DicomLayer)
-    for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++)
-    {
-      if (it->second->GetPreferredPhotomotricDisplayMode() != RadiographyPhotometricDisplayMode_Default)
-      {
-        return it->second->GetPreferredPhotomotricDisplayMode();
-      }
-    }
-
-    return RadiographyPhotometricDisplayMode_Default;
-  }
-
-
-  void RadiographyScene::GetLayersIndexes(std::vector<size_t>& output) const
-  {
-    for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++)
-    {
-      output.push_back(it->first);
-    }
-  }
-
-  void RadiographyScene::RemoveLayer(size_t layerIndex)
-  {
-    LOG(INFO) << "Removing layer: " << layerIndex;
-
-    Layers::iterator found = layers_.find(layerIndex);
-
-    if (found == layers_.end())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-    else
-    {
-      assert(found->second != NULL);
-      delete found->second;
-      
-      layers_.erase(found);
-      
-      LOG(INFO) << "Removing layer, there are now : " << layers_.size() << " layers";
-
-      _OnLayerRemoved();
-
-      BroadcastMessage(RadiographyScene::LayerRemovedMessage(*this, layerIndex));
-    }
-  }
-
-  const RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) const
-  {
-    Layers::const_iterator found = layers_.find(layerIndex);
-    
-    if (found == layers_.end())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-    else
-    {
-      assert(found->second != NULL);
-      return *found->second;
-    }
-  }
-
-  RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex)
-  {
-    Layers::const_iterator found = layers_.find(layerIndex);
-
-    if (found == layers_.end())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-    else
-    {
-      assert(found->second != NULL);
-      return *found->second;
-    }
-  }
-
-  bool RadiographyScene::GetWindowing(float& center,
-                                      float& width) const
-  {
-    if (hasWindowing_)
-    {
-      center = windowingCenter_;
-      width = windowingWidth_;
-      return true;
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-
-  void RadiographyScene::GetWindowingWithDefault(float& center,
-                                                 float& width) const
-  {
-    if (!GetWindowing(center, width))
-    {
-      center = 128;
-      width = 256;
-    }
-  }
-
-
-  void RadiographyScene::SetWindowing(float center,
-                                      float width)
-  {
-    hasWindowing_ = true;
-    windowingCenter_ = center;
-    windowingWidth_ = width;
-
-    BroadcastMessage(RadiographyScene::WindowingChangedMessage(*this));
-  }
-
-
-  RadiographyLayer& RadiographyScene::UpdateText(size_t layerIndex,
-                                                 const std::string& utf8,
-                                                 const std::string& font,
-                                                 unsigned int fontSize,
-                                                 uint8_t foreground)
-  {
-    RadiographyTextLayer& textLayer = dynamic_cast<RadiographyTextLayer&>(GetLayer(layerIndex));
-    textLayer.SetText(utf8, font, fontSize, foreground);
-
-    BroadcastMessage(RadiographyScene::ContentChangedMessage(*this, textLayer));
-    BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, textLayer));
-    return textLayer;
-  }
-
-
-  RadiographyLayer& RadiographyScene::LoadText(const std::string& utf8,
-                                               const std::string& font,
-                                               unsigned int fontSize,
-                                               uint8_t foreground,
-                                               RadiographyLayer::Geometry* centerGeometry,
-                                               bool isCenterGeometry)
-  {
-    std::unique_ptr<RadiographyTextLayer>  alpha(new RadiographyTextLayer(*this));
-    alpha->SetText(utf8, font, fontSize, foreground);
-    if (centerGeometry != NULL)
-    {
-      if (isCenterGeometry)
-      {
-        // modify geometry to reference the top left corner
-        double tlx = centerGeometry->GetPanX();
-        double tly = centerGeometry->GetPanY();
-        Extent2D textExtent = alpha->GetSceneExtent(false);
-        tlx = tlx - (textExtent.GetWidth() / 2) * centerGeometry->GetPixelSpacingX();
-        tly = tly - (textExtent.GetHeight() / 2) * centerGeometry->GetPixelSpacingY();
-        centerGeometry->SetPan(tlx, tly);
-      }
-      alpha->SetGeometry(*centerGeometry);
-    }
-
-    RadiographyLayer& registeredLayer = RegisterLayer(alpha.release());
-
-    BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, registeredLayer));
-    return registeredLayer;
-  }
-
-
-  RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width,
-                                                    unsigned int height,
-                                                    RadiographyLayer::Geometry* geometry)
-  {
-    std::unique_ptr<Orthanc::Image>  block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false));
-
-    for (unsigned int padding = 0;
-         (width > 2 * padding) && (height > 2 * padding);
-         padding++)
-    {
-      uint8_t color;
-      if (255 > 10 * padding)
-      {
-        color = 255 - 10 * padding;
-      }
-      else
-      {
-        color = 0;
-      }
-
-      Orthanc::ImageAccessor region;
-      block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding);
-      Orthanc::ImageProcessing::Set(region, color);
-    }
-
-    return LoadAlphaBitmap(block.release(), geometry);
-  }
-
-  RadiographyLayer& RadiographyScene::LoadMask(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners,
-                                               const RadiographyDicomLayer& dicomLayer,
-                                               float foreground,
-                                               RadiographyLayer::Geometry* geometry)
-  {
-    std::unique_ptr<RadiographyMaskLayer>  mask(new RadiographyMaskLayer(*this, dicomLayer, foreground));
-    mask->SetCorners(corners);
-    if (geometry != NULL)
-    {
-      mask->SetGeometry(*geometry);
-    }
-
-    return RegisterLayer(mask.release());
-  }
-
-
-  RadiographyLayer& RadiographyScene::LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, RadiographyLayer::Geometry *geometry)
-  {
-    std::unique_ptr<RadiographyAlphaLayer>  alpha(new RadiographyAlphaLayer(*this));
-    alpha->SetAlpha(bitmap);
-    if (geometry != NULL)
-    {
-      alpha->SetGeometry(*geometry);
-    }
-
-    return RegisterLayer(alpha.release());
-  }
-
-  RadiographyLayer& RadiographyScene::LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership
-                                                     const std::string& instance,
-                                                     unsigned int frame,
-                                                     Deprecated::DicomFrameConverter* converter,  // takes ownership
-                                                     RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode,
-                                                     RadiographyLayer::Geometry* geometry)
-  {
-    RadiographyDicomLayer& layer = dynamic_cast<RadiographyDicomLayer&>(RegisterLayer(new RadiographyDicomLayer(*this)));
-
-    layer.SetInstance(instance, frame);
-
-    if (geometry != NULL)
-    {
-      layer.SetGeometry(*geometry);
-    }
-
-    layer.SetDicomFrameConverter(converter);
-    layer.SetSourceImage(dicomImage);
-    layer.SetPreferredPhotomotricDisplayMode(preferredPhotometricDisplayMode);
-
-    return layer;
-  }
-
-  RadiographyLayer& RadiographyScene::LoadDicomFrame(Deprecated::OrthancApiClient& orthanc,
-                                                     const std::string& instance,
-                                                     unsigned int frame,
-                                                     bool httpCompression,
-                                                     RadiographyLayer::Geometry* geometry)
-  {
-    RadiographyDicomLayer& layer = dynamic_cast<RadiographyDicomLayer&>(RegisterLayer(new RadiographyDicomLayer( *this)));
-    layer.SetInstance(instance, frame);
-
-    if (geometry != NULL)
-    {
-      layer.SetGeometry(*geometry);
-    }
-
-    {
-      Deprecated::IWebService::HttpHeaders headers;
-      std::string uri = "/instances/" + instance + "/tags";
-
-      orthanc.GetBinaryAsync(
-            uri, headers,
-            new Deprecated::DeprecatedCallable<RadiographyScene, Deprecated::OrthancApiClient::BinaryResponseReadyMessage>
-            (GetSharedObserver(), &RadiographyScene::OnTagsReceived), NULL,
-            new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
-    }
-
-    {
-      Deprecated::IWebService::HttpHeaders headers;
-      headers["Accept"] = "image/x-portable-arbitrarymap";
-
-      if (httpCompression)
-      {
-        headers["Accept-Encoding"] = "gzip";
-      }
-
-      std::string uri = ("/instances/" + instance + "/frames/" +
-                         boost::lexical_cast<std::string>(frame) + "/image-uint16");
-
-      orthanc.GetBinaryAsync(
-            uri, headers,
-            new Deprecated::DeprecatedCallable<RadiographyScene, Deprecated::OrthancApiClient::BinaryResponseReadyMessage>
-            (GetSharedObserver(), &RadiographyScene::OnFrameReceived), NULL,
-            new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
-    }
-
-    return layer;
-  }
-
-
-  RadiographyLayer& RadiographyScene::LoadDicomWebFrame(Deprecated::IWebService& web)
-  {
-    RadiographyLayer& layer = RegisterLayer(new RadiographyDicomLayer(*this));
-
-
-    return layer;
-  }
-
-
-
-  void RadiographyScene::OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message)
-  {
-    size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>
-        (message.GetPayload()).GetValue();
-
-    VLOG(1) << "JSON received: " << message.GetUri().c_str()
-            << " (" << message.GetAnswerSize() << " bytes) for layer " << index;
-
-    Layers::iterator layer = layers_.find(index);
-    if (layer != layers_.end())
-    {
-      assert(layer->second != NULL);
-
-      OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize());
-      dynamic_cast<RadiographyDicomLayer*>(layer->second)->SetDicomTags(dicom);
-
-      float c, w;
-      if (!hasWindowing_ &&
-          layer->second->GetDefaultWindowing(c, w))
-      {
-        hasWindowing_ = true;
-        windowingCenter_ = c;
-        windowingWidth_ = w;
-      }
-
-      BroadcastMessage(GeometryChangedMessage(*this, *(layer->second)));
-    }
-  }
-
-
-  void RadiographyScene::OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message)
-  {
-    size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue();
-
-    VLOG(1) << "DICOM frame received: " << message.GetUri().c_str()
-            << " (" << message.GetAnswerSize() << " bytes) for layer " << index;
-
-    Layers::iterator layer = layers_.find(index);
-    if (layer != layers_.end())
-    {
-      assert(layer->second != NULL);
-
-      std::string content;
-      if (message.GetAnswerSize() > 0)
-      {
-        content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize());
-      }
-
-      std::unique_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader);
-      reader->ReadFromMemory(content);
-      dynamic_cast<RadiographyDicomLayer*>(layer->second)->SetSourceImage(reader.release());
-
-      BroadcastMessage(ContentChangedMessage(*this, *(layer->second)));
-    }
-  }
-
-
-  Extent2D RadiographyScene::GetSceneExtent(bool minimal) const
-  {
-    Extent2D extent;
-
-    for (Layers::const_iterator it = layers_.begin();
-         it != layers_.end(); ++it)
-    {
-      assert(it->second != NULL);
-      extent.Union(it->second->GetSceneExtent(minimal));
-    }
-
-    return extent;
-  }
-
-
-  void RadiographyScene::Render(Orthanc::ImageAccessor& buffer,
-                                const AffineTransform2D& viewTransform,
-                                ImageInterpolation interpolation,
-                                bool applyWindowing) const
-  {
-    // Render layers in the background-to-foreground order
-    for (size_t index = 0; index < nextLayerIndex_; index++)
-    {
-      try
-      {
-        Layers::const_iterator it = layers_.find(index);
-        if (it != layers_.end())
-        {
-          assert(it->second != NULL);
-          it->second->Render(buffer, viewTransform, interpolation, windowingCenter_, windowingWidth_, applyWindowing);
-        }
-      }
-      catch (Orthanc::OrthancException& ex)
-      {
-        LOG(ERROR) << "RadiographyScene::Render: " << index << ", OrthancException: " << ex.GetDetails();
-        throw ex; // rethrow because we want it to crash to see there's a problem !
-      }
-      catch (...)
-      {
-        LOG(ERROR) << "RadiographyScene::Render: " << index << ", unkown exception: ";
-        throw; // rethrow because we want it to crash to see there's a problem !
-      }
-    }
-  }
-
-
-  bool RadiographyScene::LookupLayer(size_t& index /* out */,
-                                     double x,
-                                     double y) const
-  {
-    // Render layers in the foreground-to-background order
-    for (size_t i = nextLayerIndex_; i > 0; i--)
-    {
-      index = i - 1;
-      Layers::const_iterator it = layers_.find(index);
-      if (it != layers_.end())
-      {
-        assert(it->second != NULL);
-        if (it->second->Contains(x, y))
-        {
-          return true;
-        }
-      }
-    }
-
-    return false;
-  }
-
-
-  void RadiographyScene::DrawBorder(CairoContext& context,
-                                    unsigned int layer,
-                                    double zoom)
-  {
-    Layers::const_iterator found = layers_.find(layer);
-
-    if (found != layers_.end())
-    {
-      context.SetSourceColor(255, 0, 0);
-      found->second->DrawBorders(context, zoom);
-    }
-  }
-
-
-  void RadiographyScene::GetRange(float& minValue,
-                                  float& maxValue) const
-  {
-    bool first = true;
-
-    for (Layers::const_iterator it = layers_.begin();
-         it != layers_.end(); it++)
-    {
-      assert(it->second != NULL);
-
-      float a, b;
-      if (it->second->GetRange(a, b))
-      {
-        if (first)
-        {
-          minValue = a;
-          maxValue = b;
-          first = false;
-        }
-        else
-        {
-          minValue = std::min(a, minValue);
-          maxValue = std::max(b, maxValue);
-        }
-      }
-    }
-
-    if (first)
-    {
-      minValue = 0;
-      maxValue = 0;
-    }
-  }
-
-  void RadiographyScene::ExtractLayerFromRenderedScene(Orthanc::ImageAccessor& layer,
-                                                       const Orthanc::ImageAccessor& renderedScene,
-                                                       size_t layerIndex,
-                                                       bool isCropped,
-                                                       ImageInterpolation interpolation)
-  {
-    Extent2D sceneExtent = GetSceneExtent(isCropped);
-
-    double pixelSpacingX = sceneExtent.GetWidth() / renderedScene.GetWidth();
-    double pixelSpacingY = sceneExtent.GetHeight() / renderedScene.GetHeight();
-
-    AffineTransform2D view = AffineTransform2D::Combine(
-          AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY),
-          AffineTransform2D::CreateOffset(-sceneExtent.GetX1(), -sceneExtent.GetY1()));
-
-    AffineTransform2D layerToSceneTransform = AffineTransform2D::Combine(
-          view,
-          GetLayer(layerIndex).GetTransform());
-
-    AffineTransform2D sceneToLayerTransform = AffineTransform2D::Invert(layerToSceneTransform);
-    sceneToLayerTransform.Apply(layer, renderedScene, interpolation, false);
-  }
-
-  Orthanc::Image* RadiographyScene::ExportToImage(double pixelSpacingX,
-                                                  double pixelSpacingY,
-                                                  ImageInterpolation interpolation,
-                                                  bool invert,
-                                                  int64_t maxValue /* for inversion */,
-                                                  bool autoCrop,
-                                                  bool applyWindowing)
-  {
-    if (pixelSpacingX <= 0 ||
-        pixelSpacingY <= 0)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    Extent2D extent = GetSceneExtent(autoCrop);
-
-    int w = boost::math::iround(extent.GetWidth() / pixelSpacingX);
-    int h = boost::math::iround(extent.GetHeight() / pixelSpacingY);
-
-    if (w < 0 || h < 0)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-
-    Orthanc::Image layers(Orthanc::PixelFormat_Float32,
-                          static_cast<unsigned int>(w),
-                          static_cast<unsigned int>(h), false);
-
-    AffineTransform2D view = AffineTransform2D::Combine(
-          AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY),
-          AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1()));
-
-    // wipe background before rendering
-    if (GetPreferredPhotomotricDisplayMode() == RadiographyPhotometricDisplayMode_Monochrome1)
-    {
-      Orthanc::ImageProcessing::Set(layers, 65535);
-    }
-    else
-    {
-      Orthanc::ImageProcessing::Set(layers, 0);
-    }
-
-    Render(layers, view, interpolation, applyWindowing);
-
-    std::unique_ptr<Orthanc::Image> rendered(new Orthanc::Image(Orthanc::PixelFormat_Grayscale16,
-                                                              layers.GetWidth(), layers.GetHeight(), false));
-
-    Orthanc::ImageProcessing::Convert(*rendered, layers);
-    if (invert)
-      Orthanc::ImageProcessing::Invert(*rendered, maxValue);
-
-    return rendered.release();
-  }
-
-
-  Orthanc::Image* RadiographyScene::ExportToCreateDicomRequestAndImage(Json::Value& createDicomRequestContent,
-                                                                       const Json::Value& dicomTags,
-                                                                       const std::string& parentOrthancId,
-                                                                       double pixelSpacingX,
-                                                                       double pixelSpacingY,
-                                                                       bool invert,
-                                                                       bool autoCrop,
-                                                                       ImageInterpolation interpolation)
-  {
-    LOG(INFO) << "Exporting RadiographyScene to DICOM";
-
-    std::unique_ptr<Orthanc::Image> rendered(ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, autoCrop, false)); // note: we don't invert the image in the pixels data because we'll set the PhotometricDisplayMode correctly in the DICOM tags
-
-    createDicomRequestContent["Tags"] = dicomTags;
-
-    RadiographyPhotometricDisplayMode photometricMode = GetPreferredPhotomotricDisplayMode();
-    if ((invert && photometricMode != RadiographyPhotometricDisplayMode_Monochrome2) ||
-        (!invert && photometricMode == RadiographyPhotometricDisplayMode_Monochrome1))
-    {
-      createDicomRequestContent["Tags"]["PhotometricInterpretation"] = "MONOCHROME1";
-    }
-    else
-    {
-      createDicomRequestContent["Tags"]["PhotometricInterpretation"] = "MONOCHROME2";
-    }
-
-    // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to
-    // avoid floating-point numbers to grow over 16 characters,
-    // which would be invalid according to DICOM standard
-    // ("dciodvfy" would complain).
-    char buf[32];
-    sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX);
-
-    createDicomRequestContent["Tags"]["PixelSpacing"] = buf;
-
-    float center, width;
-    if (GetWindowing(center, width))
-    {
-      createDicomRequestContent["Tags"]["WindowCenter"] =
-          boost::lexical_cast<std::string>(boost::math::iround(center));
-
-      createDicomRequestContent["Tags"]["WindowWidth"] =
-          boost::lexical_cast<std::string>(boost::math::iround(width));
-    }
-
-    if (!parentOrthancId.empty())
-    {
-      createDicomRequestContent["Parent"] = parentOrthancId;
-    }
-
-    return rendered.release();
-  }
-
-
-  void RadiographyScene::ExportToCreateDicomRequest(Json::Value& createDicomRequestContent,
-                                                    const Json::Value& dicomTags,
-                                                    const std::string& parentOrthancId,
-                                                    double pixelSpacingX,
-                                                    double pixelSpacingY,
-                                                    bool invert,
-                                                    bool autoCrop,
-                                                    ImageInterpolation interpolation,
-                                                    bool usePam)
-  {
-    LOG(INFO) << "Exporting RadiographyScene to DICOM";
-    VLOG(1) << "Exporting RadiographyScene to: export to image";
-
-    std::unique_ptr<Orthanc::Image> rendered(ExportToCreateDicomRequestAndImage(createDicomRequestContent, dicomTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation));
-
-    // convert the image into base64 for inclusing in the createDicomRequest
-    std::string base64;
-
-    {
-      std::string content;
-
-      if (usePam)
-      {
-        VLOG(1) << "Exporting RadiographyScene: convert to PAM";
-        Orthanc::PamWriter writer;
-        writer.WriteToMemory(content, *rendered);
-      }
-      else
-      {
-        Orthanc::PngWriter writer;
-        writer.WriteToMemory(content, *rendered);
-      }
-
-      VLOG(1) << "Exporting RadiographyScene: encoding to base64";
-      Orthanc::Toolbox::EncodeBase64(base64, content);
-    }
-
-    // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme
-    createDicomRequestContent["Content"] = ("data:" +
-                                            std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) +
-                                            ";base64," + base64);
-
-    VLOG(1) << "Exporting RadiographyScene: create-dicom request is ready";
-  }
-
-
-  void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc,
-                                     const Json::Value& dicomTags,
-                                     const std::string& parentOrthancId,
-                                     double pixelSpacingX,
-                                     double pixelSpacingY,
-                                     bool invert,
-                                     bool autoCrop,
-                                     ImageInterpolation interpolation,
-                                     bool usePam)
-  {
-    Json::Value createDicomRequestContent;
-
-    ExportToCreateDicomRequest(createDicomRequestContent, dicomTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation, usePam);
-
-    orthanc.PostJsonAsyncExpectJson(
-          "/tools/create-dicom", createDicomRequestContent,
-          new Deprecated::DeprecatedCallable<RadiographyScene, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
-          (GetSharedObserver(), &RadiographyScene::OnDicomExported),
-          NULL, NULL);
-
-  }
-
-
-  // Export using PAM is faster than using PNG, but requires Orthanc
-  // core >= 1.4.3
-  void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc,
-                                     const Orthanc::DicomMap& dicom,
-                                     const std::string& parentOrthancId,
-                                     double pixelSpacingX,
-                                     double pixelSpacingY,
-                                     bool invert,
-                                     bool autoCrop,
-                                     ImageInterpolation interpolation,
-                                     bool usePam)
-  {
-    std::set<Orthanc::DicomTag> tags;
-    dicom.GetTags(tags);
-
-    Json::Value jsonTags = Json::objectValue;
-
-    for (std::set<Orthanc::DicomTag>::const_iterator
-         tag = tags.begin(); tag != tags.end(); ++tag)
-    {
-      const Orthanc::DicomValue& value = dicom.GetValue(*tag);
-      if (!value.IsNull() &&
-          !value.IsBinary())
-      {
-        jsonTags[tag->Format()] = value.GetContent();
-      }
-    }
-
-    ExportDicom(orthanc, jsonTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation, usePam);
-  }
-
-  void RadiographyScene::OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
-  {
-    LOG(INFO) << "DICOM export was successful: "
-              << message.GetJson().toStyledString();
-  }
-
-
-  void RadiographyScene::OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message)
-  {
-    LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes";
-
-    const Deprecated::IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders();
-    for (Deprecated::IWebService::HttpHeaders::const_iterator
-         it = h.begin(); it != h.end(); ++it)
-    {
-      printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str());
-    }
-  }
-
-  void RadiographyScene::ExportToScene2D(Scene2D& output) const
-  {
-    int depth = 0;
-    for (Layers::const_iterator it = layers_.begin();
-         it != layers_.end(); ++it)
-    {
-      assert(it->second != NULL);
-
-      std::unique_ptr<ISceneLayer> layer;
-      if (dynamic_cast<RadiographyDicomLayer*>(it->second))
-      {
-        RadiographyDicomLayer* oldLayer = dynamic_cast<RadiographyDicomLayer*>(it->second);
-
-        std::unique_ptr<FloatTextureSceneLayer> newLayer(new FloatTextureSceneLayer(*(oldLayer->GetSourceImage())));
-
-        newLayer->SetOrigin(oldLayer->GetGeometry().GetPanX(),
-                            oldLayer->GetGeometry().GetPanY()
-                            );
-        newLayer->SetAngle(oldLayer->GetGeometry().GetAngle());
-
-        layer.reset(newLayer.release());
-
-        // TODO: windowing dynamic_cast
-      }
-      else if (dynamic_cast<RadiographyTextLayer*>(it->second))
-      {
-        RadiographyTextLayer* oldLayer = dynamic_cast<RadiographyTextLayer*>(it->second);
-
-        std::unique_ptr<TextSceneLayer> newLayer(new TextSceneLayer());
-
-        newLayer->SetText(oldLayer->GetText());
-        newLayer->SetColor(oldLayer->GetForegroundGreyLevel(),
-                           oldLayer->GetForegroundGreyLevel(),
-                           oldLayer->GetForegroundGreyLevel()
-                           );
-        newLayer->SetPosition(oldLayer->GetGeometry().GetPanX(),
-                              oldLayer->GetGeometry().GetPanY()
-                              );
-        newLayer->SetFontIndex(1);
-        newLayer->SetAnchor(BitmapAnchor_TopLeft);
-        //newLayer->SetAngle(oldLayer->GetGeometry().GetAngle());
-
-        layer.reset(newLayer.release());
-      }
-
-      output.SetLayer(depth++, layer.release());
-
-    }
-
-  }
-
-}
-
--- a/Framework/Radiography/RadiographyScene.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,370 +0,0 @@
-/**
- * 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 "RadiographyLayer.h"
-#include "../Messages/ObserverBase.h"
-#include "../Deprecated/Toolbox/DicomFrameConverter.h"
-#include "../Deprecated/Toolbox/OrthancApiClient.h"
-#include "../StoneEnumerations.h"
-#include "Core/Images/Image.h"
-#include "Core/Images/ImageProcessing.h"
-
-#include "../Scene2D/Scene2D.h"
-
-namespace OrthancStone
-{
-  class RadiographyDicomLayer;
-
-  class RadiographyScene :
-    public ObserverBase<RadiographyScene>,
-    public IObservable
-  {
-    friend class RadiographySceneGeometryReader;
-  public:
-    class GeometryChangedMessage : public OriginMessage<RadiographyScene>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-
-    private:
-      RadiographyLayer&        layer_;
-
-    public:
-      GeometryChangedMessage(const RadiographyScene& origin,
-                             RadiographyLayer& layer) :
-        OriginMessage(origin),
-        layer_(layer)
-      {
-      }
-
-      RadiographyLayer& GetLayer() const
-      {
-        return layer_;
-      }
-    };
-
-    class ContentChangedMessage : public OriginMessage<RadiographyScene>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-
-    private:
-      RadiographyLayer&        layer_;
-
-    public:
-      ContentChangedMessage(const RadiographyScene& origin,
-                            RadiographyLayer& layer) :
-        OriginMessage(origin),
-        layer_(layer)
-      {
-      }
-
-      RadiographyLayer& GetLayer() const
-      {
-        return layer_;
-      }
-    };
-
-    class LayerEditedMessage : public OriginMessage<RadiographyScene>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-
-    private:
-      const RadiographyLayer&        layer_;
-
-    public:
-      LayerEditedMessage(const RadiographyScene& origin,
-                         const RadiographyLayer& layer) :
-        OriginMessage(origin),
-        layer_(layer)
-      {
-      }
-
-      const RadiographyLayer& GetLayer() const
-      {
-        return layer_;
-      }
-    };
-
-    class LayerRemovedMessage : public OriginMessage<RadiographyScene>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-
-    private:
-      size_t&        layerIndex_;
-
-    public:
-      LayerRemovedMessage(const RadiographyScene& origin,
-                          size_t& layerIndex) :
-        OriginMessage(origin),
-        layerIndex_(layerIndex)
-      {
-      }
-
-      size_t& GetLayerIndex() const
-      {
-        return layerIndex_;
-      }
-    };
-
-
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, WindowingChangedMessage, RadiographyScene);
-
-    
-    class LayerAccessor : public boost::noncopyable
-    {
-    private:
-      RadiographyScene&  scene_;
-      size_t             index_;
-      RadiographyLayer*  layer_;
-
-    public:
-      LayerAccessor(RadiographyScene& scene,
-                    size_t index);
-
-      LayerAccessor(RadiographyScene& scene,
-                    double x,
-                    double y);
-
-      void Invalidate()
-      {
-        layer_ = NULL;
-      }
-
-      bool IsValid() const
-      {
-        return layer_ != NULL;
-      }
-
-      RadiographyScene& GetScene() const;
-
-      size_t GetIndex() const;
-
-      RadiographyLayer& GetLayer() const;
-    };
-
-
-  protected:
-    typedef std::map<size_t, RadiographyLayer*>  Layers;
-
-    size_t  nextLayerIndex_;
-    bool    hasWindowing_;
-    float   windowingCenter_;
-    float   windowingWidth_;
-    Layers  layers_;
-
-  public:
-    RadiographyLayer& RegisterLayer(RadiographyLayer* layer);
-
-  protected:
-    virtual void _RegisterLayer(RadiographyLayer* layer);
-    virtual void _OnLayerRemoved() {}
-
-    void SetLayerIndex(RadiographyLayer* layer, size_t index)
-    {
-      layer->SetIndex(index);
-    }
-
-    virtual void OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message);
-
-    virtual void OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message);
-    
-    void OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message);
-
-    void OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message);
-
-    virtual void OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message);
-
-  public:
-    RadiographyScene();
-    
-    virtual ~RadiographyScene();
-
-    virtual size_t GetApproximateMemoryUsage() const;
-
-    bool GetWindowing(float& center,
-                      float& width) const;
-
-    void GetWindowingWithDefault(float& center,
-                                 float& width) const;
-
-    virtual void SetWindowing(float center,
-                              float width);
-
-    RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const;
-
-    RadiographyLayer& LoadText(const std::string& utf8,
-                               const std::string& font,
-                               unsigned int fontSize,
-                               uint8_t foreground,
-                               RadiographyLayer::Geometry* geometry,
-                               bool isCenterGeometry);
-
-    RadiographyLayer& UpdateText(size_t layerIndex,
-                                 const std::string& font,
-                                 const std::string& utf8,
-                                 unsigned int fontSize,
-                                 uint8_t foreground);
-
-    RadiographyLayer& LoadTestBlock(unsigned int width,
-                                    unsigned int height,
-                                    RadiographyLayer::Geometry* geometry);
-
-    RadiographyLayer& LoadMask(const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners,
-                               const RadiographyDicomLayer& dicomLayer,
-                               float foreground,
-                               RadiographyLayer::Geometry* geometry);
-
-    RadiographyLayer& LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap,  // takes ownership
-                                      RadiographyLayer::Geometry* geometry);
-
-    virtual RadiographyLayer& LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership
-                                             const std::string& instance,
-                                             unsigned int frame,
-                                             Deprecated::DicomFrameConverter* converter,  // takes ownership
-                                             RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode,
-                                             RadiographyLayer::Geometry* geometry);
-
-    virtual RadiographyLayer& LoadDicomFrame(Deprecated::OrthancApiClient& orthanc,
-                                             const std::string& instance,
-                                             unsigned int frame,
-                                             bool httpCompression,
-                                             RadiographyLayer::Geometry* geometry); // pass NULL if you want default geometry
-
-    RadiographyLayer& LoadDicomWebFrame(Deprecated::IWebService& web);
-
-    void RemoveLayer(size_t layerIndex);
-
-    RadiographyLayer& GetLayer(size_t layerIndex);
-
-    const RadiographyLayer& GetLayer(size_t layerIndex) const;
-
-    template <typename TypeLayer>
-    TypeLayer* GetTypedLayer(size_t indexOfType = 0)
-    {
-      std::vector<size_t> layerIndexes;
-      GetLayersIndexes(layerIndexes);
-
-      size_t count = 0;
-
-      for (size_t i = 0; i < layerIndexes.size(); ++i)
-      {
-        TypeLayer* typedLayer = dynamic_cast<TypeLayer*>(layers_[layerIndexes[i]]);
-        if (typedLayer != NULL)
-        {
-          if (count == indexOfType)
-          {
-            return typedLayer;
-          }
-          count++;
-        }
-      }
-
-      return NULL;
-    }
-
-    void GetLayersIndexes(std::vector<size_t>& output) const;
-
-    virtual Extent2D GetSceneExtent(bool minimal) const;
-
-    virtual void Render(Orthanc::ImageAccessor& buffer,
-                        const AffineTransform2D& viewTransform,
-                        ImageInterpolation interpolation,
-                        bool applyWindowing) const;
-
-    bool LookupLayer(size_t& index /* out */,
-                     double x,
-                     double y) const;
-    
-    void DrawBorder(CairoContext& context,
-                    unsigned int layer,
-                    double zoom);
-
-    void GetRange(float& minValue,
-                  float& maxValue) const;
-
-    void ExportToScene2D(Scene2D& output) const;
-
-    // Export using PAM is faster than using PNG, but requires Orthanc
-    // core >= 1.4.3
-    void ExportDicom(Deprecated::OrthancApiClient& orthanc,
-                     const Orthanc::DicomMap& dicom,
-                     const std::string& parentOrthancId,
-                     double pixelSpacingX,
-                     double pixelSpacingY,
-                     bool invert,
-                     bool autoCrop,
-                     ImageInterpolation interpolation,
-                     bool usePam);
-
-    void ExportDicom(Deprecated::OrthancApiClient& orthanc,
-                     const Json::Value& dicomTags,
-                     const std::string& parentOrthancId,
-                     double pixelSpacingX,
-                     double pixelSpacingY,
-                     bool invert,
-                     bool autoCrop,
-                     ImageInterpolation interpolation,
-                     bool usePam);
-
-    void ExportToCreateDicomRequest(Json::Value& createDicomRequestContent,
-                                    const Json::Value& dicomTags,
-                                    const std::string& parentOrthancId,
-                                    double pixelSpacingX,
-                                    double pixelSpacingY,
-                                    bool invert,
-                                    bool autoCrop,
-                                    ImageInterpolation interpolation,
-                                    bool usePam);
-
-    Orthanc::Image* ExportToCreateDicomRequestAndImage(Json::Value& createDicomRequestContent,
-                                                       const Json::Value& dicomTags,
-                                                       const std::string& parentOrthancId,
-                                                       double pixelSpacingX,
-                                                       double pixelSpacingY,
-                                                       bool invert,
-                                                       bool autoCrop,
-                                                       ImageInterpolation interpolation);
-
-    Orthanc::Image* ExportToImage(double pixelSpacingX,
-                                  double pixelSpacingY,
-                                  ImageInterpolation interpolation,
-                                  bool autoCrop,
-                                  bool applyWindowing)
-    {
-      return ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, false, 0, autoCrop, applyWindowing);
-    }
-
-    Orthanc::Image* ExportToImage(double pixelSpacingX,
-                                  double pixelSpacingY,
-                                  ImageInterpolation interpolation,
-                                  bool invert,
-                                  int64_t maxValue /* for inversion */,
-                                  bool autoCrop,
-                                  bool applyWindowing);
-
-    void ExtractLayerFromRenderedScene(Orthanc::ImageAccessor& layer,
-                                       const Orthanc::ImageAccessor& renderedScene,
-                                       size_t layerIndex,
-                                       bool isCropped,
-                                       ImageInterpolation interpolation);
-  };
-}
--- a/Framework/Radiography/RadiographySceneCommand.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographySceneCommand.h"
-
-
-namespace OrthancStone
-{
-  RadiographySceneCommand::RadiographySceneCommand(RadiographyScene& scene,
-                                                   size_t layer) :
-    scene_(scene),
-    layer_(layer)
-  {
-  }
-
-  
-  RadiographySceneCommand::RadiographySceneCommand(const RadiographyScene::LayerAccessor& accessor) :
-    scene_(accessor.GetScene()),
-    layer_(accessor.GetIndex())
-  {
-  }
-
-  
-  void RadiographySceneCommand::Undo() const
-  {
-    RadiographyScene::LayerAccessor accessor(scene_, layer_);
-
-    if (accessor.IsValid())
-    {
-      UndoInternal(accessor.GetLayer());
-    }
-  }
-
-  
-  void RadiographySceneCommand::Redo() const
-  {
-    RadiographyScene::LayerAccessor accessor(scene_, layer_);
-
-    if (accessor.IsValid())
-    {
-      RedoInternal(accessor.GetLayer());
-    }
-  }
-}
--- a/Framework/Radiography/RadiographySceneCommand.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/**
- * 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 "../Toolbox/UndoRedoStack.h"
-#include "RadiographyScene.h"
-
-namespace OrthancStone
-{
-  class RadiographySceneCommand : public UndoRedoStack::ICommand
-  {
-  private:
-    RadiographyScene&  scene_;
-    size_t             layer_;
-
-  protected:
-    virtual void UndoInternal(RadiographyLayer& layer) const = 0;
-
-    virtual void RedoInternal(RadiographyLayer& layer) const = 0;
-
-  public:
-    RadiographySceneCommand(RadiographyScene& scene,
-                            size_t layer);
-
-    RadiographySceneCommand(const RadiographyScene::LayerAccessor& accessor);
-
-    virtual void Undo() const;
-
-    virtual void Redo() const;
-  };
-}
--- a/Framework/Radiography/RadiographySceneReader.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,189 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographySceneReader.h"
-
-#include "../Deprecated/Toolbox/DicomFrameConverter.h"
-
-#include <Core/Images/FontRegistry.h>
-#include <Core/Images/PngReader.h>
-#include <Core/OrthancException.h>
-#include <Core/Toolbox.h>
-
-namespace OrthancStone
-{
-
-  void RadiographySceneBuilder::Read(const Json::Value& input, Orthanc::ImageAccessor* dicomImage /* takes ownership */,
-                                     Deprecated::DicomFrameConverter* dicomFrameConverter  /* takes ownership */,
-                                     RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode
-                                     )
-  {
-    dicomImage_.reset(dicomImage);
-    dicomFrameConverter_.reset(dicomFrameConverter);
-    preferredPhotometricDisplayMode_ = preferredPhotometricDisplayMode;
-    Read(input);
-  }
-
-  RadiographyDicomLayer* RadiographySceneBuilder::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry)
-  {
-    return dynamic_cast<RadiographyDicomLayer*>(&(scene_.LoadDicomImage(dicomImage_.release(), instanceId, frame, dicomFrameConverter_.release(), preferredPhotometricDisplayMode_, geometry)));
-  }
-
-
-  RadiographyDicomLayer* RadiographySceneReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry)
-  {
-    return dynamic_cast<RadiographyDicomLayer*>(&(scene_.LoadDicomFrame(orthancApiClient_, instanceId, frame, false, geometry)));
-  }
-
-  RadiographyDicomLayer* RadiographySceneGeometryReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry)
-  {
-    std::unique_ptr<RadiographyPlaceholderLayer>  layer(new RadiographyPlaceholderLayer(scene_));
-    layer->SetGeometry(*geometry);
-    layer->SetSize(dicomImageWidth_, dicomImageHeight_);
-    scene_.RegisterLayer(layer.get());
-
-    return layer.release();
-  }
-
-  void RadiographySceneBuilder::Read(const Json::Value& input)
-  {
-    unsigned int version = input["version"].asUInt();
-
-    if (version != 1)
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-
-    if (input.isMember("hasWindowing") && input["hasWindowing"].asBool())
-    {
-      scene_.SetWindowing(input["windowCenter"].asFloat(), input["windowWidth"].asFloat());
-    }
-
-    RadiographyDicomLayer* dicomLayer = NULL;
-    for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++)
-    {
-      const Json::Value& jsonLayer = input["layers"][(int)layerIndex];
-      RadiographyLayer::Geometry geometry;
-
-      if (jsonLayer["type"].asString() == "dicom")
-      {
-        ReadLayerGeometry(geometry, jsonLayer);
-        dicomLayer = LoadDicom(jsonLayer["instanceId"].asString(), jsonLayer["frame"].asUInt(), &geometry);
-      }
-      else if (jsonLayer["type"].asString() == "mask")
-      {
-        if (dicomLayer == NULL)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); // we always assumed the dicom layer was read before the mask
-        }
-        ReadLayerGeometry(geometry, jsonLayer);
-
-        float foreground = jsonLayer["foreground"].asFloat();
-        std::vector<Orthanc::ImageProcessing::ImagePoint> corners;
-        for (size_t i = 0; i < jsonLayer["corners"].size(); i++)
-        {
-          Orthanc::ImageProcessing::ImagePoint corner(jsonLayer["corners"][(int)i]["x"].asInt(),
-              jsonLayer["corners"][(int)i]["y"].asInt());
-          corners.push_back(corner);
-        }
-
-        scene_.LoadMask(corners, *dicomLayer, foreground, &geometry);
-      }
-      else if (jsonLayer["type"].asString() == "text")
-      {
-        ReadLayerGeometry(geometry, jsonLayer);
-        scene_.LoadText(jsonLayer["text"].asString(), jsonLayer["font"].asString(), jsonLayer["fontSize"].asUInt(), static_cast<uint8_t>(jsonLayer["foreground"].asUInt()), &geometry, false);
-      }
-      else if (jsonLayer["type"].asString() == "alpha")
-      {
-        ReadLayerGeometry(geometry, jsonLayer);
-
-        const std::string& pngContentBase64 = jsonLayer["content"].asString();
-        std::string pngContent;
-        std::string mimeType;
-        Orthanc::Toolbox::DecodeDataUriScheme(mimeType, pngContent, pngContentBase64);
-
-        std::unique_ptr<Orthanc::ImageAccessor>  image;
-        if (mimeType == "image/png")
-        {
-          image.reset(new Orthanc::PngReader());
-          dynamic_cast<Orthanc::PngReader*>(image.get())->ReadFromMemory(pngContent);
-        }
-        else
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-
-        RadiographyAlphaLayer& layer = dynamic_cast<RadiographyAlphaLayer&>(scene_.LoadAlphaBitmap(image.release(), &geometry));
-
-        if (!jsonLayer["isUsingWindowing"].asBool())
-        {
-          layer.SetForegroundValue((float)(jsonLayer["foreground"].asDouble()));
-        }
-      }
-      else
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-    }
-  }
-
-
-
-
-  void RadiographySceneBuilder::ReadDicomLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input)
-  {
-    for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++)
-    {
-      const Json::Value& jsonLayer = input["layers"][(int)layerIndex];
-      if (jsonLayer["type"].asString() == "dicom")
-      {
-        ReadLayerGeometry(geometry, jsonLayer);
-        return;
-      }
-    }
-  }
-
-  void RadiographySceneBuilder::ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& jsonLayer)
-  {
-    {// crop
-      unsigned int x, y, width, height;
-      if (jsonLayer["crop"]["hasCrop"].asBool())
-      {
-        x = jsonLayer["crop"]["x"].asUInt();
-        y = jsonLayer["crop"]["y"].asUInt();
-        width = jsonLayer["crop"]["width"].asUInt();
-        height = jsonLayer["crop"]["height"].asUInt();
-        geometry.SetCrop(x, y, width, height);
-      }
-    }
-
-    geometry.SetAngle(jsonLayer["angle"].asDouble());
-    geometry.SetResizeable(jsonLayer["isResizable"].asBool());
-    geometry.SetPan(jsonLayer["pan"]["x"].asDouble(), jsonLayer["pan"]["y"].asDouble());
-    geometry.SetPixelSpacing(jsonLayer["pixelSpacing"]["x"].asDouble(), jsonLayer["pixelSpacing"]["y"].asDouble());
-
-    // these fields were introduced later -> they might not exist
-    if (jsonLayer.isMember("flipVertical"))
-    {
-      geometry.SetFlipVertical(jsonLayer["flipVertical"].asBool());
-    }
-    if (jsonLayer.isMember("flipHorizontal"))
-    {
-      geometry.SetFlipHorizontal(jsonLayer["flipHorizontal"].asBool());
-    }
-
-  }
-}
--- a/Framework/Radiography/RadiographySceneReader.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-/**
- * 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 "RadiographyScene.h"
-#include "RadiographyAlphaLayer.h"
-#include "RadiographyDicomLayer.h"
-#include "RadiographyMaskLayer.h"
-#include "RadiographyTextLayer.h"
-#include "../Deprecated/Toolbox/OrthancApiClient.h"
-
-#include <json/value.h>
-#include <Core/Images/FontRegistry.h>
-
-namespace OrthancStone
-{
-  // a layer containing only the geometry of a DICOM layer (bit hacky !)
-  class RadiographyPlaceholderLayer : public RadiographyDicomLayer
-  {
-  public:
-    RadiographyPlaceholderLayer(const RadiographyScene& scene) :
-      RadiographyDicomLayer(scene)
-    {
-    }
-
-  };
-
-
-  // HACK: I had to introduce this builder class in order to be able to recreate a RadiographyScene
-  // from a serialized scene that is passed to web-workers.
-  // It needs some architecturing...
-  class RadiographySceneBuilder : public boost::noncopyable
-  {
-  protected:
-    RadiographyScene&                               scene_;
-    std::unique_ptr<Orthanc::ImageAccessor>           dicomImage_;
-    std::unique_ptr<Deprecated::DicomFrameConverter>  dicomFrameConverter_;
-    RadiographyPhotometricDisplayMode               preferredPhotometricDisplayMode_;
-
-  public:
-    RadiographySceneBuilder(RadiographyScene& scene) :
-      scene_(scene)
-    {
-    }
-
-    void Read(const Json::Value& input);
-    void Read(const Json::Value& input,
-              Orthanc::ImageAccessor* dicomImage, // takes ownership
-              Deprecated::DicomFrameConverter* dicomFrameConverter, // takes ownership
-              RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode
-              );
-
-    static void ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input);
-    static void ReadDicomLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input);
-
-  protected:
-    virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry);
-
-  };
-
-
-  class RadiographySceneReader : public RadiographySceneBuilder
-  {
-    Deprecated::OrthancApiClient&             orthancApiClient_;
-
-  public:
-    RadiographySceneReader(RadiographyScene& scene, Deprecated::OrthancApiClient& orthancApiClient) :
-      RadiographySceneBuilder(scene),
-      orthancApiClient_(orthancApiClient)
-    {
-    }
-
-  protected:
-    virtual RadiographyDicomLayer*  LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry);
-  };
-
-  // reads the whole scene but the DICOM image such that we have the full geometry
-  class RadiographySceneGeometryReader : public RadiographySceneBuilder
-  {
-    unsigned int dicomImageWidth_;
-    unsigned int dicomImageHeight_;
-
-  public:
-    RadiographySceneGeometryReader(RadiographyScene& scene, unsigned int dicomImageWidth, unsigned int dicomImageHeight) :
-      RadiographySceneBuilder(scene),
-      dicomImageWidth_(dicomImageWidth),
-      dicomImageHeight_(dicomImageHeight)
-    {
-    }
-
-  protected:
-    virtual RadiographyDicomLayer*  LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry);
-  };
-}
--- a/Framework/Radiography/RadiographySceneWriter.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,168 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographySceneWriter.h"
-
-#include <Core/OrthancException.h>
-#include <Core/Images/PngWriter.h>
-#include <Core/Toolbox.h>
-
-namespace OrthancStone
-{
-  void RadiographySceneWriter::Write(Json::Value& output, const RadiographyScene& scene)
-  {
-    output["version"] = 1;
-    float windowCenter, windowWidth;
-    bool hasWindowing = scene.GetWindowing(windowCenter, windowWidth);
-    output["hasWindowing"] = hasWindowing;
-    if (hasWindowing)
-    {
-      output["windowCenter"] = windowCenter;
-      output["windowWidth"] = windowWidth;
-    }
-    output["layers"] = Json::arrayValue;
-
-    std::vector<size_t> layersIndexes;
-    scene.GetLayersIndexes(layersIndexes);
-
-    for (std::vector<size_t>::iterator itLayerIndex = layersIndexes.begin(); itLayerIndex < layersIndexes.end(); itLayerIndex++)
-    {
-      Json::Value layer;
-      WriteLayer(layer, scene.GetLayer(*itLayerIndex));
-      output["layers"].append(layer);
-    }
-  }
-
-  void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyDicomLayer& layer)
-  {
-    output["type"] = "dicom";
-    output["instanceId"] = layer.GetInstanceId();
-    output["frame"] = layer.GetFrame();
-  }
-
-  void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyTextLayer& layer)
-  {
-    output["type"] = "text";
-    output["text"] = layer.GetText();
-    output["font"] = layer.GetFont();
-    output["fontSize"] = layer.GetFontSize();
-    output["foreground"] = layer.GetForegroundGreyLevel();
-  }
-
-  void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyMaskLayer& layer)
-  {
-    output["type"] = "mask";
-    output["instanceId"] = layer.GetInstanceId(); // the dicom layer it's being linked to
-    output["foreground"] = layer.GetForeground();
-    output["corners"] = Json::arrayValue;
-    const std::vector<Orthanc::ImageProcessing::ImagePoint>& corners = layer.GetCorners();
-    for (size_t i = 0; i < corners.size(); i++)
-    {
-      Json::Value corner;
-      corner["x"] = corners[i].GetX();
-      corner["y"] = corners[i].GetY();
-      output["corners"].append(corner);
-    }
-  }
-
-  void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyAlphaLayer& layer)
-  {
-    output["type"] = "alpha";
-
-    //output["bitmap"] =
-    const Orthanc::ImageAccessor& alpha = layer.GetAlpha();
-
-    Orthanc::PngWriter pngWriter;
-    std::string pngContent;
-    std::string pngContentBase64;
-    pngWriter.WriteToMemory(pngContent, alpha);
-
-    Orthanc::Toolbox::EncodeDataUriScheme(pngContentBase64, "image/png", pngContent);
-    output["content"] = pngContentBase64;
-    output["foreground"] = layer.GetForegroundValue();
-  }
-
-  void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyLayer& layer)
-  {
-    const RadiographyLayer::Geometry& geometry = layer.GetGeometry();
-
-    {// crop
-      Json::Value crop;
-      if (geometry.HasCrop())
-      {
-        unsigned int x, y, width, height;
-        geometry.GetCrop(x, y, width, height);
-        crop["hasCrop"] = true;
-        crop["x"] = x;
-        crop["y"] = y;
-        crop["width"] = width;
-        crop["height"] = height;
-      }
-      else
-      {
-        crop["hasCrop"] = false;
-      }
-
-      output["crop"] = crop;
-    }
-
-    output["angle"] = geometry.GetAngle();
-    output["isResizable"] = geometry.IsResizeable();
-
-    {// pan
-      Json::Value pan;
-      pan["x"] = geometry.GetPanX();
-      pan["y"] = geometry.GetPanY();
-      output["pan"] = pan;
-    }
-
-    {// pixelSpacing
-      Json::Value pan;
-      pan["x"] = geometry.GetPixelSpacingX();
-      pan["y"] = geometry.GetPixelSpacingY();
-      output["pixelSpacing"] = pan;
-    }
-
-    output["flipVertical"] = geometry.GetFlipVertical();
-    output["flipHorizontal"] = geometry.GetFlipHorizontal();
-
-    if (dynamic_cast<const RadiographyTextLayer*>(&layer) != NULL)
-    {
-      WriteLayer(output, dynamic_cast<const RadiographyTextLayer&>(layer));
-    }
-    else if (dynamic_cast<const RadiographyDicomLayer*>(&layer) != NULL)
-    {
-      WriteLayer(output, dynamic_cast<const RadiographyDicomLayer&>(layer));
-    }
-    else if (dynamic_cast<const RadiographyAlphaLayer*>(&layer) != NULL)
-    {
-      WriteLayer(output, dynamic_cast<const RadiographyAlphaLayer&>(layer));
-    }
-    else if (dynamic_cast<const RadiographyMaskLayer*>(&layer) != NULL)
-    {
-      WriteLayer(output, dynamic_cast<const RadiographyMaskLayer&>(layer));
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-    }
-  }
-}
--- a/Framework/Radiography/RadiographySceneWriter.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/**
- * 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 "RadiographyScene.h"
-#include "RadiographyAlphaLayer.h"
-#include "RadiographyDicomLayer.h"
-#include "RadiographyTextLayer.h"
-#include "RadiographyMaskLayer.h"
-#include <json/value.h>
-
-namespace OrthancStone
-{
-  class RadiographyScene;
-
-  class RadiographySceneWriter : public boost::noncopyable
-  {
-
-  public:
-    RadiographySceneWriter()
-    {
-    }
-
-    void Write(Json::Value& output, const RadiographyScene& scene);
-
-  private:
-    void WriteLayer(Json::Value& output, const RadiographyLayer& layer);
-    void WriteLayer(Json::Value& output, const RadiographyDicomLayer& layer);
-    void WriteLayer(Json::Value& output, const RadiographyTextLayer& layer);
-    void WriteLayer(Json::Value& output, const RadiographyAlphaLayer& layer);
-    void WriteLayer(Json::Value& output, const RadiographyMaskLayer& layer);
-  };
-}
--- a/Framework/Radiography/RadiographyTextLayer.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "RadiographyTextLayer.h"
-
-#include "Core/OrthancException.h"
-#include "RadiographyScene.h"
-#include "../Toolbox/TextRenderer.h"
-
-namespace OrthancStone
-{
-  std::map<std::string, Orthanc::EmbeddedResources::FileResourceId> RadiographyTextLayer::fonts_;
-
-  void RadiographyTextLayer::SetText(const std::string& utf8,
-                                     const std::string& font,
-                                     unsigned int fontSize,
-                                     uint8_t foregroundGreyLevel)
-  {
-    if (fonts_.find(font) == fonts_.end())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, "The font has not been registered");
-    }
-
-    text_ = utf8;
-    font_ = font;
-    fontSize_ = fontSize;
-    foregroundGreyLevel_ = foregroundGreyLevel;
-
-    SetAlpha(TextRenderer::Render(fonts_[font_],
-                                  fontSize_,
-                                  text_));
-
-    SetForegroundValue(foregroundGreyLevel * 256.0f);
-  }
-
-}
--- a/Framework/Radiography/RadiographyTextLayer.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/**
- * 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 "RadiographyAlphaLayer.h"
-
-namespace OrthancStone
-{
-  class RadiographyScene;
-
-  class RadiographyTextLayer : public RadiographyAlphaLayer
-  {
-  private:
-    std::string                 text_;
-    std::string                 font_;
-    unsigned int                fontSize_;
-    uint8_t                     foregroundGreyLevel_;
-
-    static std::map<std::string, Orthanc::EmbeddedResources::FileResourceId>  fonts_;
-  public:
-    RadiographyTextLayer(const RadiographyScene& scene) :
-      RadiographyAlphaLayer(scene)
-    {
-    }
-
-    void SetText(const std::string& utf8, const std::string& font, unsigned int fontSize, uint8_t foregroundGreyLevel);
-
-    const std::string& GetText() const
-    {
-      return text_;
-    }
-
-    const std::string& GetFont() const
-    {
-      return font_;
-    }
-
-    unsigned int GetFontSize() const
-    {
-      return fontSize_;
-    }
-
-    uint8_t GetForegroundGreyLevel() const
-    {
-      return foregroundGreyLevel_;
-    }
-
-    static void RegisterFont(const std::string& name, Orthanc::EmbeddedResources::FileResourceId fontResourceId)
-    {
-      fonts_[name] = fontResourceId;
-    }
-  };
-}
--- a/Framework/Radiography/RadiographyWidget.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,284 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographyWidget.h"
-
-#include <Core/OrthancException.h>
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-
-#include "RadiographyMaskLayer.h"
-
-namespace OrthancStone
-{
-
-  bool RadiographyWidget::IsInvertedInternal() const
-  {
-    // MONOCHROME1 images must be inverted and the user can invert the 
-    // image, too -> XOR the two
-    return (scene_->GetPreferredPhotomotricDisplayMode() == 
-            RadiographyPhotometricDisplayMode_Monochrome1) ^ invert_; 
-  }
-
-  void RadiographyWidget::RenderBackground(
-    Orthanc::ImageAccessor& image, float minValue, float maxValue)
-  {
-    // wipe background before rendering
-    float backgroundValue = minValue;
-
-    switch (scene_->GetPreferredPhotomotricDisplayMode())
-    {
-    case RadiographyPhotometricDisplayMode_Monochrome1:
-    case RadiographyPhotometricDisplayMode_Default:
-      if (IsInvertedInternal())
-        backgroundValue = maxValue;
-      else
-        backgroundValue = minValue;
-      break;
-    case RadiographyPhotometricDisplayMode_Monochrome2:
-      if (IsInvertedInternal())
-        backgroundValue = minValue;
-      else
-        backgroundValue = maxValue;
-      break;
-    default:
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-    }
-
-    Orthanc::ImageProcessing::Set(image, static_cast<int64_t>(backgroundValue));
-  }
-
-  bool RadiographyWidget::RenderInternal(unsigned int width,
-                                         unsigned int height,
-                                         ImageInterpolation interpolation)
-  {
-    if (floatBuffer_.get() == NULL ||
-        floatBuffer_->GetWidth() != width ||
-        floatBuffer_->GetHeight() != height)
-    {
-      floatBuffer_.reset(new Orthanc::Image(
-        Orthanc::PixelFormat_Float32, width, height, false));
-
-      if (floatBuffer_.get() == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "RadiographyWidget::RenderInternal: unable to allocate float buffer");
-      }
-    }
-
-    if (cairoBuffer_.get() == NULL ||
-        cairoBuffer_->GetWidth() != width ||
-        cairoBuffer_->GetHeight() != height)
-    {
-      cairoBuffer_.reset(new CairoSurface(width, height, false /* no alpha */));
-
-      if (cairoBuffer_.get() == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "RadiographyWidget::RenderInternal: unable to allocate cairo buffer");
-      }
-    }
-
-    RenderBackground(*floatBuffer_, 0.0, 65535.0);
-
-    scene_->Render(*floatBuffer_, GetView().GetMatrix(), interpolation, true);
-
-    // Conversion from Float32 to BGRA32 (cairo). Very similar to
-    // GrayscaleFrameRenderer => TODO MERGE?
-    Orthanc::ImageAccessor target;
-    cairoBuffer_->GetWriteableAccessor(target);
-
-    bool invert = IsInvertedInternal();
-
-    for (unsigned int y = 0; y < height; y++)
-    {
-      const float* p = reinterpret_cast<const float*>(floatBuffer_->GetConstRow(y));
-      uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
-
-      for (unsigned int x = 0; x < width; x++, p++, q += 4)
-      {
-        uint8_t v = 0;
-        if (*p >= 65535.0)
-        {
-          v = 255;
-        }
-        else if (*p <= 0.0)
-        {
-          v = 0;
-        }
-        else
-        {
-          v = static_cast<uint8_t>(*p / 256.0);
-        }
-
-        if (invert)
-        {
-          v = 255 - v;
-        }
-
-        q[0] = v;
-        q[1] = v;
-        q[2] = v;
-        q[3] = 255;
-      }
-    }
-
-    return true;
-  }
-
-
-  bool RadiographyWidget::RenderScene(CairoContext& context,
-                                      const Deprecated::ViewportGeometry& view)
-  {
-    cairo_t* cr = context.GetObject();
-
-    if (RenderInternal(context.GetWidth(), context.GetHeight(), interpolation_))
-    {
-      // https://www.cairographics.org/FAQ/#paint_from_a_surface
-      cairo_save(cr);
-      cairo_identity_matrix(cr);
-      cairo_set_source_surface(cr, cairoBuffer_->GetObject(), 0, 0);
-      cairo_paint(cr);
-      cairo_restore(cr);
-    }
-    else
-    {
-      // https://www.cairographics.org/FAQ/#clear_a_surface
-      context.SetSourceColor(0, 0, 0);
-      cairo_paint(cr);
-    }
-
-    if (hasSelection_)
-    {
-      scene_->DrawBorder(
-        context, static_cast<unsigned int>(selectedLayer_), view.GetZoom());
-    }
-
-    return true;
-  }
-
-
-  RadiographyWidget::RadiographyWidget(boost::shared_ptr<RadiographyScene> scene,
-                                       const std::string& name) :
-    WorldSceneWidget(name),
-    invert_(false),
-    interpolation_(ImageInterpolation_Nearest),
-    hasSelection_(false),
-    selectedLayer_(0)    // Dummy initialization
-  {
-    SetScene(scene);
-  }
-
-
-  void RadiographyWidget::Select(size_t layer)
-  {
-    hasSelection_ = true;
-    selectedLayer_ = layer;
-
-    NotifyContentChanged();
-    BroadcastMessage(SelectionChangedMessage(*this));
-  }
-
-  void RadiographyWidget::Unselect()
-  {
-    hasSelection_ = false;
-
-    NotifyContentChanged();
-    BroadcastMessage(SelectionChangedMessage(*this));
-  }
-
-  bool RadiographyWidget::LookupSelectedLayer(size_t& layer) const
-  {
-    if (hasSelection_)
-    {
-      layer = selectedLayer_;
-      return true;
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-  
-  void RadiographyWidget::OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message)
-  {
-//    LOG(INFO) << "Scene geometry has changed";
-    FitContent();
-  }
-
-  
-  void RadiographyWidget::OnContentChanged(const RadiographyScene::ContentChangedMessage& message)
-  {
-//    LOG(INFO) << "Scene content has changed";
-    NotifyContentChanged();
-  }
-
-  void RadiographyWidget::OnLayerRemoved(const RadiographyScene::LayerRemovedMessage& message)
-  {
-    size_t removedLayerIndex = message.GetLayerIndex();
-    if (hasSelection_ && selectedLayer_ == removedLayerIndex)
-    {
-      Unselect();
-    }
-    NotifyContentChanged();
-  }
-  
-  void RadiographyWidget::SetInvert(bool invert)
-  {
-    if (invert_ != invert)
-    {
-      invert_ = invert;
-      NotifyContentChanged();
-    }
-  }
-
-  
-    void RadiographyWidget::SwitchInvert()
-  {
-    invert_ = !invert_;
-    NotifyContentChanged();
-  }
-
-
-  void RadiographyWidget::SetInterpolation(ImageInterpolation interpolation)
-  {
-    if (interpolation_ != interpolation)
-    {
-      interpolation_ = interpolation;
-      NotifyContentChanged();
-    }
-  }
-
-  void RadiographyWidget::SetScene(boost::shared_ptr<RadiographyScene> scene)
-  {
-    scene_ = scene;
-
-    Register<RadiographyScene::GeometryChangedMessage>(*scene_, &RadiographyWidget::OnGeometryChanged);
-    Register<RadiographyScene::ContentChangedMessage>(*scene_, &RadiographyWidget::OnContentChanged);
-    Register<RadiographyScene::LayerRemovedMessage>(*scene_, &RadiographyWidget::OnLayerRemoved);
-
-    Unselect();
-
-    NotifyContentChanged();
-
-    // force redraw
-    FitContent();
-  }
-}
--- a/Framework/Radiography/RadiographyWidget.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-/**
- * 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 "../Deprecated/Widgets/WorldSceneWidget.h"
-#include "../Messages/ObserverBase.h"
-#include "RadiographyScene.h"
-
-
-namespace OrthancStone
-{
-  class RadiographyMaskLayer;
-
-  class RadiographyWidget :
-    public Deprecated::WorldSceneWidget,
-    public ObserverBase<RadiographyWidget>,
-    public IObservable
-  {
-  public:
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SelectionChangedMessage, RadiographyWidget);
-
-  private:
-    boost::shared_ptr<RadiographyScene>    scene_;
-    std::unique_ptr<Orthanc::ImageAccessor>  floatBuffer_;
-    std::unique_ptr<CairoSurface>            cairoBuffer_;
-    bool                                   invert_;
-    ImageInterpolation                     interpolation_;
-    bool                                   hasSelection_;
-    size_t                                 selectedLayer_;
-
-    bool RenderInternal(unsigned int width,
-                        unsigned int height,
-                        ImageInterpolation interpolation);
-
-  protected:
-    virtual Extent2D GetSceneExtent()
-    {
-      return scene_->GetSceneExtent(false);
-    }
-
-    virtual bool RenderScene(CairoContext& context,
-                             const Deprecated::ViewportGeometry& view);
-
-    virtual void RenderBackground(Orthanc::ImageAccessor& image, float minValue, float maxValue);
-
-    bool IsInvertedInternal() const;
-
-  public:
-    RadiographyWidget(boost::shared_ptr<RadiographyScene> scene,  // TODO: check how we can avoid boost::shared_ptr here since we don't want them in the public API (app is keeping a boost::shared_ptr to this right now)
-                      const std::string& name);
-
-    RadiographyScene& GetScene() const
-    {
-      return *scene_;
-    }
-
-    void SetScene(boost::shared_ptr<RadiographyScene> scene);
-
-    void Select(size_t layer);
-
-    void Unselect();
-
-    template<typename LayerType> bool SelectLayerByType(size_t index = 0);
-
-    bool LookupSelectedLayer(size_t& layer) const;
-
-    void OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message);
-
-    void OnContentChanged(const RadiographyScene::ContentChangedMessage& message);
-
-    void OnLayerRemoved(const RadiographyScene::LayerRemovedMessage& message);
-
-    void SetInvert(bool invert);
-
-    void SwitchInvert();
-
-    bool IsInverted() const
-    {
-      return invert_;
-    }
-
-    void SetInterpolation(ImageInterpolation interpolation);
-
-    ImageInterpolation GetInterpolation() const
-    {
-      return interpolation_;
-    }
-  };
-
-  template<typename LayerType> bool RadiographyWidget::SelectLayerByType(size_t index)
-  {
-    std::vector<size_t> layerIndexes;
-    size_t count = 0;
-    scene_->GetLayersIndexes(layerIndexes);
-
-    for (size_t i = 0; i < layerIndexes.size(); ++i)
-    {
-      const LayerType* typedLayer = dynamic_cast<const LayerType*>(&(scene_->GetLayer(layerIndexes[i])));
-      if (typedLayer != NULL)
-      {
-        if (count == index)
-        {
-          Select(layerIndexes[i]);
-          return true;
-        }
-        count++;
-      }
-    }
-
-    return false;
-  }
-}
--- a/Framework/Radiography/RadiographyWindowingTracker.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,192 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "RadiographyWindowingTracker.h"
-#include "RadiographyWidget.h"
-
-#include <Core/OrthancException.h>
-
-
-namespace OrthancStone
-{
-  class RadiographyWindowingTracker::UndoRedoCommand : public UndoRedoStack::ICommand
-  {
-  private:
-    RadiographyScene&  scene_;
-    float              sourceCenter_;
-    float              sourceWidth_;
-    float              targetCenter_;
-    float              targetWidth_;
-
-  public:
-    UndoRedoCommand(const RadiographyWindowingTracker& tracker) :
-      scene_(tracker.scene_),
-      sourceCenter_(tracker.sourceCenter_),
-      sourceWidth_(tracker.sourceWidth_)
-    {
-      scene_.GetWindowingWithDefault(targetCenter_, targetWidth_);
-    }
-
-    virtual void Undo() const
-    {
-      scene_.SetWindowing(sourceCenter_, sourceWidth_);
-    }
-      
-    virtual void Redo() const
-    {
-      scene_.SetWindowing(targetCenter_, targetWidth_);
-    }
-  };
-
-
-  void RadiographyWindowingTracker::ComputeAxisEffect(int& deltaCenter,
-                                                      int& deltaWidth,
-                                                      int delta,
-                                                      Action actionNegative,
-                                                      Action actionPositive)
-  {
-    if (delta < 0)
-    {
-      switch (actionNegative)
-      {
-        case Action_IncreaseWidth:
-          deltaWidth = -delta;
-          break;
-
-        case Action_DecreaseWidth:
-          deltaWidth = delta;
-          break;
-
-        case Action_IncreaseCenter:
-          deltaCenter = -delta;
-          break;
-
-        case Action_DecreaseCenter:
-          deltaCenter = delta;
-          break;
-
-        default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-    }
-    else if (delta > 0)
-    {
-      switch (actionPositive)
-      {
-        case Action_IncreaseWidth:
-          deltaWidth = delta;
-          break;
-
-        case Action_DecreaseWidth:
-          deltaWidth = -delta;
-          break;
-
-        case Action_IncreaseCenter:
-          deltaCenter = delta;
-          break;
-
-        case Action_DecreaseCenter:
-          deltaCenter = -delta;
-          break;
-
-        default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-    }
-  }
-    
-    
-  RadiographyWindowingTracker::RadiographyWindowingTracker(UndoRedoStack& undoRedoStack,
-                                                           RadiographyScene& scene,
-                                                           RadiographyWidget& widget,
-                                                           ImageInterpolation interpolationDuringTracking,
-                                                           int x,
-                                                           int y,
-                                                           Action leftAction,
-                                                           Action rightAction,
-                                                           Action upAction,
-                                                           Action downAction) :
-    undoRedoStack_(undoRedoStack),
-    scene_(scene),
-    widget_(widget),
-    initialWidgetInterpolation_(widget.GetInterpolation()),
-    clickX_(x),
-    clickY_(y),
-    leftAction_(leftAction),
-    rightAction_(rightAction),
-    upAction_(upAction),
-    downAction_(downAction)
-  {
-    scene_.GetWindowingWithDefault(sourceCenter_, sourceWidth_);
-    widget_.SetInterpolation(interpolationDuringTracking);
-
-    float minValue, maxValue;
-    scene.GetRange(minValue, maxValue);
-
-    assert(minValue <= maxValue);
-
-    float delta = (maxValue - minValue);
-    strength_ = delta / 1000.0f; // 1px move will change the ww/wc by 0.1%
-
-    if (strength_ < 1)
-    {
-      strength_ = 1;
-    }
-  }
-
-
-  void RadiographyWindowingTracker::Render(CairoContext& context,
-                                           double zoom)
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-  }
-  
-
-  void RadiographyWindowingTracker::MouseUp()
-  {
-    widget_.SetInterpolation(initialWidgetInterpolation_);
-    undoRedoStack_.Add(new UndoRedoCommand(*this));
-  }
-
-
-  void RadiographyWindowingTracker::MouseMove(int displayX,
-                                              int displayY,
-                                              double sceneX,
-                                              double sceneY,
-                                              const std::vector<Deprecated::Touch>& displayTouches,
-                                              const std::vector<Deprecated::Touch>& sceneTouches)
-  {
-    // This follows the behavior of the Osimis Web viewer:
-    // https://bitbucket.org/osimis/osimis-webviewer-plugin/src/master/frontend/src/app/viewport/image-plugins/windowing-viewport-tool.class.js
-
-    static const float SCALE = 1.0;
-      
-    int deltaCenter = 0;
-    int deltaWidth = 0;
-
-    ComputeAxisEffect(deltaCenter, deltaWidth, displayX - clickX_, leftAction_, rightAction_);
-    ComputeAxisEffect(deltaCenter, deltaWidth, displayY - clickY_, upAction_, downAction_);
-
-    float newCenter = sourceCenter_ + (deltaCenter / SCALE * strength_);
-    float newWidth = sourceWidth_ + (deltaWidth / SCALE * strength_);
-    scene_.SetWindowing(newCenter, newWidth);
-  }
-}
--- a/Framework/Radiography/RadiographyWindowingTracker.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-/**
- * 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 "../Toolbox/UndoRedoStack.h"
-#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
-#include "RadiographyScene.h"
-
-namespace OrthancStone
-{
-
-  class RadiographyWidget;
-
-  class RadiographyWindowingTracker : public Deprecated::IWorldSceneMouseTracker
-  {   
-  public:
-    enum Action
-    {
-      Action_IncreaseWidth,
-      Action_DecreaseWidth,
-      Action_IncreaseCenter,
-      Action_DecreaseCenter
-    };
-    
-  private:
-    class UndoRedoCommand;
-
-    UndoRedoStack&      undoRedoStack_;
-    RadiographyScene&   scene_;
-    RadiographyWidget&  widget_;
-    ImageInterpolation  initialWidgetInterpolation_;
-    int                 clickX_;
-    int                 clickY_;
-    Action              leftAction_;
-    Action              rightAction_;
-    Action              upAction_;
-    Action              downAction_;
-    float               strength_;
-    float               sourceCenter_;
-    float               sourceWidth_;
-
-    static void ComputeAxisEffect(int& deltaCenter,
-                                  int& deltaWidth,
-                                  int delta,
-                                  Action actionNegative,
-                                  Action actionPositive);    
-    
-  public:
-    RadiographyWindowingTracker(UndoRedoStack& undoRedoStack,
-                                RadiographyScene& scene,
-                                RadiographyWidget& widget,
-                                ImageInterpolation interpolationDuringTracking,
-                                int x,
-                                int y,
-                                Action leftAction,
-                                Action rightAction,
-                                Action upAction,
-                                Action downAction);
-    
-    virtual bool HasRender() const
-    {
-      return false;
-    }
-
-    virtual void Render(CairoContext& context,
-                        double zoom);
-
-    virtual void MouseUp();
-
-    virtual void MouseMove(int displayX,
-                           int displayY,
-                           double sceneX,
-                           double sceneY,
-                           const std::vector<Deprecated::Touch>& displayTouches,
-                           const std::vector<Deprecated::Touch>& sceneTouches);
-  };
-}
--- a/Framework/StoneInitialization.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ b/Framework/StoneInitialization.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -25,10 +25,6 @@
 #  error Macro ORTHANC_ENABLE_SDL must be defined
 #endif
 
-#if !defined(ORTHANC_ENABLE_QT)
-#  error Macro ORTHANC_ENABLE_QT must be defined
-#endif
-
 #if !defined(ORTHANC_ENABLE_SSL)
 #  error Macro ORTHANC_ENABLE_SSL must be defined
 #endif
@@ -48,10 +44,6 @@
 #  include "Viewport/SdlWindow.h"
 #endif
 
-#if ORTHANC_ENABLE_QT == 1
-#  include <QCoreApplication>
-#endif
-
 #if ORTHANC_ENABLE_CURL == 1
 #  include <Core/HttpClient.h>
 #endif
@@ -122,14 +114,6 @@
       // Run-time checks of locale settings, to be run after Qt has
       // been initialized, as Qt changes locale settings
 
-#if ORTHANC_ENABLE_QT == 1
-      if (QCoreApplication::instance() == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
-                                        "Qt must be initialized before Stone");
-      }
-#endif
-      
       {
         OrthancStone::Vector v;
         if (!OrthancStone::LinearAlgebra::ParseVector(v, "1.3671875\\-1.3671875") ||
--- a/Platforms/Generic/DelayedCallCommand.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "DelayedCallCommand.h"
-#include "boost/thread/thread.hpp"
-
-#include <iostream>
-
-namespace Deprecated
-{
-  DelayedCallCommand::DelayedCallCommand(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,  // takes ownership
-                                         unsigned int timeoutInMs,
-                                         Orthanc::IDynamicObject* payload /* takes ownership */,
-                                         OrthancStone::NativeStoneApplicationContext& context
-                                         ) :
-    callback_(callback),
-    payload_(payload),
-    context_(context),
-    expirationTimePoint_(boost::posix_time::microsec_clock::local_time() + boost::posix_time::milliseconds(timeoutInMs)),
-    timeoutInMs_(timeoutInMs)
-  {
-  }
-
-
-  void DelayedCallCommand::Execute()
-  {
-    while (boost::posix_time::microsec_clock::local_time() < expirationTimePoint_)
-    {
-      boost::this_thread::sleep(boost::posix_time::milliseconds(1));
-    }
-  }
-
-  void DelayedCallCommand::Commit()
-  {
-    // We want to make sure that, i.e, the UpdateThread is not
-    // triggered while we are updating the "model" with the result of
-    // an OracleCommand
-    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_);
-
-    if (callback_.get() != NULL)
-    {
-      IDelayedCallExecutor::TimeoutMessage message; // TODO: add payload
-      callback_->Apply(message);
-    }
-  }
-}
--- a/Platforms/Generic/DelayedCallCommand.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/**
- * 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 "IOracleCommand.h"
-
-#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
-#include "../../Framework/Messages/IObservable.h"
-#include "../../Framework/Messages/ICallable.h"
-#include "../../Applications/Generic/NativeStoneApplicationContext.h"
-
-#include <boost/date_time/posix_time/posix_time.hpp>
-
-namespace Deprecated
-{
-  class DelayedCallCommand : public IOracleCommand, OrthancStone::IObservable
-  {
-  protected:
-    std::unique_ptr<MessageHandler<IDelayedCallExecutor::TimeoutMessage> >  callback_;
-    std::unique_ptr<Orthanc::IDynamicObject>  payload_;
-    OrthancStone::NativeStoneApplicationContext&          context_;
-    boost::posix_time::ptime                expirationTimePoint_;
-    unsigned int                            timeoutInMs_;
-
-  public:
-    DelayedCallCommand(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,  // takes ownership
-                       unsigned int timeoutInMs,
-                       Orthanc::IDynamicObject* payload /* takes ownership */,
-                       OrthancStone::NativeStoneApplicationContext& context
-                       );
-
-    virtual void Execute();
-
-    virtual void Commit();
-  };
-
-}
--- a/Platforms/Generic/IOracleCommand.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/**
- * 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 <Core/IDynamicObject.h>
-
-namespace Deprecated
-{
-  class IOracleCommand : public Orthanc::IDynamicObject
-  {
-  public:
-    virtual ~IOracleCommand()
-    {
-    }
-
-    // This part of the command can be invoked simultaneously, and
-    // must not modify the Stone context
-    virtual void Execute() = 0;
-
-    // This part of the command must be invoked in mutual exclusion
-    virtual void Commit() = 0;
-  };
-}
--- a/Platforms/Generic/Oracle.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,212 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "Oracle.h"
-
-#include <Core/Logging.h>
-#include <Core/MultiThreading/SharedMessageQueue.h>
-#include <Core/OrthancException.h>
-
-#include <vector>
-#include <stdio.h>
-#include <boost/thread/mutex.hpp>
-
-namespace Deprecated
-{
-  class Oracle::PImpl
-  {
-  private:
-    enum State
-    {
-      State_Init,
-      State_Started,
-      State_Stopped
-    };
-
-    boost::mutex                   oracleMutex_;
-    State                          state_;
-    std::vector<boost::thread*>    threads_;
-    Orthanc::SharedMessageQueue    queue_;
-
-    static void Worker(PImpl* that)
-    {
-      for (;;)
-      {
-        State state;
-        
-        {
-          boost::mutex::scoped_lock lock(that->oracleMutex_);
-          state = that->state_;
-        }
-
-        if (state == State_Stopped)
-        {
-          break;
-        }
-
-        std::unique_ptr<Orthanc::IDynamicObject> item(that->queue_.Dequeue(100));
-        if (item.get() != NULL)
-        {
-          IOracleCommand& command = dynamic_cast<IOracleCommand&>(*item);
-          try
-          {
-            command.Execute();
-          }
-          catch (Orthanc::OrthancException& /*ex*/)
-          {
-            // this is probably a curl error that has been triggered.  We may just ignore it.
-            // The command.success_ will stay at false and this will be handled in the command.Commit
-          }
-
-          // Random sleeping to test
-          //boost::this_thread::sleep(boost::posix_time::milliseconds(50 * (1 + rand() % 10)));
-
-          command.Commit();
-        }
-      }
-    }
-    
-  public:
-    PImpl(unsigned int threadCount) :
-      state_(State_Init),
-      threads_(threadCount)
-    {
-    }
-
-    ~PImpl()
-    {
-      if (state_ == State_Started)
-      {
-        LOG(ERROR) << "You should have manually called Oracle::Stop()";
-        Stop();
-      }
-    }
-
-    Orthanc::SharedMessageQueue& GetQueue()
-    {
-      return queue_;
-    }
-
-    void Submit(IOracleCommand* command)
-    {
-      std::unique_ptr<IOracleCommand> protection(command);
-
-      if (command == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-      
-      boost::mutex::scoped_lock lock(oracleMutex_);
-
-      switch (state_)
-      {
-        case State_Init:
-        case State_Started:
-          queue_.Enqueue(protection.release());
-          break;
-
-        case State_Stopped:
-          LOG(ERROR) << "Cannot schedule a request to the Oracle after having "
-                     << "called Oracle::Stop()";
-          break;
-
-        default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-      
-    }
-
-    void Start()
-    {
-      boost::mutex::scoped_lock lock(oracleMutex_);
-
-      if (state_ != State_Init)
-      {
-        LOG(ERROR) << "Oracle::PImpl::Start: (state_ != State_Init)";
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-      
-      for (size_t i = 0; i < threads_.size(); i++)
-      {
-        threads_[i] = new boost::thread(Worker, this);
-      }
-
-      state_ = State_Started;
-    }
-
-    void Stop()
-    {
-      {
-        boost::mutex::scoped_lock lock(oracleMutex_);
-
-        if (state_ != State_Started)
-        {
-          LOG(ERROR) << "Oracle::PImpl::Stop(): (state_ != State_Started)";
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-        }
-
-        state_ = State_Stopped;
-      }
-      
-      for (size_t i = 0; i < threads_.size(); i++)
-      {
-        if (threads_[i] != NULL)
-        {
-          if (threads_[i]->joinable())
-          {
-            threads_[i]->join();
-          }
-
-          delete threads_[i];
-        }
-      }
-    }
-  };
-  
-
-  Oracle::Oracle(unsigned int threadCount) :
-    pimpl_(new PImpl(threadCount))
-  {
-  }
-
-  void Oracle::Start()
-  {
-    pimpl_->Start();
-  }
-
-
-  void Oracle::Submit(IOracleCommand* command)
-  {
-    pimpl_->Submit(command);
-  }
-     
-   
-  void Oracle::Stop()
-  {
-    pimpl_->Stop();
-  }
-
-
-  void Oracle::WaitEmpty()
-  {
-    pimpl_->GetQueue().WaitEmpty(50);
-  }
-}
--- a/Platforms/Generic/Oracle.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/**
- * 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 "IOracleCommand.h"
-
-#include <boost/shared_ptr.hpp>
-
-namespace Deprecated
-{
-  class Oracle : public boost::noncopyable
-  {
-  private:
-    class PImpl;
-  
-    boost::shared_ptr<PImpl>  pimpl_;
-
-  public:
-    Oracle(unsigned int threadCount);
-
-    void Start();
-
-    void Submit(IOracleCommand* command);
-        
-    void WaitEmpty();  // For unit tests
-
-    void Stop();
-  };
-}
--- a/Platforms/Generic/OracleDelayedCallExecutor.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/**
- * 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 "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
-#include "Oracle.h"
-#include "../../Applications/Generic/NativeStoneApplicationContext.h"
-#include "DelayedCallCommand.h"
-
-namespace Deprecated
-{
-  // The OracleTimeout executes callbacks after a delay.
-  class OracleDelayedCallExecutor : public IDelayedCallExecutor
-  {
-  private:
-    Oracle&                        oracle_;
-    OrthancStone::NativeStoneApplicationContext& context_;
-
-  public:
-    OracleDelayedCallExecutor(Oracle& oracle,
-                              OrthancStone::NativeStoneApplicationContext& context) :
-      oracle_(oracle),
-      context_(context)
-    {
-    }
-
-    virtual void Schedule(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
-                          unsigned int timeoutInMs = 1000)
-    {
-      oracle_.Submit(new DelayedCallCommand(callback, timeoutInMs, NULL, context_));
-    }
-  };
-}
--- a/Platforms/Generic/OracleWebService.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "OracleWebService.h"
-#include "../../Framework/Deprecated/Toolbox/IWebService.h"
-
-namespace Deprecated
-{
-
-
-  class OracleWebService::WebServiceCachedGetCommand : public IOracleCommand, OrthancStone::IObservable
-  {
-  protected:
-    std::unique_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> >  successCallback_;
-    std::unique_ptr<Orthanc::IDynamicObject>                                  payload_;
-    boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage>      cachedMessage_;
-    OrthancStone::NativeStoneApplicationContext&                                          context_;
-
-  public:
-    WebServiceCachedGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                               boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
-                               Orthanc::IDynamicObject* payload /* takes ownership */,
-                               OrthancStone::NativeStoneApplicationContext& context
-                               ) :
-      successCallback_(successCallback),
-      payload_(payload),
-      cachedMessage_(cachedMessage),
-      context_(context)
-    {
-    }
-
-    virtual void Execute()
-    {
-      // nothing to do, everything is in the commit
-    }
-
-    virtual void Commit()
-    {
-      // We want to make sure that, i.e, the UpdateThread is not
-      // triggered while we are updating the "model" with the result of
-      // a WebServiceCommand
-      OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_);
-
-      IWebService::HttpRequestSuccessMessage successMessage(cachedMessage_->GetUri(),
-                                                            cachedMessage_->GetAnswer(),
-                                                            cachedMessage_->GetAnswerSize(),
-                                                            cachedMessage_->GetAnswerHttpHeaders(),
-                                                            payload_.get());
-
-      successCallback_->Apply(successMessage);
-    }
-  };
-
-  void OracleWebService::NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
-                                                Orthanc::IDynamicObject* payload, // takes ownership
-                                                MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback)
-  {
-    oracle_.Submit(new WebServiceCachedGetCommand(successCallback, cachedMessage, payload, context_));
-  }
-
-
-}
--- a/Platforms/Generic/OracleWebService.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-/**
- * 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 "../../Framework/Deprecated/Toolbox/BaseWebService.h"
-#include "Oracle.h"
-#include "WebServiceGetCommand.h"
-#include "WebServicePostCommand.h"
-#include "WebServiceDeleteCommand.h"
-#include "../../Applications/Generic/NativeStoneApplicationContext.h"
-
-namespace Deprecated
-{
-  // The OracleWebService performs HTTP requests in a native environment.
-  // It uses a thread pool to handle multiple HTTP requests in a same time.
-  // It works asynchronously to mimick the behaviour of the WebService running in a WASM environment.
-  class OracleWebService : public BaseWebService
-  {
-  private:
-    Oracle&                        oracle_;
-    OrthancStone::NativeStoneApplicationContext& context_;
-    Orthanc::WebServiceParameters  parameters_;
-
-    class WebServiceCachedGetCommand;
-
-  public:
-    OracleWebService(Oracle& oracle,
-                     const Orthanc::WebServiceParameters& parameters,
-                     OrthancStone::NativeStoneApplicationContext& context) :
-      oracle_(oracle),
-      context_(context),
-      parameters_(parameters)
-    {
-    }
-
-    virtual void PostAsync(const std::string& uri,
-                           const HttpHeaders& headers,
-                           const std::string& body,
-                           Orthanc::IDynamicObject* payload, // takes ownership
-                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership
-                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL, // takes ownership
-                           unsigned int timeoutInSeconds = 60)
-    {
-      oracle_.Submit(new WebServicePostCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, body, payload, context_));
-    }
-
-    virtual void DeleteAsync(const std::string& uri,
-                             const HttpHeaders& headers,
-                             Orthanc::IDynamicObject* payload,
-                             MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
-                             MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                             unsigned int timeoutInSeconds = 60)
-    {
-      oracle_.Submit(new WebServiceDeleteCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_));
-    }
-
-  protected:
-    virtual void GetAsyncInternal(const std::string& uri,
-                                  const HttpHeaders& headers,
-                                  Orthanc::IDynamicObject* payload, // takes ownership
-                                  MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,   // takes ownership
-                                  MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,// takes ownership
-                                  unsigned int timeoutInSeconds = 60)
-    {
-      oracle_.Submit(new WebServiceGetCommand(successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_));
-    }
-
-    virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage,
-                                        Orthanc::IDynamicObject* payload, // takes ownership
-                                        MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback);
-
-  };
-}
--- a/Platforms/Generic/WebServiceCommandBase.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "WebServiceCommandBase.h"
-
-#include <Core/HttpClient.h>
-
-namespace Deprecated
-{
-  WebServiceCommandBase::WebServiceCommandBase(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
-                                               MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
-                                               const Orthanc::WebServiceParameters& parameters,
-                                               const std::string& url,
-                                               const IWebService::HttpHeaders& headers,
-                                               unsigned int timeoutInSeconds,
-                                               Orthanc::IDynamicObject* payload /* takes ownership */,
-                                               OrthancStone::NativeStoneApplicationContext& context) :
-    successCallback_(successCallback),
-    failureCallback_(failureCallback),
-    parameters_(parameters),
-    url_(url),
-    headers_(headers),
-    payload_(payload),
-    success_(false),
-    httpStatus_(Orthanc::HttpStatus_None),
-    context_(context),
-    timeoutInSeconds_(timeoutInSeconds)
-  {
-  }
-
-
-  void WebServiceCommandBase::Commit()
-  {
-    // We want to make sure that, i.e, the UpdateThread is not
-    // triggered while we are updating the "model" with the result of
-    // a WebServiceCommand
-    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); 
-
-    if (success_ && successCallback_.get() != NULL)
-    {
-      IWebService::HttpRequestSuccessMessage message
-        (url_, answer_.c_str(), answer_.size(), answerHeaders_, payload_.get());
-      successCallback_->Apply(message);
-    }
-    else if (!success_ && failureCallback_.get() != NULL)
-    {
-      IWebService::HttpRequestErrorMessage message(url_, httpStatus_, payload_.get());
-      failureCallback_->Apply(message);
-    }
-  }
-}
--- a/Platforms/Generic/WebServiceCommandBase.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/**
- * 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 "IOracleCommand.h"
-
-#include "../../Framework/Deprecated/Toolbox/IWebService.h"
-#include "../../Framework/Messages/IObservable.h"
-#include "../../Framework/Messages/ICallable.h"
-#include "../../Applications/Generic/NativeStoneApplicationContext.h"
-
-#include <Core/WebServiceParameters.h>
-
-#include <memory>
-
-namespace Deprecated
-{
-  class WebServiceCommandBase : public IOracleCommand, OrthancStone::IObservable
-  {
-  protected:
-    std::unique_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> >  successCallback_;
-    std::unique_ptr<MessageHandler<IWebService::HttpRequestErrorMessage> >    failureCallback_;
-    Orthanc::WebServiceParameters           parameters_;
-    std::string                             url_;
-    IWebService::HttpHeaders                headers_;
-    std::unique_ptr<Orthanc::IDynamicObject>  payload_;
-    bool                                    success_;
-    Orthanc::HttpStatus                     httpStatus_;
-    std::string                             answer_;
-    IWebService::HttpHeaders                answerHeaders_;
-    OrthancStone::NativeStoneApplicationContext&          context_;
-    unsigned int                            timeoutInSeconds_;
-
-  public:
-    WebServiceCommandBase(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
-                          const Orthanc::WebServiceParameters& parameters,
-                          const std::string& url,
-                          const IWebService::HttpHeaders& headers,
-                          unsigned int timeoutInSeconds,
-                          Orthanc::IDynamicObject* payload /* takes ownership */,
-                          OrthancStone::NativeStoneApplicationContext& context
-                          );
-
-    virtual void Execute() = 0;
-
-    virtual void Commit();
-  };
-
-}
--- a/Platforms/Generic/WebServiceDeleteCommand.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "WebServiceDeleteCommand.h"
-
-#include <Core/HttpClient.h>
-
-namespace Deprecated
-{
-  WebServiceDeleteCommand::WebServiceDeleteCommand(MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                                                   MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
-                                                   const Orthanc::WebServiceParameters& parameters,
-                                                   const std::string& url,
-                                                   const Deprecated::IWebService::HttpHeaders& headers,
-                                                   unsigned int timeoutInSeconds,
-                                                   Orthanc::IDynamicObject* payload /* takes ownership */,
-                                                   OrthancStone::NativeStoneApplicationContext& context) :
-    WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context)
-  {
-  }
-
-  void WebServiceDeleteCommand::Execute()
-  {
-    Orthanc::HttpClient client(parameters_, "/");
-    client.SetUrl(url_);
-    client.SetTimeout(timeoutInSeconds_);
-    client.SetMethod(Orthanc::HttpMethod_Delete);
-
-    for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
-    {
-      client.AddHeader(it->first, it->second);
-    }
-
-    success_ = client.Apply(answer_, answerHeaders_);
-    httpStatus_ = client.GetLastStatus();
-  }
-
-}
--- a/Platforms/Generic/WebServiceDeleteCommand.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/**
- * 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 "WebServiceCommandBase.h"
-
-namespace Deprecated
-{
-  class WebServiceDeleteCommand : public WebServiceCommandBase
-  {
-  public:
-    WebServiceDeleteCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                            MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
-                            const Orthanc::WebServiceParameters& parameters,
-                            const std::string& url,
-                            const IWebService::HttpHeaders& headers,
-                            unsigned int timeoutInSeconds,
-                            Orthanc::IDynamicObject* payload /* takes ownership */,
-                            OrthancStone::NativeStoneApplicationContext& context);
-
-    virtual void Execute();
-  };
-}
--- a/Platforms/Generic/WebServiceGetCommand.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "WebServiceGetCommand.h"
-
-#include <Core/HttpClient.h>
-
-namespace Deprecated
-{
-  WebServiceGetCommand::WebServiceGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                                             MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
-                                             const Orthanc::WebServiceParameters& parameters,
-                                             const std::string& url,
-                                             const IWebService::HttpHeaders& headers,
-                                             unsigned int timeoutInSeconds,
-                                             Orthanc::IDynamicObject* payload /* takes ownership */,
-                                             OrthancStone::NativeStoneApplicationContext& context) :
-    WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context)
-  {
-  }
-
-
-  void WebServiceGetCommand::Execute()
-  {
-    Orthanc::HttpClient client(parameters_, "/");
-    client.SetUrl(url_);
-    client.SetTimeout(timeoutInSeconds_);
-    client.SetMethod(Orthanc::HttpMethod_Get);
-
-    for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
-    {
-      client.AddHeader(it->first, it->second);
-    }
-
-    success_ = client.Apply(answer_, answerHeaders_);
-    httpStatus_ = client.GetLastStatus();
-  }
-
-}
--- a/Platforms/Generic/WebServiceGetCommand.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/**
- * 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 "WebServiceCommandBase.h"
-
-namespace Deprecated
-{
-  class WebServiceGetCommand : public WebServiceCommandBase
-  {
-  public:
-    WebServiceGetCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                         MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
-                         const Orthanc::WebServiceParameters& parameters,
-                         const std::string& url,
-                         const IWebService::HttpHeaders& headers,
-                         unsigned int timeoutInSeconds,
-                         Orthanc::IDynamicObject* payload /* takes ownership */,
-                         OrthancStone::NativeStoneApplicationContext& context);
-
-    virtual void Execute();
-  };
-
-}
--- a/Platforms/Generic/WebServicePostCommand.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "WebServicePostCommand.h"
-
-#include <Core/HttpClient.h>
-
-namespace Deprecated
-{
-  WebServicePostCommand::WebServicePostCommand(MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                                               MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
-                                               const Orthanc::WebServiceParameters& parameters,
-                                               const std::string& url,
-                                               const Deprecated::IWebService::HttpHeaders& headers,
-                                               unsigned int timeoutInSeconds,
-                                               const std::string& body,
-                                               Orthanc::IDynamicObject* payload /* takes ownership */,
-                                               OrthancStone::NativeStoneApplicationContext& context) :
-    WebServiceCommandBase(successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context),
-    body_(body)
-  {
-  }
-
-  void WebServicePostCommand::Execute()
-  {
-    Orthanc::HttpClient client(parameters_, "/");
-    client.SetUrl(url_);
-    client.SetTimeout(timeoutInSeconds_);
-    client.SetMethod(Orthanc::HttpMethod_Post);
-    client.GetBody().swap(body_);
-
-    for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
-    {
-      client.AddHeader(it->first, it->second);
-    }
-
-    success_ = client.Apply(answer_, answerHeaders_);
-    httpStatus_ = client.GetLastStatus();
-  }
-}
--- a/Platforms/Generic/WebServicePostCommand.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-/**
- * 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 "WebServiceCommandBase.h"
-
-namespace Deprecated
-{
-  class WebServicePostCommand : public WebServiceCommandBase
-  {
-  protected:
-    std::string  body_;
-
-  public:
-    WebServicePostCommand(MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
-                          const Orthanc::WebServiceParameters& parameters,
-                          const std::string& url,
-                          const IWebService::HttpHeaders& headers,
-                          unsigned int timeoutInSeconds,
-                          const std::string& body,
-                          Orthanc::IDynamicObject* payload /* takes ownership */,
-                          OrthancStone::NativeStoneApplicationContext& context);
-
-    virtual void Execute();
-  };
-}
--- a/Platforms/Wasm/Defaults.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,416 +0,0 @@
-#include "Defaults.h"
-
-#include "WasmWebService.h"
-#include "WasmDelayedCallExecutor.h"
-#include "../../Framework/Deprecated/Widgets/TestCairoWidget.h"
-#include <Framework/Deprecated/Viewport/WidgetViewport.h>
-#include <Applications/Wasm/StartupParametersBuilder.h>
-#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h>
-#include <Framework/StoneInitialization.h>
-#include <Core/Logging.h>
-#include <sstream>
-
-#include <algorithm>
-
-
-static unsigned int width_ = 0;
-static unsigned int height_ = 0;
-
-/**********************************/
-
-static std::unique_ptr<OrthancStone::IStoneApplication> application;
-static std::unique_ptr<OrthancStone::WasmPlatformApplicationAdapter> applicationWasmAdapter = NULL;
-static std::unique_ptr<OrthancStone::StoneApplicationContext> context;
-static OrthancStone::StartupParametersBuilder startupParametersBuilder;
-static OrthancStone::MessageBroker broker;
-
-static OrthancStone::ViewportContentChangedObserver viewportContentChangedObserver_(broker);
-static OrthancStone::StatusBar statusBar_;
-
-static std::list<std::shared_ptr<Deprecated::WidgetViewport>> viewports_;
-
-std::shared_ptr<Deprecated::WidgetViewport> FindViewportSharedPtr(ViewportHandle viewport) {
-  for (const auto& v : viewports_) {
-    if (v.get() == viewport) {
-      return v;
-    }
-  }
-  assert(false);
-  return std::shared_ptr<Deprecated::WidgetViewport>();
-}
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if 0
-  // rewrite malloc/free in order to monitor allocations.  We actually only monitor large allocations (like images ...)
-
-  size_t bigChunksTotalSize = 0;
-  std::map<void*, size_t> allocatedBigChunks;
-
-  extern void* emscripten_builtin_malloc(size_t bytes);
-  extern void emscripten_builtin_free(void* mem);
-
-  void * __attribute__((noinline)) malloc(size_t size)
-  {
-    void *ptr = emscripten_builtin_malloc(size);
-    if (size > 100000)
-    {
-      bigChunksTotalSize += size;
-      printf("++ Allocated %zu bytes, got %p. (%zu MB consumed by big chunks)\n", size, ptr, bigChunksTotalSize/(1024*1024));
-      allocatedBigChunks[ptr] = size;
-    }
-    return ptr;
-  }
-
-  void __attribute__((noinline)) free(void *ptr)
-  {
-    emscripten_builtin_free(ptr);
-
-    std::map<void*, size_t>::iterator it = allocatedBigChunks.find(ptr);
-    if (it != allocatedBigChunks.end())
-    {
-      bigChunksTotalSize -= it->second;
-      printf("--     Freed %zu bytes at %p.   (%zu MB consumed by big chunks)\n", it->second, ptr, bigChunksTotalSize/(1024*1024));
-      allocatedBigChunks.erase(it);
-    }
-  }
-#endif // 0
-
-  using namespace OrthancStone;
-
-  // when WASM needs a C++ viewport
-  ViewportHandle EMSCRIPTEN_KEEPALIVE CreateCppViewport() {
-    
-    std::shared_ptr<Deprecated::WidgetViewport> viewport(new Deprecated::WidgetViewport(broker));
-    printf("viewport %x\n", (int)viewport.get());
-
-    viewports_.push_back(viewport);
-
-    printf("There are now %lu viewports in C++\n", viewports_.size());
-
-    viewport->SetStatusBar(statusBar_);
-
-    viewport->RegisterObserverCallback(
-      new Callable<ViewportContentChangedObserver, Deprecated::IViewport::ViewportChangedMessage>
-      (viewportContentChangedObserver_, &ViewportContentChangedObserver::OnViewportChanged));
-
-    return viewport.get();
-  }
-
-  // when WASM does not need a viewport anymore, it should release it 
-  void EMSCRIPTEN_KEEPALIVE ReleaseCppViewport(ViewportHandle viewport) {
-    viewports_.remove_if([viewport](const std::shared_ptr<Deprecated::WidgetViewport>& v) { return v.get() == viewport;});
-
-    printf("There are now %lu viewports in C++\n", viewports_.size());
-  }
-
-  void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle viewport) {
-    printf("Initializing Stone\n");
-    OrthancStone::StoneInitialize();
-    printf("CreateWasmApplication\n");
-
-    application.reset(CreateUserApplication(broker));
-    applicationWasmAdapter.reset(CreateWasmApplicationAdapter(broker, application.get())); 
-    Deprecated::WasmWebService::SetBroker(broker);
-    Deprecated::WasmDelayedCallExecutor::SetBroker(broker);
-
-    startupParametersBuilder.Clear();
-  }
-
-  void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc,
-                                                  const char* value) {
-    startupParametersBuilder.SetStartupParameter(keyc, value);
-  }
-
-  void EMSCRIPTEN_KEEPALIVE StartWasmApplication(const char* baseUri) {
-
-    printf("StartWasmApplication\n");
-
-    Orthanc::Logging::SetErrorWarnInfoTraceLoggingFunctions(
-      stone_console_error, stone_console_warning,
-      stone_console_info, stone_console_trace);
-
-    // recreate a command line from uri arguments and parse it
-    boost::program_options::variables_map parameters;
-    boost::program_options::options_description options;
-    application->DeclareStartupOptions(options);
-    startupParametersBuilder.GetStartupParameters(parameters, options);
-
-    context.reset(new OrthancStone::StoneApplicationContext(broker));
-    context->SetOrthancBaseUrl(baseUri);
-    printf("Base URL to Orthanc API: [%s]\n", baseUri);
-    context->SetWebService(Deprecated::WasmWebService::GetInstance());
-    context->SetDelayedCallExecutor(Deprecated::WasmDelayedCallExecutor::GetInstance());
-    application->Initialize(context.get(), statusBar_, parameters);
-    application->InitializeWasm();
-
-//    viewport->SetSize(width_, height_);
-    printf("StartWasmApplication - completed\n");
-  }
-  
-  bool EMSCRIPTEN_KEEPALIVE WasmIsTraceLevelEnabled()
-  {
-    return Orthanc::Logging::IsTraceLevelEnabled();
-  }
-
-  bool EMSCRIPTEN_KEEPALIVE WasmIsInfoLevelEnabled()
-  {
-    return Orthanc::Logging::IsInfoLevelEnabled();
-  }
-  
-  void EMSCRIPTEN_KEEPALIVE WasmDoAnimation()
-  {
-    for (auto viewport : viewports_) {
-      // TODO Only launch the JavaScript timer if "HasAnimation()"
-      if (viewport->HasAnimation())
-      {
-        viewport->DoAnimation();
-      }
-
-    }
-
-  }
-  
-
-  void EMSCRIPTEN_KEEPALIVE ViewportSetSize(ViewportHandle viewport, unsigned int width, unsigned int height)
-  {
-    width_ = width;
-    height_ = height;
-    
-    viewport->SetSize(width, height);
-  }
-
-  int EMSCRIPTEN_KEEPALIVE ViewportRender(ViewportHandle viewport,
-                                          unsigned int width,
-                                          unsigned int height,
-                                          uint8_t* data)
-  {
-    viewportContentChangedObserver_.Reset();
-
-    //printf("ViewportRender called %dx%d\n", width, height);
-    if (width == 0 ||
-        height == 0)
-    {
-      return 1;
-    }
-
-    Orthanc::ImageAccessor surface;
-    surface.AssignWritable(Orthanc::PixelFormat_BGRA32, width, height, 4 * width, data);
-
-    viewport->Render(surface);
-
-    // Convert from BGRA32 memory layout (only color mode supported by
-    // Cairo, which corresponds to CAIRO_FORMAT_ARGB32) to RGBA32 (as
-    // expected by HTML5 canvas). This simply amounts to swapping the
-    // B and R channels.
-    uint8_t* p = data;
-    for (unsigned int y = 0; y < height; y++) {
-      for (unsigned int x = 0; x < width; x++) {
-        uint8_t tmp = p[0];
-        p[0] = p[2];
-        p[2] = tmp;
-        
-        p += 4;
-      }
-    }
-
-    return 1;
-  }
-
-
-  void EMSCRIPTEN_KEEPALIVE ViewportMouseDown(ViewportHandle viewport,
-                                              unsigned int rawButton,
-                                              int x,
-                                              int y,
-                                              unsigned int rawModifiers)
-  {
-    OrthancStone::MouseButton button;
-    switch (rawButton)
-    {
-      case 0:
-        button = OrthancStone::MouseButton_Left;
-        break;
-
-      case 1:
-        button = OrthancStone::MouseButton_Middle;
-        break;
-
-      case 2:
-        button = OrthancStone::MouseButton_Right;
-        break;
-
-      default:
-        return;  // Unknown button
-    }
-
-    viewport->MouseDown(button, x, y, OrthancStone::KeyboardModifiers_None, std::vector<Deprecated::Touch>());
-  }
-  
-
-  void EMSCRIPTEN_KEEPALIVE ViewportMouseWheel(ViewportHandle viewport,
-                                               int deltaY,
-                                               int x,
-                                               int y,
-                                               int isControl)
-  {
-    if (deltaY != 0)
-    {
-      OrthancStone::MouseWheelDirection direction = (deltaY < 0 ?
-                                                     OrthancStone::MouseWheelDirection_Up :
-                                                     OrthancStone::MouseWheelDirection_Down);
-      OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None;
-
-      if (isControl != 0)
-      {
-        modifiers = OrthancStone::KeyboardModifiers_Control;
-      }
-
-      viewport->MouseWheel(direction, x, y, modifiers);
-    }
-  }
-  
-
-  void EMSCRIPTEN_KEEPALIVE ViewportMouseMove(ViewportHandle viewport,
-                                              int x,
-                                              int y)
-  {
-    viewport->MouseMove(x, y, std::vector<Deprecated::Touch>());
-  }
-
-  void GetTouchVector(std::vector<Deprecated::Touch>& output,
-                      int touchCount,
-                      float x0,
-                      float y0,
-                      float x1,
-                      float y1,
-                      float x2,
-                      float y2)
-  {
-    // TODO: it might be nice to try to pass all the x0,y0 coordinates as arrays but that's not so easy to pass array between JS and C++
-    if (touchCount > 0)
-    {
-      output.push_back(Deprecated::Touch(x0, y0));
-    }
-    if (touchCount > 1)
-    {
-      output.push_back(Deprecated::Touch(x1, y1));
-    }
-    if (touchCount > 2)
-    {
-      output.push_back(Deprecated::Touch(x2, y2));
-    }
-
-  }
-
-  void EMSCRIPTEN_KEEPALIVE ViewportTouchStart(ViewportHandle viewport,
-                                              int touchCount,
-                                              float x0,
-                                              float y0,
-                                              float x1,
-                                              float y1,
-                                              float x2,
-                                              float y2)
-  {
-    // printf("touch start with %d touches\n", touchCount);
-
-    std::vector<Deprecated::Touch> touches;
-    GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
-    viewport->TouchStart(touches);
-  }
-
-  void EMSCRIPTEN_KEEPALIVE ViewportTouchMove(ViewportHandle viewport,
-                                              int touchCount,
-                                              float x0,
-                                              float y0,
-                                              float x1,
-                                              float y1,
-                                              float x2,
-                                              float y2)
-  {
-    // printf("touch move with %d touches\n", touchCount);
-
-    std::vector<Deprecated::Touch> touches;
-    GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
-    viewport->TouchMove(touches);
-  }
-
-  void EMSCRIPTEN_KEEPALIVE ViewportTouchEnd(ViewportHandle viewport,
-                                              int touchCount,
-                                              float x0,
-                                              float y0,
-                                              float x1,
-                                              float y1,
-                                              float x2,
-                                              float y2)
-  {
-    // printf("touch end with %d touches remaining\n", touchCount);
-
-    std::vector<Deprecated::Touch> touches;
-    GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
-    viewport->TouchEnd(touches);
-  }
-
-  void EMSCRIPTEN_KEEPALIVE ViewportKeyPressed(ViewportHandle viewport,
-                                               int key,
-                                               const char* keyChar, 
-                                               bool isShiftPressed, 
-                                               bool isControlPressed,
-                                               bool isAltPressed)
-                                               
-  {
-    OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None;
-    if (isShiftPressed) {
-      modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Shift);
-    }
-    if (isControlPressed) {
-      modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Control);
-    }
-    if (isAltPressed) {
-      modifiers = static_cast<OrthancStone::KeyboardModifiers>(modifiers + OrthancStone::KeyboardModifiers_Alt);
-    }
-
-    char c = 0;
-    if (keyChar != NULL && key == OrthancStone::KeyboardKeys_Generic) {
-      c = keyChar[0];
-    }
-    viewport->KeyPressed(static_cast<OrthancStone::KeyboardKeys>(key), c, modifiers);
-  }
-  
-
-  void EMSCRIPTEN_KEEPALIVE ViewportMouseUp(ViewportHandle viewport)
-  {
-    viewport->MouseUp();
-  }
-  
-
-  void EMSCRIPTEN_KEEPALIVE ViewportMouseEnter(ViewportHandle viewport)
-  {
-    viewport->MouseEnter();
-  }
-  
-
-  void EMSCRIPTEN_KEEPALIVE ViewportMouseLeave(ViewportHandle viewport)
-  {
-    viewport->MouseLeave();
-  }
-
-  const char* EMSCRIPTEN_KEEPALIVE SendSerializedMessageToStoneApplication(const char* message) 
-  {
-    static std::string output; // we don't want the string to be deallocated when we return to JS code so we always use the same string (this is fine since JS is single-thread)
-
-    //printf("SendSerializedMessageToStoneApplication\n");
-    //printf("%s", message);
-
-    if (applicationWasmAdapter.get() != NULL) {
-      applicationWasmAdapter->HandleSerializedMessageFromWeb(output, std::string(message));
-      return output.c_str();
-    }
-    printf("This Stone application does not have a Web Adapter, unable to send messages");
-    return NULL;
-  }
-
-#ifdef __cplusplus
-}
-#endif
--- a/Platforms/Wasm/Defaults.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-#pragma once
-
-#include <emscripten/emscripten.h>
-
-#include "../../Framework/Deprecated/Viewport/WidgetViewport.h"
-#include "../../Framework/Deprecated/Widgets/LayoutWidget.h"
-#include <Applications/IStoneApplication.h>
-#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h>
-
-typedef Deprecated::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-  
-  // JS methods accessible from C++
-  extern void ScheduleWebViewportRedrawFromCpp(ViewportHandle cppViewportHandle);
-  extern void UpdateStoneApplicationStatusFromCppWithString(const char* statusUpdateMessage);
-  extern void UpdateStoneApplicationStatusFromCppWithSerializedMessage(const char* statusUpdateMessage);
-  extern void stone_console_error(const char*);
-  extern void stone_console_warning(const char*);
-  extern void stone_console_info(const char*);
-  extern void stone_console_trace(const char*);
-
-  // C++ methods accessible from JS
-  extern void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle cppViewportHandle);
-  extern void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc, const char* value);
-  
-
-#ifdef __cplusplus
-}
-#endif
-
-// these methods must be implemented in the custom app "mainWasm.cpp"
-extern OrthancStone::IStoneApplication* CreateUserApplication(OrthancStone::MessageBroker& broker);
-extern OrthancStone::WasmPlatformApplicationAdapter* CreateWasmApplicationAdapter(OrthancStone::MessageBroker& broker, OrthancStone::IStoneApplication* application);
-
-namespace OrthancStone {
-
-  // default Observer to trigger Viewport redraw when something changes in the Viewport
-  class ViewportContentChangedObserver : public IObserver
-  {
-  private:
-    // Flag to avoid flooding JavaScript with redundant Redraw requests
-    bool isScheduled_; 
-
-  public:
-    ViewportContentChangedObserver(MessageBroker& broker) :
-      IObserver(broker),
-      isScheduled_(false)
-    {
-    }
-
-    void Reset()
-    {
-      isScheduled_ = false;
-    }
-
-    void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message)
-    {
-      if (!isScheduled_)
-      {
-        ScheduleWebViewportRedrawFromCpp((ViewportHandle)&message.GetOrigin());  // loosing constness when transmitted to Web
-        isScheduled_ = true;
-      }
-    }
-  };
-
-  // default status bar to log messages on the console/stdout
-  class StatusBar : public Deprecated::IStatusBar
-  {
-  public:
-    virtual void ClearMessage()
-    {
-    }
-
-    virtual void SetMessage(const std::string& message)
-    {
-      printf("%s\n", message.c_str());
-    }
-  };
-}
--- a/Platforms/Wasm/WasmDelayedCallExecutor.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-#include "WasmDelayedCallExecutor.h"
-#include "json/value.h"
-#include "json/writer.h"
-#include <emscripten/emscripten.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  extern void WasmDelayedCallExecutor_Schedule(void* callable,
-                                      unsigned int timeoutInMs
-                                      /*void* payload*/);
-
-  void EMSCRIPTEN_KEEPALIVE WasmDelayedCallExecutor_ExecuteCallback(void* callable
-                                                       //void* payload
-                                                       )
-  {
-    if (callable == NULL)
-    {
-      throw;
-    }
-    else
-    {
-      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IDelayedCallExecutor::TimeoutMessage>*>(callable)->
-        Apply(Deprecated::IDelayedCallExecutor::TimeoutMessage()); // uri, reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
-    }
-  }
-
-
-#ifdef __cplusplus
-}
-#endif
-
-
-
-namespace Deprecated
-{
-  OrthancStone::MessageBroker* WasmDelayedCallExecutor::broker_ = NULL;
-
-
-  void WasmDelayedCallExecutor::Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
-                         unsigned int timeoutInMs)
-  {
-    WasmDelayedCallExecutor_Schedule(callback, timeoutInMs);
-  }
-}
--- a/Platforms/Wasm/WasmDelayedCallExecutor.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-#pragma once
-
-#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
-#include <Core/OrthancException.h>
-
-namespace Deprecated
-{
-  class WasmDelayedCallExecutor : public IDelayedCallExecutor
-  {
-  private:
-    static OrthancStone::MessageBroker* broker_;
-
-    // Private constructor => Singleton design pattern
-    WasmDelayedCallExecutor(OrthancStone::MessageBroker& broker) :
-      IDelayedCallExecutor(broker)
-    {
-    }
-
-  public:
-    static WasmDelayedCallExecutor& GetInstance()
-    {
-      if (broker_ == NULL)
-      {
-        printf("WasmDelayedCallExecutor::GetInstance(): broker not initialized\n");
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-      static WasmDelayedCallExecutor instance(*broker_);
-      return instance;
-    }
-
-    static void SetBroker(OrthancStone::MessageBroker& broker)
-    {
-      broker_ = &broker;
-    }
-
-    virtual void Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
-                         unsigned int timeoutInMs = 1000);
-
-  };
-}
--- a/Platforms/Wasm/WasmDelayedCallExecutor.js	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-mergeInto(LibraryManager.library, {
-  WasmDelayedCallExecutor_Schedule: function(callable, timeoutInMs/*, payload*/) {
-    setTimeout(function() {
-      window.WasmDelayedCallExecutor_ExecuteCallback(callable/*, payload*/);
-    }, timeoutInMs);
-  }
-});
--- a/Platforms/Wasm/WasmPlatformApplicationAdapter.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-#include "WasmPlatformApplicationAdapter.h"
-
-#include "Framework/StoneException.h"
-#include <stdio.h>
-#include "Platforms/Wasm/Defaults.h"
-
-namespace OrthancStone
-{
-  WasmPlatformApplicationAdapter::WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application)
-  : IObserver(broker),
-    application_(application)
-  {
-  }
-
-  void WasmPlatformApplicationAdapter::HandleSerializedMessageFromWeb(std::string& output, const std::string& input)
-  {
-    try
-    {
-      application_.HandleSerializedMessage(input.c_str());
-    }
-    catch (StoneException& exc)
-    {
-      printf("Error while handling message from web (error code = %d):\n", exc.GetErrorCode());
-      printf("While interpreting input: '%s'\n", input.c_str());
-      output = std::string("ERROR : ");
-    }
-    catch (std::exception& exc)
-    {
-      printf("Error while handling message from web (error text = %s):\n", exc.what());
-      printf("While interpreting input: '%s'\n", input.c_str());
-      output = std::string("ERROR : ");
-    }
-  }
-
-  void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage)
-  {
-    try
-    {
-      UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str());
-    }
-    catch (...)
-    {
-      printf("Error while handling string message to web\n");
-    }
-  }
-
-  void WasmPlatformApplicationAdapter::NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage)
-  {
-    try
-    {
-      UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str());
-    }
-    catch (...)
-    {
-      printf("Error while handling serialized message to web\n");
-    }
-  }
-
-}
--- a/Platforms/Wasm/WasmPlatformApplicationAdapter.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-#pragma once
-
-#include <string>
-#include <Framework/Messages/IObserver.h>
-#include <Applications/IStoneApplication.h>
-
-namespace OrthancStone
-{
-  class WasmPlatformApplicationAdapter : public IObserver
-  {
-      IStoneApplication&  application_;
-    public:
-      WasmPlatformApplicationAdapter(MessageBroker& broker, IStoneApplication& application);
-
-      virtual void HandleSerializedMessageFromWeb(std::string& output, const std::string& input);
-      virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage);
-      virtual void NotifyStatusUpdateFromCppToWebWithSerializedMessage(const std::string& statusUpdateMessage);
-  };
-}
\ No newline at end of file
--- a/Platforms/Wasm/WasmViewport.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-#include "WasmViewport.h"
-
-#include <vector>
-#include <memory>
-
-std::vector<std::shared_ptr<Deprecated::WidgetViewport>> wasmViewports;
-
-void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget) {
-    std::shared_ptr<Deprecated::WidgetViewport> viewport(CreateWasmViewportFromCpp(htmlCanvasId));
-    viewport->SetCentralWidget(centralWidget);
-
-    wasmViewports.push_back(viewport);
-}
--- a/Platforms/Wasm/WasmViewport.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-#pragma once
-
-#include "../../Framework/Deprecated/Viewport/WidgetViewport.h"
-
-#include <emscripten/emscripten.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  // JS methods accessible from C++
-  extern Deprecated::WidgetViewport* CreateWasmViewportFromCpp(const char* htmlCanvasId);
-
-#ifdef __cplusplus
-}
-#endif
-
-extern void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget);
--- a/Platforms/Wasm/WasmWebService.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,168 +0,0 @@
-#include "WasmWebService.h"
-#include "json/value.h"
-#include "json/writer.h"
-#include <emscripten/emscripten.h>
-#include <boost/shared_ptr.hpp>
-
-struct CachedSuccessNotification
-{
-  boost::shared_ptr<Deprecated::BaseWebService::CachedHttpRequestSuccessMessage>    cachedMessage;
-  std::unique_ptr<Orthanc::IDynamicObject>                                              payload;
-  OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback;
-};
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-  extern void WasmWebService_GetAsync(void* callableSuccess,
-                                      void* callableFailure,
-                                      const char* uri,
-                                      const char* headersInJsonString,
-                                      void* payload,
-                                      unsigned int timeoutInSeconds);
-
-  extern void WasmWebService_ScheduleLaterCachedSuccessNotification(void* brol);
-
-  extern void WasmWebService_PostAsync(void* callableSuccess,
-                                       void* callableFailure,
-                                       const char* uri,
-                                       const char* headersInJsonString,
-                                       const void* body,
-                                       size_t bodySize,
-                                       void* payload,
-                                       unsigned int timeoutInSeconds);
-
-  extern void WasmWebService_DeleteAsync(void* callableSuccess,
-                                         void* callableFailure,
-                                         const char* uri,
-                                         const char* headersInJsonString,
-                                         void* payload,
-                                         unsigned int timeoutInSeconds);
-
-  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyError(void* failureCallable,
-                                                       const char* uri,
-                                                       unsigned int httpStatus,
-                                                       void* payload)
-  {
-    if (failureCallable != NULL)
-    {
-      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>*>(failureCallable)->
-        Apply(Deprecated::IWebService::HttpRequestErrorMessage(uri, static_cast<Orthanc::HttpStatus>(httpStatus), reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
-    }
-  }
-
-  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyCachedSuccess(void* notification_)
-  {
-    // notification has been allocated in C++ and passed to JS.  It must be deleted by this method
-    std::unique_ptr<CachedSuccessNotification> notification(reinterpret_cast<CachedSuccessNotification*>(notification_));
-
-    notification->successCallback->Apply(Deprecated::IWebService::HttpRequestSuccessMessage(
-      notification->cachedMessage->GetUri(), 
-      notification->cachedMessage->GetAnswer(),
-      notification->cachedMessage->GetAnswerSize(),
-      notification->cachedMessage->GetAnswerHttpHeaders(),
-      notification->payload.get()
-      ));
-  }
-
-  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifySuccess(void* successCallable,
-                                                         const char* uri,
-                                                         const void* body,
-                                                         size_t bodySize,
-                                                         const char* answerHeaders,
-                                                         void* payload)
-  {
-    if (successCallable != NULL)
-    {
-      Deprecated::IWebService::HttpHeaders headers;
-
-      // TODO - Parse "answerHeaders"
-      //printf("TODO: parse headers [%s]\n", answerHeaders);
-      
-      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>*>(successCallable)->
-        Apply(Deprecated::IWebService::HttpRequestSuccessMessage(uri, body, bodySize, headers,
-                                                                   reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
-    }
-  }
-
-#ifdef __cplusplus
-}
-#endif
-
-
-
-namespace Deprecated
-{
-  OrthancStone::MessageBroker* WasmWebService::broker_ = NULL;
-
-  void ToJsonString(std::string& output, const IWebService::HttpHeaders& headers)
-  {
-    Json::Value jsonHeaders;
-    for (IWebService::HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ )
-    {
-      jsonHeaders[it->first] = it->second;
-    }
-
-    Json::StreamWriterBuilder builder;
-    std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
-    std::ostringstream outputStr;
-
-    writer->write(jsonHeaders, &outputStr);
-    output = outputStr.str();
-  }
-
-  void WasmWebService::PostAsync(const std::string& relativeUri,
-                                 const HttpHeaders& headers,
-                                 const std::string& body,
-                                 Orthanc::IDynamicObject* payload,
-                                 OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
-                                 OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
-                                 unsigned int timeoutInSeconds)
-  {
-    std::string headersInJsonString;
-    ToJsonString(headersInJsonString, headers);
-    WasmWebService_PostAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(),
-                             body.c_str(), body.size(), payload, timeoutInSeconds);
-  }
-
-  void WasmWebService::DeleteAsync(const std::string& relativeUri,
-                                   const HttpHeaders& headers,
-                                   Orthanc::IDynamicObject* payload,
-                                   OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
-                                   OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
-                                   unsigned int timeoutInSeconds)
-  {
-    std::string headersInJsonString;
-    ToJsonString(headersInJsonString, headers);
-    WasmWebService_DeleteAsync(successCallable, failureCallable, relativeUri.c_str(), headersInJsonString.c_str(),
-                               payload, timeoutInSeconds);
-  }
-
-  void WasmWebService::GetAsyncInternal(const std::string &relativeUri,
-                                        const HttpHeaders &headers,
-                                        Orthanc::IDynamicObject *payload,
-                                        OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
-                                        OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable,
-                                        unsigned int timeoutInSeconds)
-  {
-    std::string headersInJsonString;
-    ToJsonString(headersInJsonString, headers);
-    WasmWebService_GetAsync(successCallable, failureCallable, relativeUri.c_str(),
-                            headersInJsonString.c_str(), payload, timeoutInSeconds);
-  }
-
-  void WasmWebService::NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
-                                              Orthanc::IDynamicObject* payload, // takes ownership
-                                              OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback)
-  {
-    CachedSuccessNotification* notification = new CachedSuccessNotification();  // allocated on the heap, it will be passed to JS and deleted when coming back to C++
-    notification->cachedMessage = cachedMessage;
-    notification->payload.reset(payload);
-    notification->successCallback = successCallback;
-
-    WasmWebService_ScheduleLaterCachedSuccessNotification(notification);
-  }
-
-}
--- a/Platforms/Wasm/WasmWebService.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-#pragma once
-
-#include "../../Framework/Deprecated/Toolbox/BaseWebService.h"
-#include <Core/OrthancException.h>
-
-namespace Deprecated
-{
-class WasmWebService : public BaseWebService
-{
-private:
-  static OrthancStone::MessageBroker *broker_;
-
-  // Private constructor => Singleton design pattern
-  WasmWebService(OrthancStone::MessageBroker &broker) : BaseWebService(broker)
-  {
-  }
-
-public:
-  static WasmWebService &GetInstance()
-  {
-    if (broker_ == NULL)
-    {
-      printf("WasmWebService::GetInstance(): broker not initialized\n");
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    static WasmWebService instance(*broker_);
-    return instance;
-  }
-
-  static void SetBroker(OrthancStone::MessageBroker &broker)
-  {
-    broker_ = &broker;
-  }
-
-  virtual void PostAsync(const std::string &uri,
-                         const HttpHeaders &headers,
-                         const std::string &body,
-                         Orthanc::IDynamicObject *payload,
-                         OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
-                         OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
-                         unsigned int timeoutInSeconds = 60);
-
-  virtual void DeleteAsync(const std::string &uri,
-                           const HttpHeaders &headers,
-                           Orthanc::IDynamicObject *payload,
-                           OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
-                           OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
-                           unsigned int timeoutInSeconds = 60);
-
-protected:
-  virtual void GetAsyncInternal(const std::string &uri,
-                                const HttpHeaders &headers,
-                                Orthanc::IDynamicObject *payload,
-                                OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
-                                OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
-                                unsigned int timeoutInSeconds = 60);
-
-  virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage,
-                                      Orthanc::IDynamicObject *payload, // takes ownership
-                                      OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallback);
-};
-} // namespace Deprecated
--- a/Platforms/Wasm/WasmWebService.js	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-mergeInto(LibraryManager.library, {
-  WasmWebService_GetAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) {
-    // Directly use XMLHttpRequest (no jQuery) to retrieve the raw binary data
-    // http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/
-    var xhr = new XMLHttpRequest();
-    var url_ = UTF8ToString(url);
-    var headersInJsonString_ = UTF8ToString(headersInJsonString);
-
-    xhr.open('GET', url_, true);
-    xhr.responseType = 'arraybuffer';
-    xhr.timeout = timeoutInSeconds * 1000;
-    var headers = JSON.parse(headersInJsonString_);
-    for (var key in headers) {
-      xhr.setRequestHeader(key, headers[key]);
-    }
-    //console.log(xhr); 
-    xhr.onreadystatechange = function() {
-      if (this.readyState == XMLHttpRequest.DONE) {
-        if (xhr.status === 200) {
-          var s = xhr.getAllResponseHeaders();
-          var headers = _malloc(s.length + 1);
-          stringToUTF8(s, headers, s.length + 1);
-          
-          // TODO - Is "new Uint8Array()" necessary? This copies the
-          // answer to the WebAssembly stack, hence necessitating
-          // increasing the TOTAL_STACK parameter of Emscripten
-          window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
-                                       this.response.byteLength, headers, payload);
-        } else {
-          window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload);
-        }
-      }
-    }
-    
-    xhr.send();
-  },
-
-  WasmWebService_ScheduleLaterCachedSuccessNotification: function (brol) {
-    setTimeout(function() {
-      window.WasmWebService_NotifyCachedSuccess(brol);
-    }, 0);
-  },
-
-  WasmWebService_PostAsync: function(callableSuccess, callableFailure, url, headersInJsonString, body, bodySize, payload, timeoutInSeconds) {
-    var xhr = new XMLHttpRequest();
-    var url_ = UTF8ToString(url);
-    var headersInJsonString_ = UTF8ToString(headersInJsonString);
-    xhr.open('POST', url_, true);
-    xhr.timeout = timeoutInSeconds * 1000;
-    xhr.responseType = 'arraybuffer';
-    xhr.setRequestHeader('Content-type', 'application/octet-stream');
-
-    var headers = JSON.parse(headersInJsonString_);
-    for (var key in headers) {
-      xhr.setRequestHeader(key, headers[key]);
-    }
-    
-    xhr.onreadystatechange = function() {
-      if (this.readyState == XMLHttpRequest.DONE) {
-        if (xhr.status === 200) {
-          var s = xhr.getAllResponseHeaders();
-          var headers = _malloc(s.length + 1);
-          stringToUTF8(s, headers, s.length + 1);
-
-          window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
-                                       this.response.byteLength, headers, payload);
-        } else {
-          window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload);
-        }
-      }
-    }
-
-    xhr.send(new Uint8ClampedArray(HEAPU8.buffer, body, bodySize));
-  },
-
-  WasmWebService_DeleteAsync: function(callableSuccess, callableFailure, url, headersInJsonString, payload, timeoutInSeconds) {
-    var xhr = new XMLHttpRequest();
-    var url_ = UTF8ToString(url);
-    var headersInJsonString_ = UTF8ToString(headersInJsonString);
-    xhr.open('DELETE', url_, true);
-    xhr.timeout = timeoutInSeconds * 1000;
-    xhr.responseType = 'arraybuffer';
-    xhr.setRequestHeader('Content-type', 'application/octet-stream');
-  
-    var headers = JSON.parse(headersInJsonString_);
-    for (var key in headers) {
-      xhr.setRequestHeader(key, headers[key]);
-    }
-    
-    xhr.onreadystatechange = function() {
-      if (this.readyState == XMLHttpRequest.DONE) {
-        if (xhr.status === 200) {
-          var s = xhr.getAllResponseHeaders();
-          var headers = _malloc(s.length + 1);
-          stringToUTF8(s, headers, s.length + 1);
-
-          window.WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
-                                       this.response.byteLength, headers, payload);
-        } else {
-          window.WasmWebService_NotifyError(callableFailure, url_, xhr.status, payload);
-        }
-      }
-    }
-  
-    xhr.send();
-  }
-  
-});
--- a/Platforms/Wasm/default-library.js	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-// this file contains the JS method you want to expose to C++ code
-
-mergeInto(LibraryManager.library, {
-
-  ScheduleWebViewportRedrawFromCpp: function(cppViewportHandle) {
-    window.ScheduleWebViewportRedraw(cppViewportHandle);
-  },
-
-  CreateWasmViewportFromCpp: function(htmlCanvasId) {
-    return window.CreateWasmViewport(htmlCanvasId);
-  },
-
-  // each time the StoneApplication updates its status, it may signal it 
-  // through this method. i.e, to change the status of a button in the web interface
-  UpdateStoneApplicationStatusFromCppWithString: function(statusUpdateMessage) {
-    var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage);
-    window.UpdateWebApplicationWithString(statusUpdateMessage_);
-  },
-
-  // same, but with a serialized message
-  UpdateStoneApplicationStatusFromCppWithSerializedMessage: function(statusUpdateMessage) {
-    var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage);
-    window.UpdateWebApplicationWithSerializedMessage(statusUpdateMessage_);
-  },
-
-  // These functions are called from C++ (through an extern declaration) 
-  // and call the standard logger that, here, routes to the console.
-
-  stone_console_error : function(message) {
-    var text = UTF8ToString(message);
-    window.errorFromCpp(text);
-  },
-
-  stone_console_warning : function(message) {
-    var text = UTF8ToString(message);
-    window.warningFromCpp(text);
-  },
-
-  stone_console_info: function(message) {
-    var text = UTF8ToString(message);
-    window.infoFromCpp(text);
-  },
-  
-  stone_console_trace : function(message) {
-    var text = UTF8ToString(message);
-    window.debugFromCpp(text);
-  }
-
-});
--- a/Platforms/Wasm/logger.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-export enum LogSource {
-  Cpp,
-  Typescript
-}
-
-export class StandardConsoleLogger {
-  public showSource: boolean = true;
-
-  public debug(...args: any[]): void {
-    this._debug(LogSource.Typescript, ...args);
-  }
-
-  public debugFromCpp(...args: any[]): void {
-    this._debug(LogSource.Cpp, ...args);
-  }
-
-  public info(...args: any[]): void {
-    this._info(LogSource.Typescript, ...args);
-  }
-
-  public infoFromCpp(message: string): void {
-    this._info(LogSource.Cpp, message);
-  }
-
-  public warning(...args: any[]): void {
-    this._warning(LogSource.Typescript, ...args);
-  }
-
-  public warningFromCpp(message: string): void {
-    this._warning(LogSource.Cpp, message);
-  }
-
-  public error(...args: any[]): void {
-    this._error(LogSource.Typescript, ...args);
-  }
-
-  public errorFromCpp(message: string): void {
-    this._error(LogSource.Cpp, message);
-  }
-
-  public _debug(source: LogSource, ...args: any[]): void {
-    if ((<any> window).IsTraceLevelEnabled)
-    {
-      if ((<any> window).IsTraceLevelEnabled())
-      {
-        var output = this.getOutput(source, args);
-        console.debug(...output);
-      }
-    }
-  }
-
-  private _info(source: LogSource, ...args: any[]): void {
-    if ((<any> window).IsInfoLevelEnabled)
-    {
-      if ((<any> window).IsInfoLevelEnabled())
-      {
-        var output = this.getOutput(source, args);
-        console.info(...output);
-      }
-    }
-  }
-
-  public _warning(source: LogSource, ...args: any[]): void {
-    var output = this.getOutput(source, args);
-    console.warn(...output);
-  }
-
-  public _error(source: LogSource, ...args: any[]): void {
-    var output = this.getOutput(source, args);
-    console.error(...output);
-  }
-
-
-  private getOutput(source: LogSource, args: any[]): any[] {
-    var prefix = this.getPrefix();
-    var prefixAndSource = Array<string>();
-
-    if (prefix != null) {
-      prefixAndSource = [prefix];
-    } 
-
-    if (this.showSource) {
-      if (source == LogSource.Typescript) {
-        prefixAndSource = [...prefixAndSource, "TS "];
-      } else if (source == LogSource.Cpp) {
-        prefixAndSource = [...prefixAndSource, "C++"];
-      }
-    }
-
-    if (prefixAndSource.length > 0) {
-      prefixAndSource = [...prefixAndSource, "|"];
-    }
-
-    return [...prefixAndSource, ...args];
-  }
-
-  protected getPrefix(): string | null {
-    return null;
-  }
-}
-
-export class TimeConsoleLogger extends StandardConsoleLogger {
-  protected getPrefix(): string {
-    let now = new Date();
-    let timeString = now.getHours().toString().padStart(2, "0") + ":" + now.getMinutes().toString().padStart(2, "0") + ":" + now.getSeconds().toString().padStart(2, "0") + "." + now.getMilliseconds().toString().padStart(3, "0");
-    return timeString;
-  }
-}
-
-export var defaultLogger: StandardConsoleLogger = new TimeConsoleLogger();
-
--- a/Platforms/Wasm/stone-framework-loader.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-/**
- * This file contains primitives to interface with WebAssembly and
- * with the Stone framework.
- **/
-import * as Logger from './logger'
-
-export declare type InitializationCallback = () => void;
-
-//export declare var StoneFrameworkModule : any;
-export var StoneFrameworkModule : any;
-
-//const ASSETS_FOLDER : string = "assets/lib";
-//const WASM_FILENAME : string = "orthanc-framework";
-
-export class Framework
-{
-  private static singleton_ : Framework = null;
-  private static wasmModuleName_ : string = null;
-
-  public static Configure(wasmModuleName: string) {
-    this.wasmModuleName_ = wasmModuleName;
-  }
-
-  private constructor(verbose : boolean) 
-  {
-    //this.ccall('Initialize', null, [ 'number' ], [ verbose ]);
-  }
-
-  
-  public ccall( name: string,
-                returnType: string,
-                argTypes: Array<string>,
-                argValues: Array<any>) : any
-  {
-    return (<any> window).StoneFrameworkModule.ccall(name, returnType, argTypes, argValues);
-  }
-
-  
-  public cwrap( name: string,
-                returnType: string,
-                argTypes: Array<string>) : any
-  {
-    return (<any> window).StoneFrameworkModule.cwrap(name, returnType, argTypes);
-  }
-
-  
-  public static GetInstance() : Framework
-  {
-    if (Framework.singleton_ == null) {
-      throw new Error('The WebAssembly module is not loaded yet');
-    } else {
-      return Framework.singleton_;
-    }
-  }
-
-
-  public static Initialize( verbose: boolean,
-                            callback: InitializationCallback)
-  {
-    Logger.defaultLogger.debug('Initializing WebAssembly Module');
-
-    (<any> window).errorFromCpp = function(text:any) { Logger.defaultLogger.errorFromCpp(text); };
-    (<any> window).warningFromCpp = function(text:any) { Logger.defaultLogger.warningFromCpp(text); };
-    (<any> window).infoFromCpp = function(text:any) { Logger.defaultLogger.infoFromCpp(text); };
-    (<any> window).debugFromCpp = function(text:any) { Logger.defaultLogger.debugFromCpp(text); };
-
-    // (<any> window).
-    (<any> window).StoneFrameworkModule = {
-      preRun: [ 
-        function() {
-          Logger.defaultLogger.debug('Loading the Stone Framework using WebAssembly');
-        }
-      ],
-      postRun: [ 
-        function()  {
-          // This function is called by ".js" wrapper once the ".wasm"
-          // WebAssembly module has been loaded and compiled by the
-          // browser
-          Logger.defaultLogger.debug('WebAssembly is ready');
-          Framework.singleton_ = new Framework(verbose);
-          callback();
-        }
-      ],
-      totalDependencies: 0
-    };
-
-    // Dynamic loading of the JavaScript wrapper around WebAssembly
-    var script = document.createElement('script');
-    script.type = 'application/javascript';
-    //script.src = "orthanc-stone.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js';
-    script.src = this.wasmModuleName_ + ".js";//  "OrthancStoneSimpleViewer.js"; // ASSETS_FOLDER + '/' + WASM_FILENAME + '.js';
-    script.async = true;
-    document.head.appendChild(script);
-  }
-}
--- a/Platforms/Wasm/tsconfig-stone.json	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-{
-    "include" : [
-        "stone-framework-loader.ts",
-        "logger.ts",
-        "wasm-application-runner.ts",
-        "wasm-viewport.ts"
-    ]
-}
--- a/Platforms/Wasm/wasm-application-runner.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +0,0 @@
-import * as Stone from './stone-framework-loader'
-import * as StoneViewport from './wasm-viewport'
-import * as Logger from './logger'
-
-if (!('WebAssembly' in window)) {
-  alert('Sorry, your browser does not support WebAssembly :(');
-}
-
-//var StoneFrameworkModule : Stone.Framework = (<any>window).StoneFrameworkModule;
-//export declare var StoneFrameworkModule : Stone.Framework;
-
-// global functions
-var WasmWebService_NotifyError: Function = null;
-var WasmWebService_NotifySuccess: Function = null;
-var WasmWebService_NotifyCachedSuccess: Function = null;
-var WasmDelayedCallExecutor_ExecuteCallback: Function = null;
-var WasmDoAnimation: Function = null;
-var SetStartupParameter: Function = null;
-var CreateWasmApplication: Function = null;
-export var CreateCppViewport: Function = null;
-var ReleaseCppViewport: Function = null;
-var StartWasmApplication: Function = null;
-export var SendSerializedMessageToStoneApplication: Function = null;
-
-var auxiliaryParameters : Map<string,string>  = null;
-
-export function SetApplicationParameters(params : Map<string,string>) {
-  if (auxiliaryParameters != null) {
-    console.warn("wasm-application-runner.SetApplicationParameters: about to overwrite the existing application parameters!")
-  }
-  auxiliaryParameters = params;
-}
-
-function DoAnimationThread() {
-  if (WasmDoAnimation != null) {
-    WasmDoAnimation();
-  }
-
-  // Update the viewport content every 100ms if need be
-  setTimeout(DoAnimationThread, 100);  
-}
-
-
-function GetUriParameters(): Map<string, string> {
-  var parameters = window.location.search.substr(1);
-
-  if (parameters != null &&
-    parameters != '') {
-    var result = new Map<string, string>();
-    var tokens = parameters.split('&');
-
-    for (var i = 0; i < tokens.length; i++) {
-      var tmp = tokens[i].split('=');
-      if (tmp.length == 2) {
-        result[tmp[0]] = decodeURIComponent(tmp[1]);
-      } else if(tmp.length == 1) {
-        // if there is no '=', we treat ot afterwards as a flag-style param
-        result[tmp[0]] = "";
-      }
-    }
-  return result;
-  }
-  else {
-    return new Map<string, string>();
-  }
-}
-
-// function UpdateWebApplication(statusUpdateMessage: string) {
-//   console.log(statusUpdateMessage);
-// }
-
-function _InitializeWasmApplication(orthancBaseUrl: string): void {
-
-  CreateWasmApplication();
-
-  // transmit the API-specified parameters to the app before initializing it
-  for (let key in auxiliaryParameters) {
-    if (auxiliaryParameters.hasOwnProperty(key)) {
-      Logger.defaultLogger.debug(
-        `About to call SetStartupParameter("${key}","${auxiliaryParameters[key]}")`);
-      SetStartupParameter(key, auxiliaryParameters[key]);
-    }
-  }
-
-  // parse uri and transmit the URI parameters to the app before initializing it
-  let parameters = GetUriParameters();
-
-  for (let key in parameters) {
-    if (parameters.hasOwnProperty(key)) {
-      Logger.defaultLogger.debug(
-        `About to call SetStartupParameter("${key}","${parameters[key]}")`);
-      SetStartupParameter(key, parameters[key]);
-    }
-  }
-
-  StartWasmApplication(orthancBaseUrl);
-
-  // trigger a first resize of the canvas that has just been initialized
-  StoneViewport.WasmViewport.ResizeAll();
-
-  DoAnimationThread();
-}
-
-export function InitializeWasmApplication(wasmModuleName: string, orthancBaseUrl: string) {
-  
-  Stone.Framework.Configure(wasmModuleName);
-
-  // Wait for the Orthanc Framework to be initialized (this initializes
-  // the WebAssembly environment) and then, create and initialize the Wasm application
-  Stone.Framework.Initialize(true, function () {
-
-    Logger.defaultLogger.debug("Connecting C++ methods to JS methods");
-    
-    SetStartupParameter = (<any> window).StoneFrameworkModule.cwrap('SetStartupParameter', null, ['string', 'string']);
-    CreateWasmApplication = (<any> window).StoneFrameworkModule.cwrap('CreateWasmApplication', null, ['number']);
-    CreateCppViewport = (<any> window).StoneFrameworkModule.cwrap('CreateCppViewport', 'number', []);
-    ReleaseCppViewport = (<any> window).StoneFrameworkModule.cwrap('ReleaseCppViewport', null, ['number']);
-    StartWasmApplication = (<any> window).StoneFrameworkModule.cwrap('StartWasmApplication', null, ['string']);
-    (<any> window).IsTraceLevelEnabled = (<any> window).StoneFrameworkModule.cwrap('WasmIsTraceLevelEnabled', 'boolean', null);
-    (<any> window).IsInfoLevelEnabled = (<any> window).StoneFrameworkModule.cwrap('WasmIsInfoLevelEnabled', 'boolean', null);
-
-    (<any> window).WasmWebService_NotifyCachedSuccess = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifyCachedSuccess', null, ['number']);
-    (<any> window).WasmWebService_NotifySuccess = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifySuccess', null, ['number', 'string', 'array', 'number', 'number']);
-    (<any> window).WasmWebService_NotifyError = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifyError', null, ['number', 'string', 'number', 'number']);
-    (<any> window).WasmDelayedCallExecutor_ExecuteCallback = (<any> window).StoneFrameworkModule.cwrap('WasmDelayedCallExecutor_ExecuteCallback', null, ['number']);
-    // no need to put this into the globals for it's only used in this very module
-    WasmDoAnimation = (<any> window).StoneFrameworkModule.cwrap('WasmDoAnimation', null, []);
-
-    SendSerializedMessageToStoneApplication = (<any> window).StoneFrameworkModule.cwrap('SendSerializedMessageToStoneApplication', 'string', ['string']);
-
-    Logger.defaultLogger.debug("Connecting C++ methods to JS methods - done");
-
-    _InitializeWasmApplication(orthancBaseUrl);
-  });
-}
-
-
-// exports.InitializeWasmApplication = InitializeWasmApplication;
-
-
-
--- a/Platforms/Wasm/wasm-viewport.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,359 +0,0 @@
-import * as wasmApplicationRunner from './wasm-application-runner'
-import * as Logger from './logger'
-
-var isPendingRedraw = false;
-
-function ScheduleWebViewportRedraw(cppViewportHandle: any) : void
-{
-  if (!isPendingRedraw) {
-    isPendingRedraw = true;
-    Logger.defaultLogger.debug('Scheduling a refresh of the viewport, as its content changed');
-    window.requestAnimationFrame(function() {
-      isPendingRedraw = false;
-      let viewport = WasmViewport.GetFromCppViewport(cppViewportHandle);
-      if (viewport) {
-        viewport.Redraw();
-      }
-    });
-  }
-}
-
-(<any>window).ScheduleWebViewportRedraw = ScheduleWebViewportRedraw;
-
-declare function UTF8ToString(v: any): string;
-
-function CreateWasmViewport(htmlCanvasId: string) : any {
-  var cppViewportHandle = wasmApplicationRunner.CreateCppViewport();
-  var canvasId = UTF8ToString(htmlCanvasId);
-  var webViewport = new WasmViewport((<any> window).StoneFrameworkModule, canvasId, cppViewportHandle);  // viewports are stored in a static map in WasmViewport -> won't be deleted
-  webViewport.Initialize();
-
-  return cppViewportHandle;
-}
- 
-(<any>window).CreateWasmViewport = CreateWasmViewport;
-
-export class WasmViewport {
-
-    private static viewportsMapByCppHandle_ : Map<number, WasmViewport> = new Map<number, WasmViewport>(); // key = the C++ handle
-    private static viewportsMapByCanvasId_ : Map<string, WasmViewport> = new Map<string, WasmViewport>(); // key = the canvasId
-
-    private module_ : any;
-    private canvasId_ : string;
-    private htmlCanvas_ : HTMLCanvasElement;
-    private context_ : CanvasRenderingContext2D | null;
-    private imageData_ : any = null;
-    private renderingBuffer_ : any = null;
-    
-    private touchGestureInProgress_: boolean = false;
-    private touchCount_: number = 0;
-    private touchGestureLastCoordinates_: [number, number][] = []; // last x,y coordinates of each touch
-    
-    private touchZoom_ : any = false;
-    private touchTranslation_ : any = false;
-
-    private ViewportSetSize : Function;
-    private ViewportRender : Function;
-    private ViewportMouseDown : Function;
-    private ViewportMouseMove : Function;
-    private ViewportMouseUp : Function;
-    private ViewportMouseEnter : Function;
-    private ViewportMouseLeave : Function;
-    private ViewportMouseWheel : Function;
-    private ViewportKeyPressed : Function;
-    private ViewportTouchStart : Function;
-    private ViewportTouchMove : Function;
-    private ViewportTouchEnd : Function;
-
-    private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object
-
-    public constructor(module: any, canvasId: string, cppViewport: any) {
-      
-      this.pimpl_ = cppViewport;
-      WasmViewport.viewportsMapByCppHandle_[this.pimpl_] = this;
-      WasmViewport.viewportsMapByCanvasId_[canvasId] = this;
-
-      this.module_ = module;
-      this.canvasId_ = canvasId;
-      this.htmlCanvas_ = document.getElementById(this.canvasId_) as HTMLCanvasElement;
-      if (this.htmlCanvas_ == null) {
-        Logger.defaultLogger.error("Can not create WasmViewport, did not find the canvas whose id is '", this.canvasId_, "'");
-      }
-      this.context_ = this.htmlCanvas_.getContext('2d');
-
-      this.ViewportSetSize = this.module_.cwrap('ViewportSetSize', null, [ 'number', 'number', 'number' ]);
-      this.ViewportRender = this.module_.cwrap('ViewportRender', null, [ 'number', 'number', 'number', 'number' ]);
-      this.ViewportMouseDown = this.module_.cwrap('ViewportMouseDown', null, [ 'number', 'number', 'number', 'number', 'number' ]);
-      this.ViewportMouseMove = this.module_.cwrap('ViewportMouseMove', null, [ 'number', 'number', 'number' ]);
-      this.ViewportMouseUp = this.module_.cwrap('ViewportMouseUp', null, [ 'number' ]);
-      this.ViewportMouseEnter = this.module_.cwrap('ViewportMouseEnter', null, [ 'number' ]);
-      this.ViewportMouseLeave = this.module_.cwrap('ViewportMouseLeave', null, [ 'number' ]);
-      this.ViewportMouseWheel = this.module_.cwrap('ViewportMouseWheel', null, [ 'number', 'number', 'number', 'number', 'number' ]);
-      this.ViewportKeyPressed = this.module_.cwrap('ViewportKeyPressed', null, [ 'number', 'number', 'string', 'number', 'number' ]);
-      this.ViewportTouchStart = this.module_.cwrap('ViewportTouchStart', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
-      this.ViewportTouchMove = this.module_.cwrap('ViewportTouchMove', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
-      this.ViewportTouchEnd = this.module_.cwrap('ViewportTouchEnd', null, [ 'number', 'number', 'number', 'number', 'number', 'number', 'number' ]);
-    }
-
-    public GetCppViewport() : number {
-      return this.pimpl_;
-    }
-
-    public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport | null {
-      if (WasmViewport.viewportsMapByCppHandle_[cppViewportHandle] !== undefined) {
-        return WasmViewport.viewportsMapByCppHandle_[cppViewportHandle];
-      }
-      Logger.defaultLogger.error("WasmViewport not found !");
-      return null;
-    }
-
-    public static GetFromCanvasId(canvasId: string) : WasmViewport | null {
-      if (WasmViewport.viewportsMapByCanvasId_[canvasId] !== undefined) {
-        return WasmViewport.viewportsMapByCanvasId_[canvasId];
-      }
-      Logger.defaultLogger.error("WasmViewport not found !");
-      return null;
-    }
-
-    public static ResizeAll() {
-      for (let canvasId in WasmViewport.viewportsMapByCanvasId_) {
-        WasmViewport.viewportsMapByCanvasId_[canvasId].Resize();
-      }
-    }
-
-    public Redraw() {
-      if (this.imageData_ === null ||
-          this.renderingBuffer_ === null ||
-          this.ViewportRender(this.pimpl_,
-                         this.imageData_.width,
-                         this.imageData_.height,
-                         this.renderingBuffer_) == 0) {
-        Logger.defaultLogger.error('The rendering has failed');
-      } else {
-        // Create an accessor to the rendering buffer (i.e. create a
-        // "window" above the heap of the WASM module), then copy it to
-        // the ImageData object
-        this.imageData_.data.set(new Uint8ClampedArray(
-          this.module_.HEAPU8.buffer,
-          this.renderingBuffer_,
-          this.imageData_.width * this.imageData_.height * 4));
-        
-        if (this.context_) {
-          this.context_.putImageData(this.imageData_, 0, 0);
-        }
-      }
-    }
-  
-    public Resize() {
-      if (this.imageData_ != null &&
-          (this.imageData_.width != window.innerWidth ||
-           this.imageData_.height != window.innerHeight)) {
-        this.imageData_ = null;
-      }
-      
-      // width/height is defined by the parent width/height
-      if (this.htmlCanvas_.parentElement) {
-        this.htmlCanvas_.width = this.htmlCanvas_.parentElement.offsetWidth;  
-        this.htmlCanvas_.height = this.htmlCanvas_.parentElement.offsetHeight;  
-
-        Logger.defaultLogger.debug("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height);
-
-        if (this.imageData_ === null && this.context_) {
-          this.imageData_ = this.context_.getImageData(0, 0, this.htmlCanvas_.width, this.htmlCanvas_.height);
-          this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height);
-    
-          if (this.renderingBuffer_ != null) {
-            this.module_._free(this.renderingBuffer_);
-          }
-          
-          this.renderingBuffer_ = this.module_._malloc(this.imageData_.width * this.imageData_.height * 4);
-        } else {
-          this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height);
-        }
-        
-        this.Redraw();
-      }
-    }
-
-    public Initialize() {
-      
-      // Force the rendering of the viewport for the first time
-      this.Resize();
-    
-      var that : WasmViewport = this;
-      // Register an event listener to call the Resize() function 
-      // each time the window is resized.
-      window.addEventListener('resize', function(event) {
-        that.Resize();
-      }, false);
-  
-      this.htmlCanvas_.addEventListener('contextmenu', function(event) {
-        // Prevent right click on the canvas
-        event.preventDefault();
-      }, false);
-      
-      this.htmlCanvas_.addEventListener('mouseleave', function(event) {
-        that.ViewportMouseLeave(that.pimpl_);
-      });
-      
-      this.htmlCanvas_.addEventListener('mouseenter', function(event) {
-        that.ViewportMouseEnter(that.pimpl_);
-      });
-    
-      this.htmlCanvas_.addEventListener('mousedown', function(event) {
-        var x = event.pageX - this.offsetLeft;
-        var y = event.pageY - this.offsetTop;
-
-       that.ViewportMouseDown(that.pimpl_, event.button, x, y, 0 /* TODO detect modifier keys*/);    
-      });
-    
-      this.htmlCanvas_.addEventListener('mousemove', function(event) {
-        var x = event.pageX - this.offsetLeft;
-        var y = event.pageY - this.offsetTop;
-        that.ViewportMouseMove(that.pimpl_, x, y);
-      });
-    
-      this.htmlCanvas_.addEventListener('mouseup', function(event) {
-        that.ViewportMouseUp(that.pimpl_);
-      });
-    
-      window.addEventListener('keydown', function(event) {
-        var keyChar: string | null = event.key;
-        var keyCode = event.keyCode
-        if (keyChar.length == 1) {
-          keyCode = 0; // maps to OrthancStone::KeyboardKeys_Generic
-        } else {
-          keyChar = null;
-        }
-//        console.log("key: ", keyCode, keyChar);
-        that.ViewportKeyPressed(that.pimpl_, keyCode, keyChar, event.shiftKey, event.ctrlKey, event.altKey);
-      });
-    
-      this.htmlCanvas_.addEventListener('wheel', function(event) {
-        var x = event.pageX - this.offsetLeft;
-        var y = event.pageY - this.offsetTop;
-        that.ViewportMouseWheel(that.pimpl_, event.deltaY, x, y, event.ctrlKey);
-        event.preventDefault();
-      }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
-
-      this.htmlCanvas_.addEventListener('touchstart', function(event: TouchEvent) {
-        // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
-        event.preventDefault();
-        event.stopPropagation();
-
-        // TODO: find a way to pass the coordinates as an array between JS and C++
-        var x0 = 0;
-        var y0 = 0;
-        var x1 = 0;
-        var y1 = 0;
-        var x2 = 0;
-        var y2 = 0;
-        if (event.targetTouches.length > 0) {
-          x0 = event.targetTouches[0].pageX;
-          y0 = event.targetTouches[0].pageY;
-        }
-        if (event.targetTouches.length > 1) {
-          x1 = event.targetTouches[1].pageX;
-          y1 = event.targetTouches[1].pageY;
-        }
-        if (event.targetTouches.length > 2) {
-          x2 = event.targetTouches[2].pageX;
-          y2 = event.targetTouches[2].pageY;
-        }
-
-        that.ViewportTouchStart(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
-      }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
-    
-      this.htmlCanvas_.addEventListener('touchend', function(event) {
-        // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
-        event.preventDefault();
-        event.stopPropagation();
-
-        // TODO: find a way to pass the coordinates as an array between JS and C++
-        var x0 = 0;
-        var y0 = 0;
-        var x1 = 0;
-        var y1 = 0;
-        var x2 = 0;
-        var y2 = 0;
-        if (event.targetTouches.length > 0) {
-          x0 = event.targetTouches[0].pageX;
-          y0 = event.targetTouches[0].pageY;
-        }
-        if (event.targetTouches.length > 1) {
-          x1 = event.targetTouches[1].pageX;
-          y1 = event.targetTouches[1].pageY;
-        }
-        if (event.targetTouches.length > 2) {
-          x2 = event.targetTouches[2].pageX;
-          y2 = event.targetTouches[2].pageY;
-        }
-
-        that.ViewportTouchEnd(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
-      });
-    
-      this.htmlCanvas_.addEventListener('touchmove', function(event: TouchEvent) {
-
-        // don't propagate events to the whole body (this could zoom the entire page instead of zooming the viewport)
-        event.preventDefault();
-        event.stopPropagation();
-
-
-        // TODO: find a way to pass the coordinates as an array between JS and C++
-        var x0 = 0;
-        var y0 = 0;
-        var x1 = 0;
-        var y1 = 0;
-        var x2 = 0;
-        var y2 = 0;
-        if (event.targetTouches.length > 0) {
-          x0 = event.targetTouches[0].pageX;
-          y0 = event.targetTouches[0].pageY;
-        }
-        if (event.targetTouches.length > 1) {
-          x1 = event.targetTouches[1].pageX;
-          y1 = event.targetTouches[1].pageY;
-        }
-        if (event.targetTouches.length > 2) {
-          x2 = event.targetTouches[2].pageX;
-          y2 = event.targetTouches[2].pageY;
-        }
-
-        that.ViewportTouchMove(that.pimpl_, event.targetTouches.length, x0, y0, x1, y1, x2, y2);
-        return;
-
-      }, {passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface
-    }  
-
-  public ResetTouch() {
-    if (this.touchTranslation_ ||
-        this.touchZoom_) {
-      this.ViewportMouseUp(this.pimpl_);
-    }
-
-    this.touchTranslation_ = false;
-    this.touchZoom_ = false;
-  }
-  
-  public GetTouchTranslation(event: any) {
-    var touch = event.targetTouches[0];
-    return [
-      touch.pageX,
-      touch.pageY
-    ];
-  }
-    
-  public GetTouchZoom(event: any) {
-    var touch1 = event.targetTouches[0];
-    var touch2 = event.targetTouches[1];
-    var dx = (touch1.pageX - touch2.pageX);
-    var dy = (touch1.pageY - touch2.pageY);
-    var d = Math.sqrt(dx * dx + dy * dy);
-    return [
-      (touch1.pageX + touch2.pageX) / 2.0,
-      (touch1.pageY + touch2.pageY) / 2.0,
-      d
-    ];
-  }
-   
-}
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Wed Apr 29 22:06:24 2020 +0200
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Wed Apr 29 22:06:58 2020 +0200
@@ -47,18 +47,14 @@
     message(FATAL_ERROR "Cannot enable SDL in sandboxed environments")
   endif()
 
-  if (ENABLE_QT)
-    message(FATAL_ERROR "Cannot enable QT in sandboxed environments")
-  endif()
-
   if (ENABLE_SSL)
     message(FATAL_ERROR "Cannot enable SSL in sandboxed environments")
   endif()
 endif()
 
 if (ENABLE_OPENGL)
-  if (NOT ENABLE_QT AND NOT ENABLE_SDL AND NOT ENABLE_WASM)
-    message(FATAL_ERROR "Cannot enable OpenGL if WebAssembly, SDL and Qt are all disabled")
+  if (NOT ENABLE_SDL AND NOT ENABLE_WASM)
+    message(FATAL_ERROR "Cannot enable OpenGL if WebAssembly and SDL are both disabled")
   endif()
 endif()
 
@@ -111,32 +107,17 @@
 endif()
 
 
-if (ENABLE_SDL AND ENABLE_QT)
-  message("SDL and QT cannot not be enabled together")
-elseif(ENABLE_SDL)
+if(ENABLE_SDL)
   message("SDL is enabled")
   include(${CMAKE_CURRENT_LIST_DIR}/SdlConfiguration.cmake)
   add_definitions(
-    -DORTHANC_ENABLE_QT=0
     -DORTHANC_ENABLE_SDL=1
     )
-elseif(ENABLE_QT)
-  add_definitions(
-    -DORTHANC_ENABLE_QT=1
-    -DORTHANC_ENABLE_SDL=0
-    )
-  if(DISABLE_STONE_QT_CMAKE_FILE)
-    message("QT is enabled, but QtConfiguration.cmake will not be included")  
-  else()    
-    message("QT is enabled")  
-    include(${CMAKE_CURRENT_LIST_DIR}/QtConfiguration.cmake)
-  endif()
 else()
-  message("SDL and QT are both disabled")
+  message("SDL is disabled")
   unset(USE_SYSTEM_SDL CACHE)
   add_definitions(
     -DORTHANC_ENABLE_SDL=0
-    -DORTHANC_ENABLE_QT=0
     )
 endif()
 
@@ -258,19 +239,6 @@
   set(PLATFORM_SOURCES
     ${ORTHANC_STONE_ROOT}/Framework/Loaders/GenericLoadersContext.cpp
     ${ORTHANC_STONE_ROOT}/Framework/Loaders/GenericLoadersContext.h
-    ${ORTHANC_STONE_ROOT}/Platforms/Generic/DelayedCallCommand.cpp
-    ${ORTHANC_STONE_ROOT}/Platforms/Generic/DelayedCallCommand.h
-    ${ORTHANC_STONE_ROOT}/Platforms/Generic/Oracle.cpp
-    ${ORTHANC_STONE_ROOT}/Platforms/Generic/Oracle.h
-    ${ORTHANC_STONE_ROOT}/Platforms/Generic/OracleDelayedCallExecutor.h
-    ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServiceCommandBase.cpp
-    ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServiceCommandBase.h
-    ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServiceDeleteCommand.cpp
-    ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServiceDeleteCommand.h
-    ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServiceGetCommand.cpp
-    ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServiceGetCommand.h
-    ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServicePostCommand.cpp
-    ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServicePostCommand.h
     )
 
   if (ENABLE_SDL)
@@ -280,7 +248,7 @@
       )
   endif()
 
-  if (ENABLE_SDL OR ENABLE_QT)
+  if (ENABLE_SDL)
     if (ENABLE_OPENGL)
       list(APPEND ORTHANC_STONE_SOURCES
         ${ORTHANC_STONE_ROOT}/Framework/OpenGL/SdlOpenGLContext.cpp
@@ -290,182 +258,6 @@
         )
     endif()
   endif()
-elseif (ENABLE_WASM)
-  set(STONE_WASM_SOURCES
-    ${ORTHANC_STONE_ROOT}/Platforms/Wasm/Defaults.cpp
-    ${ORTHANC_STONE_ROOT}/Platforms/Wasm/WasmDelayedCallExecutor.cpp
-    ${ORTHANC_STONE_ROOT}/Platforms/Wasm/WasmWebService.cpp
-    ${ORTHANC_STONE_ROOT}/Platforms/Wasm/WasmViewport.cpp
-    ${ORTHANC_STONE_ROOT}/Platforms/Wasm/WasmPlatformApplicationAdapter.cpp
-    ${AUTOGENERATED_DIR}/WasmWebService.c
-    ${AUTOGENERATED_DIR}/default-library.c
-  )
-
-  # Regenerate a dummy "WasmWebService.c" file each time the "WasmWebService.js" file
-  # is modified, so as to force a new execution of the linking
-  add_custom_command(
-    OUTPUT "${AUTOGENERATED_DIR}/WasmWebService.c"
-    COMMAND ${CMAKE_COMMAND} -E touch "${AUTOGENERATED_DIR}/WasmWebService.c" ""
-    DEPENDS "${ORTHANC_STONE_ROOT}/Platforms/Wasm/WasmWebService.js")
-  add_custom_command(
-    OUTPUT "${AUTOGENERATED_DIR}/WasmDelayedCallExecutor.c"
-    COMMAND ${CMAKE_COMMAND} -E touch "${AUTOGENERATED_DIR}/WasmDelayedCallExecutor.c" ""
-    DEPENDS "${ORTHANC_STONE_ROOT}/Platforms/Wasm/WasmDelayedCallExecutor.js")
-  add_custom_command(
-    OUTPUT "${AUTOGENERATED_DIR}/default-library.c"
-    COMMAND ${CMAKE_COMMAND} -E touch "${AUTOGENERATED_DIR}/default-library.c" ""
-    DEPENDS "${ORTHANC_STONE_ROOT}/Platforms/Wasm/default-library.js")
-endif()
-
-if (ENABLE_STONE_DEPRECATED)
-  if (NOT ORTHANC_SANDBOXED)
-    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()
-
-  if (ENABLE_SDL OR ENABLE_QT)
-    list(APPEND APPLICATIONS_SOURCES
-      ${ORTHANC_STONE_ROOT}/Applications/Generic/NativeStoneApplicationRunner.cpp
-      ${ORTHANC_STONE_ROOT}/Applications/Generic/NativeStoneApplicationContext.cpp
-      )
-  endif()
-
-  if (ENABLE_SDL)
-    list(APPEND APPLICATIONS_SOURCES
-      ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlCairoSurface.cpp
-      ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlEngine.cpp
-      ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlOrthancSurface.cpp
-      ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlStoneApplicationRunner.cpp
-      )
-  endif()
-
-  if (ENABLE_WASM)
-    list(APPEND APPLICATIONS_SOURCES
-      ${ORTHANC_STONE_ROOT}/Applications/Wasm/StartupParametersBuilder.cpp
-      )
-  endif()
-
-  if (ENABLE_THREADS)
-    list(APPEND ORTHANC_STONE_SOURCES
-      ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Messages/LockingEmitter.cpp
-      ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Messages/LockingEmitter.h
-      )
-  endif()
-
-  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/DicomStructureSetLoader2.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Loaders/DicomStructureSetLoader2.h
-    ${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/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/Widgets/ZoomMouseTracker.h
-
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyAlphaLayer.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyDicomLayer.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayer.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayerCropTracker.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayerMaskTracker.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayerMoveTracker.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayerResizeTracker.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayerRotateTracker.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyMaskLayer.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyScene.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographySceneCommand.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographySceneReader.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographySceneWriter.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyTextLayer.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyWidget.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyWindowingTracker.cpp
-    )
 endif()
 
 
@@ -495,17 +287,7 @@
     )
 endif()
 
-if (ENABLE_SDL OR ENABLE_WASM)
-list(APPEND APPLICATIONS_SOURCES
-  ${ORTHANC_STONE_ROOT}/Applications/Generic/GuiAdapter.cpp
-  ${ORTHANC_STONE_ROOT}/Applications/Generic/GuiAdapter.h
-  )
-endif()
-
-
 list(APPEND ORTHANC_STONE_SOURCES
-  #${ORTHANC_STONE_ROOT}/Framework/Layers/SeriesFrameRendererFactory.cpp
-  #${ORTHANC_STONE_ROOT}/Framework/Layers/SingleFrameRendererFactory.cpp
 
   ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomDatasetReader.cpp
   ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomPath.cpp
--- a/Resources/CMake/ProtobufCodeGeneration.cmake	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-# HOW TO USE:
-# the GenerateCodeFromProtobufSchema will generate files in ${CMAKE_BINARY_DIR} and will
-# populate PROTOBUF_AUTOGENERATED_SOURCES with the list of generated files
-# AS OF 2019-01-30, it requires protoc (version 3.6.1.x) to be available in the path
-set(PROTOBUF_AUTOGENERATED_SOURCES)
-
-# TODO: use find_program (<VAR> name1 [path1 path2 ...]) to located the protobuf compiler
-# TODO: automated the TS plugin installation
-
-macro(GenerateCodeFromProtobufSchema schemaFilePath outputBaseDirectory)
-  # extract file name
-  GetFilePathWithoutLastExtension(schemaFilePathWithoutExt ${schemaFilePath})
-  
-  # remove extension
-  GetFilenameFromPath(schemaFileNameWithoutExt ${schemaFilePathWithoutExt})
-  
-  set(generatedFilePathWithoutExtension "${CMAKE_BINARY_DIR}/AUTOGENERATED/${schemaFileNameWithoutExt}")
-  set(generatedCppSourceFilePath "${generatedFilePathWithoutExtension}.pb.cc")
-  set(generatedCppHeaderFilePath "${generatedFilePathWithoutExtension}.pb.h")
-  set(generatedJsFilePath "${generatedFilePathWithoutExtension}_pb.js")
-  set(generatedTsFilePath "${generatedFilePathWithoutExtension}_pb.d.ts")
-  # set(generatedJsFileName "${generatedFilePathWithoutExtension}.js")
-
-  # set(AUTOGENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/AUTOGENERATED")
-  # set(AUTOGENERATED_SOURCES)
-  
-  set(PROTOC_EXECUTABLE "PROTOC")
-  find_program(PROTOC_EXECUTABLE_SEARCH ${FLATC_EXECUTABLE})
-  if(NOT PROTOC_EXECUTABLE_SEARCH)
-    message(FATAL_ERROR "The Protocol Buffers compiler (protoc[.exe]) cannot be found!")
-  endif()
-
-  # TODO CUSTOMIZE FOR TYPESCRIPT
-  set(SCRIPT_CPP_OPTIONS)
-  list(APPEND SCRIPT_CPP_OPTIONS "----cpp_out=${CMAKE_BINARY_DIR}/AUTOGENERATED")
-  # list(APPEND SCRIPT_CPP_OPTIONS "gnagna")
-  
-  set(SCRIPT_TS_OPTIONS)
-
-  list(APPEND SCRIPT_TS_OPTIONS "--ts")
-  list(APPEND SCRIPT_TS_OPTIONS "gnagna")
-
-  add_custom_command(
-    OUTPUT
-    ${generatedCppSourceFilePath}
-    ${generatedCppHeaderFilePath}
-    COMMAND 
-    ${PROTOC_EXECUTABLE} ${SCRIPT_CPP_OPTIONS} ${schemaFilePath}
-    DEPENDS
-    ${schemaFilePath}
-    )
-  
-  add_custom_command(
-    OUTPUT
-    ${generatedTsFileName}
-    ${generatedJsFilePath}
-    COMMAND 
-    ${PROTOC_EXECUTABLE} ${SCRIPT_TS_OPTIONS} ${schemaFilePath}
-    DEPENDS
-    ${schemaFilePath}
-    )
-  
-  # add_custom_command(
-  #   OUTPUT
-  #   ${generatedJsFileName}
-  #   COMMAND 
-  #   ${FLATC_EXECUTABLE} ${SCRIPT_JS_OPTIONS} ${schemaFilePath}
-  #   DEPENDS
-  #   ${schemaFilePath}
-  #   )
-
-    list(APPEND FLATC_AUTOGENERATED_SOURCES "${generatedCppFileName}") 
-    # list(APPEND FLATC_AUTOGENERATED_SOURCES "${generatedJsFileName}") 
-    list(APPEND FLATC_AUTOGENERATED_SOURCES "${generatedTsFileName}") 
-
-endmacro()
-
--- a/Resources/CMake/QtConfiguration.cmake	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,236 +0,0 @@
-# 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/>.
-
-
-set(CMAKE_AUTOMOC OFF)
-set(CMAKE_AUTOUIC OFF)
-
-
-## Note that these set of macros MUST be defined as a "function()",
-## otherwise it fails
-function(DEFINE_QT_MACROS)
-  include(Qt4Macros)
-
-  ##
-  ## This part is adapted from file "Qt4Macros.cmake" shipped with
-  ## CMake 3.5.1, released under the following license:
-  ##
-  ##=============================================================================
-  ## Copyright 2005-2009 Kitware, Inc.
-  ##
-  ## Distributed under the OSI-approved BSD License (the "License");
-  ## see accompanying file Copyright.txt for details.
-  ##
-  ## This software is distributed WITHOUT ANY WARRANTY; without even the
-  ## implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-  ## See the License for more information.
-  ##=============================================================================
-  ## 
-  macro (ORTHANC_QT_WRAP_UI outfiles)
-    QT4_EXTRACT_OPTIONS(ui_files ui_options ui_target ${ARGN})
-    foreach (it ${ui_files})
-      get_filename_component(outfile ${it} NAME_WE)
-      get_filename_component(infile ${it} ABSOLUTE)
-      set(outfile ${CMAKE_CURRENT_BINARY_DIR}/ui_${outfile}.h)
-      add_custom_command(OUTPUT ${outfile}
-        COMMAND ${QT_UIC_EXECUTABLE}
-        ARGS ${ui_options} -o ${outfile} ${infile}
-        MAIN_DEPENDENCY ${infile} VERBATIM)
-      set(${outfiles} ${${outfiles}} ${outfile})
-    endforeach ()
-  endmacro ()
-  
-  macro (ORTHANC_QT_WRAP_CPP outfiles )
-    QT4_GET_MOC_FLAGS(moc_flags)
-    QT4_EXTRACT_OPTIONS(moc_files moc_options moc_target ${ARGN})
-    foreach (it ${moc_files})
-      get_filename_component(outfile ${it} NAME_WE)
-      get_filename_component(infile ${it} ABSOLUTE)
-      set(outfile ${CMAKE_CURRENT_BINARY_DIR}/moc_${outfile}.cxx)
-      add_custom_command(OUTPUT ${outfile}
-        COMMAND ${QT_MOC_EXECUTABLE}
-        ARGS ${infile} "${moc_flags}" -o ${outfile}
-        MAIN_DEPENDENCY ${infile} VERBATIM)
-      set(${outfiles} ${${outfiles}} ${outfile})
-    endforeach ()
-  endmacro ()
-  ##
-  ## End of "Qt4Macros.cmake" adaptation.
-  ##
-endfunction()
-
-
-if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
-  # Linux Standard Base version 5 ships Qt 4.2.3
-  DEFINE_QT_MACROS()
- 
-  # The script "LinuxStandardBaseUic.py" is just a wrapper around the
-  # "uic" compiler from LSB that does not support the "<?xml ...?>"
-  # header that is automatically added by Qt Creator
-  set(QT_UIC_EXECUTABLE ${CMAKE_CURRENT_LIST_DIR}/LinuxStandardBaseUic.py)
-
-  set(QT_MOC_EXECUTABLE ${LSB_PATH}/bin/moc)
-
-  include_directories(
-    ${LSB_PATH}/include/QtCore
-    ${LSB_PATH}/include/QtGui
-    ${LSB_PATH}/include/QtOpenGL
-    )
-
-  link_libraries(QtCore QtGui QtOpenGL)
-
-elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
-  DEFINE_QT_MACROS()
-  
-  include_directories(${QT5_INSTALL_ROOT}/include)
-  link_directories(${QT5_INSTALL_ROOT}/lib)
-
-  if (OFF) #CMAKE_CROSSCOMPILING)
-    set(QT_UIC_EXECUTABLE wine ${QT5_INSTALL_ROOT}/bin/uic.exe)
-    set(QT_MOC_EXECUTABLE wine ${QT5_INSTALL_ROOT}/bin/moc.exe)
-  else()
-    set(QT_UIC_EXECUTABLE ${QT5_INSTALL_ROOT}/bin/uic)
-    set(QT_MOC_EXECUTABLE ${QT5_INSTALL_ROOT}/bin/moc)
-  endif()
-
-  include_directories(
-    ${QT5_INSTALL_ROOT}/include/QtCore
-    ${QT5_INSTALL_ROOT}/include/QtGui
-    ${QT5_INSTALL_ROOT}/include/QtOpenGL
-    ${QT5_INSTALL_ROOT}/include/QtWidgets
-    )
-
-  if (OFF)
-    # Dynamic Qt
-    link_libraries(Qt5Core Qt5Gui Qt5OpenGL Qt5Widgets)
-
-    file(COPY
-      ${QT5_INSTALL_ROOT}/bin/Qt5Core.dll
-      ${QT5_INSTALL_ROOT}/bin/Qt5Gui.dll
-      ${QT5_INSTALL_ROOT}/bin/Qt5OpenGL.dll
-      ${QT5_INSTALL_ROOT}/bin/Qt5Widgets.dll
-      ${QT5_INSTALL_ROOT}/bin/libstdc++-6.dll
-      ${QT5_INSTALL_ROOT}/bin/libgcc_s_dw2-1.dll
-      ${QT5_INSTALL_ROOT}/bin/libwinpthread-1.dll
-      DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
-
-    file(COPY
-      ${QT5_INSTALL_ROOT}/plugins/platforms/qwindows.dll
-      DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/platforms)
-
-  else()
-    # Static Qt
-    link_libraries(
-      ${QT5_INSTALL_ROOT}/lib/libQt5Widgets.a
-      ${QT5_INSTALL_ROOT}/lib/libQt5Gui.a
-      ${QT5_INSTALL_ROOT}/lib/libQt5OpenGL.a
-      ${QT5_INSTALL_ROOT}/lib/libQt5Core.a
-      ${QT5_INSTALL_ROOT}/lib/libqtharfbuzz.a
-      ${QT5_INSTALL_ROOT}/lib/libqtpcre2.a
-      ${QT5_INSTALL_ROOT}/lib/libQt5FontDatabaseSupport.a
-      ${QT5_INSTALL_ROOT}/lib/libQt5EventDispatcherSupport.a
-      ${QT5_INSTALL_ROOT}/lib/libQt5ThemeSupport.a
-      ${QT5_INSTALL_ROOT}/plugins/platforms/libqwindows.a
-      winmm
-      version
-      ws2_32
-      uxtheme
-      imm32
-      dwmapi
-      )
-  endif()
-  
-else()
-  # Not using Windows, not using Linux Standard Base, 
-  # Find the QtWidgets library
-  find_package(Qt5Widgets QUIET)
-
-  if (Qt5Widgets_FOUND)
-    message("Qt5 has been detected")
-    find_package(Qt5Core REQUIRED)
-    link_libraries(
-      Qt5::Widgets
-      Qt5::Core
-      )
-
-    if (ENABLE_OPENGL)
-      find_package(Qt5OpenGL REQUIRED)
-      link_libraries(
-        Qt5::OpenGL
-        )
-    endif()
-    
-    # Create aliases for the CMake commands
-    macro(ORTHANC_QT_WRAP_UI)
-      QT5_WRAP_UI(${ARGN})
-    endmacro()
-    
-    macro(ORTHANC_QT_WRAP_CPP)
-      QT5_WRAP_CPP(${ARGN})
-    endmacro()
-
-  else()
-    message("Qt5 has not been found, trying with Qt4")
-    find_package(Qt4 REQUIRED QtGui)
-    link_libraries(
-      Qt4::QtGui
-      )
-
-    if (ENABLE_OPENGL)
-      find_package(Qt4 REQUIRED QtOpenGL)
-      link_libraries(
-        Qt4::QtOpenGL
-        )
-    endif()
-    
-    # Create aliases for the CMake commands
-    macro(ORTHANC_QT_WRAP_UI)
-      QT4_WRAP_UI(${ARGN})
-    endmacro()
-    
-    macro(ORTHANC_QT_WRAP_CPP)
-      QT4_WRAP_CPP(${ARGN})
-    endmacro()  
-  endif()
-endif()
-
-
-if (ENABLE_STONE_DEPRECATED)
-  list(APPEND QT_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.cpp
-    ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.cpp
-    ${ORTHANC_STONE_ROOT}/Applications/Qt/QtStoneApplicationRunner.cpp
-    )
-
-  ORTHANC_QT_WRAP_CPP(QT_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.h
-    ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.h
-    )
-endif()
-  
-
-# NB: Including CMAKE_CURRENT_BINARY_DIR is mandatory, as the CMake
-# macros for Qt will put their result in that directory, which cannot
-# be changed.
-# https://stackoverflow.com/a/4016784/881731
-
-include_directories(
-  ${ORTHANC_STONE_ROOT}/Applications/Qt/
-  ${CMAKE_CURRENT_BINARY_DIR}
-  )
-
--- a/Resources/CodeGeneration/Graveyard/playground.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,259 +0,0 @@
-/*
-         1         2         3         4         5         6         7
-12345678901234567890123456789012345678901234567890123456789012345678901234567890
-*/
-
-namespace VsolStuff
-{
-  enum EnumMonth0
-  {
-    January,
-    February,
-    March
-  };
-
-  // interface Serializer
-  // {
-  //   Serialize(value: number): string;
-  //   Serialize(value: string): string;
-  //   Serialize(value: EnumMonth0): string;
-  // };
-  function printf(value: any):void
-  {
-    console.log(value)
-  }
-
-  // function StoneSerialize(value: string) : string;
-  // function StoneSerialize(value: number) : string;
-  // function StoneSerialize(value: EnumMonth0) : string;
-  function StoneSerialize<T>(value: T[]) : string;
-
-  function StoneSerialize<T>(value: T[] | EnumMonth0) : string
-  {
-    let valueType = typeof value;
-    printf(`About to serialize value. Type is ${valueType}`)
-    printf(`About to serialize value. Type is ${typeof value}`)
-    return "Choucroute";
-  }
-
-  function main():number
-  {
-    enum Color {Red = 1, Green = 2, Blue = 4}
-    let color: Color = Color.Green;
-    printf("---------------------------");
-    printf(`typeof color: ${typeof color}`);
-    printf("---------------------------");
-    let colors: Color[] = []
-    colors.push(Color.Green);
-    colors.push(Color.Red);
-    printf(`typeof colors: ${typeof colors}`);
-    printf(`Array.isArray(colors): ${Array.isArray(colors)}`);
-    printf("---------------------------");
-    
-
-    let toto:EnumMonth0[] = [];
-
-    toto.push(EnumMonth0.February);
-    toto.push(EnumMonth0.March);
-
-    printf(JSON.stringify(toto));
-
-    return 0;
-
-  }
-
-  main()
-
-//   string StoneSerialize_number(int32_t value)
-//   {
-
-//     Json::Value result(value);
-//     return result;
-//   }
-
-//   Json::Value StoneSerialize(double value)
-//   {
-//     Json::Value result(value);
-//     return result;
-//   }
-
-//   Json::Value StoneSerialize(bool value)
-//   {
-//     Json::Value result(value);
-//     return result;
-//   }
-
-//   Json::Value StoneSerialize(const std::string& value)
-//   {
-//     // the following is better than 
-//     Json::Value result(value.data(),value.data()+value.size());
-//     return result;
-//   }
-
-//   template<typename T>
-//   Json::Value StoneSerialize(const std::map<std::string,T>& value)
-//   {
-//     Json::Value result(Json::objectValue);
-
-//     for (std::map<std::string, T>::const_iterator it = value.cbegin();
-//       it != value.cend(); ++it)
-//     {
-//       // it->first it->second
-//       result[it->first] = StoneSerialize(it->second);
-//     }
-//     return result;
-//   }
-
-//   template<typename T>
-//   Json::Value StoneSerialize(const std::vector<T>& value)
-//   {
-//     Json::Value result(Json::arrayValue);
-//     for (size_t i = 0; i < value.size(); ++i)
-//     {
-//       result.append(StoneSerialize(value[i]));
-//     }
-//     return result;
-//   }
-
-//   enum EnumMonth0
-//   {
-//     January,
-//     February,
-//     March
-//   };
-
-//   std::string ToString(EnumMonth0 value)
-//   {
-//     switch(value)
-//     {
-//       case January: 
-//         return "January";
-//       case February:
-//         return "February";
-//       case March:
-//         return "March";
-//       default:
-//         {
-//           std::stringstream ss;
-//           ss << "Unrecognized EnumMonth0 value (" << static_cast<int64_t>(value) << ")";
-//           throw std::runtime_error(ss.str());
-//         }
-//     }
-//   }
-
-//   void FromString(EnumMonth0& value, std::string strValue)
-//   {
-//     if (strValue == "January" || strValue == "EnumMonth0_January")
-//     {
-//       return January;
-//     }
-//     else if (strValue == "February" || strValue == "EnumMonth0_February")
-//     {
-//       return February;
-//     }
-// #error Not implemented yet
-//   }
-
-//   Json::Value StoneSerialize(const EnumMonth0& value)
-//   {
-//     return StoneSerialize(ToString(value));
-//   }
-//   struct Message1
-//   {
-//     int32_t a;
-//     std::string b;
-//     EnumMonth0 c;
-//     bool d;
-//   };
-
-//   struct Message2
-//   {
-//     std::string toto;
-//     std::vector<Message1> tata;
-//     std::vector<std::string> tutu;
-//     std::map<std::string, std::string> titi;
-//     std::map<std::string, Message1> lulu;
-//   };
-
-//   Json::Value StoneSerialize(const Message1& value)
-//   {
-//     Json::Value result(Json::objectValue);
-//     result["a"] = StoneSerialize(value.a);
-//     result["b"] = StoneSerialize(value.b);
-//     result["c"] = StoneSerialize(value.c);
-//     result["d"] = StoneSerialize(value.d);
-//     return result;
-//   }
-    
-//   Json::Value StoneSerialize(const Message2& value)
-//   {
-//     Json::Value result(Json::objectValue);
-//     result["toto"] = StoneSerialize(value.toto);
-//     result["tata"] = StoneSerialize(value.tata);
-//     result["tutu"] = StoneSerialize(value.tutu);
-//     result["titi"] = StoneSerialize(value.titi);
-//     result["lulu"] = StoneSerialize(value.lulu);
-//     return result;
-//   }
-// }
-
-// int main()
-// {
-//   VsolStuff::Message1 msg1_0;
-//   msg1_0.a = 42;
-//   msg1_0.b = "Benjamin";
-//   msg1_0.c = VsolStuff::January;
-//   msg1_0.d = true;
-
-//   VsolStuff::Message1 msg1_1;
-//   msg1_1.a = 43;
-//   msg1_1.b = "Sandrine";
-//   msg1_1.c = VsolStuff::March;
-//   msg1_0.d = false;
-
-//   // std::string toto;
-//   // std::vector<Message1> tata;
-//   // std::vector<std::string> tutu;
-//   // std::map<int32_t, std::string> titi;
-//   // std::map<int32_t, Message1> lulu;
-
-//   VsolStuff::Message2 msg2_0;
-//   msg2_0.toto = "Prout zizi";
-//   msg2_0.tata.push_back(msg1_0);
-//   msg2_0.tata.push_back(msg1_1);
-//   msg2_0.tutu.push_back("Mercadet");
-//   msg2_0.tutu.push_back("Poisson");
-//   msg2_0.titi["44"] = "key 44";
-//   msg2_0.titi["45"] = "key 45";
-//   msg2_0.lulu["54"] = msg1_1;
-//   msg2_0.lulu["55"] = msg1_0;
-//   auto result = VsolStuff::StoneSerialize(msg2_0);
-//   auto resultStr = result.toStyledString();
-
-//   Json::Value readValue;
-
-//   Json::CharReaderBuilder builder;
-//   Json::CharReader* reader = builder.newCharReader();
-//   std::string errors;
-
-//   bool ok = reader->parse(
-//     resultStr.c_str(),
-//     resultStr.c_str() + resultStr.size(),
-//     &readValue,
-//     &errors
-//   );
-//   delete reader;
-
-//   if (!ok)
-//   {
-//     std::stringstream ss;
-//     ss << "Json parsing error: " << errors;
-//     throw std::runtime_error(ss.str());
-//   }
-//   std::cout << readValue.get("toto", "Default Value").asString() << std::endl;
-//   return 0;
-// }
-
-
-}
-
--- a/Resources/CodeGeneration/Graveyard/playground2.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-class Greeter {
-    greeting: string;
-    constructor(message: string) {
-        this.greeting = message;
-    }
-    greet() {
-        return "Hello, " + this.greeting;
-    }
-}
-enum Color {
-    Red,
-    Green,
-    Blue,
-};
-
-function ColorToString(value: Color)
-{
-    switch (value)
-    {
-        case Color.Red:
-            return "Red";
-        case Color.Green:
-            return "Green";
-        case Color.Blue:
-            return "Blue";
-        default:
-            throw new Error(`Unrecognized Color value(${value})`);
-    }
-}
-
-let color: Color = Color.Red;
-
-document.body.textContent = "<p>---------------------</p>"
-document.body.textContent += "<p>********************************</p>"
-
-class TestMessage {
-    s1: string;
-    s2: Array<string>;
-    s3: Array<Array<string>>;
-    s4: Map<string, number>;
-    s5: Map<number, Array<string>>;
-    s6: Color;
-    s7: boolean;
-}
-
-let tm = new TestMessage();
-tm.s2 = new Array<string>()
-tm.s2.push("toto");
-tm.s2.push("toto2");
-tm.s2.push("toto3");
-tm.s4 = new Map<string, number>();
-tm.s4["toto"] = 42;
-tm.s4["toto"] = 1999;
-tm.s4["tatata"] = 1999;
-tm.s6 = Color.Red;
-tm.s7 = true
-
-let txt = JSON.stringify(tm)
-let txtElem = document.createElement('textarea');
-txtElem.value = txt;
-
-document.body.appendChild(txtElem);
-
-let greeter = new Greeter("world");
-
-let button = document.createElement('button');
-button.textContent = "Say Hello";
-button.onclick = function() {
-    alert(greeter.greet());
-}
-
-document.body.appendChild(button);
--- a/Resources/CodeGeneration/Graveyard/playground3.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,275 +0,0 @@
-/*
-         1         2         3         4         5         6         7
-12345678901234567890123456789012345678901234567890123456789012345678901234567890
-*/
-
-namespace VsolStuff222 {
-  export enum EnumMonth0 {
-    January,
-    February,
-    March
-  };
-
-  export class Message1 {
-    a: number;
-    b: string;
-    c: EnumMonth0;
-    d: boolean;
-    public StoneSerialize(): string {
-      let container: object = {};
-      container['type'] = 'VsolStuff.Message1';
-      container['value'] = this;
-      return JSON.stringify(container);
-    }
-  };
-
-  export class Message2 {
-    toto: string;
-    tata: Message1[];
-    tutu: string[];
-    titi: Map<string, string>;
-    lulu: Map<string, Message1>;
-
-    public StoneSerialize(): string {
-      let container: object = {};
-      container['type'] = 'VsolStuff.Message2';
-      container['value'] = this;
-      return JSON.stringify(container);
-    }
-  };
-}
-
-function printf(value: any): void {
-  console.log(value)
-}
-
-function main(): number {
-
-  let msg1_0 = new VsolStuff.Message1();
-  msg1_0.a = 42;
-  msg1_0.b = "Benjamin";
-  msg1_0.c = VsolStuff.EnumMonth0.January;
-  msg1_0.d = true;
-
-  let msg1_1 = new VsolStuff.Message1();
-  msg1_1.a = 43;
-  msg1_1.b = "Sandrine";
-  msg1_1.c = VsolStuff.EnumMonth0.March;
-  msg1_0.d = false;
-
-  // std::string toto;
-  // std::vector<Message1> tata;
-  // std::vector<std::string> tutu;
-  // std::map<int32_t, std::string> titi;
-  // std::map<int32_t, Message1> lulu;
-
-  let msg2_0 = new VsolStuff.Message2();
-  msg2_0.toto = "Prout zizi";
-  msg2_0.tata = new Array<VsolStuff.Message1>();
-  msg2_0.tata.push(msg1_0);
-  msg2_0.tata.push(msg1_1);
-  msg2_0.tutu.push("Mercadet");
-  msg2_0.tutu.push("Poisson");ing
-  msg2_0.titi["44"] = "key 44";
-  msg2_0.titi["45"] = "key 45";
-  msg2_0.lulu["54"] = msg1_1;
-  msg2_0.lulu["55"] = msg1_0;
-  let result:string = VsolStuff.StoneSerialize(msg2_0);
-  return 0;
-}
-
-main()
-
-//   string StoneSerialize_number(int32_t value)
-//   {
-
-//     Json::Value result(value);
-//     return result;
-//   }
-
-//   Json::Value StoneSerialize(double value)
-//   {
-//     Json::Value result(value);
-//     return result;
-//   }
-
-//   Json::Value StoneSerialize(bool value)
-//   {
-//     Json::Value result(value);
-//     return result;
-//   }
-
-//   Json::Value StoneSerialize(const std::string& value)
-//   {
-//     // the following is better than 
-//     Json::Value result(value.data(),value.data()+value.size());
-//     return result;
-//   }
-
-//   template<typename T>
-//   Json::Value StoneSerialize(const std::map<std::string,T>& value)
-//   {
-//     Json::Value result(Json::objectValue);
-
-//     for (std::map<std::string, T>::const_iterator it = value.cbegin();
-//       it != value.cend(); ++it)
-//     {
-//       // it->first it->second
-//       result[it->first] = StoneSerialize(it->second);
-//     }
-//     return result;
-//   }
-
-//   template<typename T>
-//   Json::Value StoneSerialize(const std::vector<T>& value)
-//   {
-//     Json::Value result(Json::arrayValue);
-//     for (size_t i = 0; i < value.size(); ++i)
-//     {
-//       result.append(StoneSerialize(value[i]));
-//     }
-//     return result;
-//   }
-
-//   enum EnumMonth0
-//   {
-//     January,
-//     February,
-//     March
-//   };
-
-//   std::string ToString(EnumMonth0 value)
-//   {
-//     switch(value)
-//     {
-//       case January: 
-//         return "January";
-//       case February:
-//         return "February";
-//       case March:
-//         return "March";
-//       default:
-//         {
-//           std::stringstream ss;
-//           ss << "Unrecognized EnumMonth0 value (" << static_cast<int64_t>(value) << ")";
-//           throw std::runtime_error(ss.str());
-//         }
-//     }
-//   }
-
-//   void FromString(EnumMonth0& value, std::string strValue)
-//   {
-//     if (strValue == "January" || strValue == "EnumMonth0_January")
-//     {
-//       return January;
-//     }
-//     else if (strValue == "February" || strValue == "EnumMonth0_February")
-//     {
-//       return February;
-//     }
-// #error Not implemented yet
-//   }
-
-//   Json::Value StoneSerialize(const EnumMonth0& value)
-//   {
-//     return StoneSerialize(ToString(value));
-//   }
-//   struct Message1
-//   {
-//     int32_t a;
-//     std::string b;
-//     EnumMonth0 c;
-//     bool d;
-//   };
-
-//   struct Message2
-//   {
-//     std::string toto;
-//     std::vector<Message1> tata;
-//     std::vector<std::string> tutu;
-//     std::map<std::string, std::string> titi;
-//     std::map<std::string, Message1> lulu;
-//   };
-
-//   Json::Value StoneSerialize(const Message1& value)
-//   {
-//     Json::Value result(Json::objectValue);
-//     result["a"] = StoneSerialize(value.a);
-//     result["b"] = StoneSerialize(value.b);
-//     result["c"] = StoneSerialize(value.c);
-//     result["d"] = StoneSerialize(value.d);
-//     return result;
-//   }
-
-//   Json::Value StoneSerialize(const Message2& value)
-//   {
-//     Json::Value result(Json::objectValue);
-//     result["toto"] = StoneSerialize(value.toto);
-//     result["tata"] = StoneSerialize(value.tata);
-//     result["tutu"] = StoneSerialize(value.tutu);
-//     result["titi"] = StoneSerialize(value.titi);
-//     result["lulu"] = StoneSerialize(value.lulu);
-//     return result;
-//   }
-// }
-
-// int main()
-// {
-//   VsolStuff::Message1 msg1_0;
-//   msg1_0.a = 42;
-//   msg1_0.b = "Benjamin";
-//   msg1_0.c = VsolStuff::January;
-//   msg1_0.d = true;
-
-//   VsolStuff::Message1 msg1_1;
-//   msg1_1.a = 43;
-//   msg1_1.b = "Sandrine";
-//   msg1_1.c = VsolStuff::March;
-//   msg1_0.d = false;
-
-//   // std::string toto;
-//   // std::vector<Message1> tata;
-//   // std::vector<std::string> tutu;
-//   // std::map<int32_t, std::string> titi;
-//   // std::map<int32_t, Message1> lulu;
-
-//   VsolStuff::Message2 msg2_0;
-//   msg2_0.toto = "Prout zizi";
-//   msg2_0.tata.push_back(msg1_0);
-//   msg2_0.tata.push_back(msg1_1);
-//   msg2_0.tutu.push_back("Mercadet");
-//   msg2_0.tutu.push_back("Poisson");
-//   msg2_0.titi["44"] = "key 44";
-//   msg2_0.titi["45"] = "key 45";
-//   msg2_0.lulu["54"] = msg1_1;
-//   msg2_0.lulu["55"] = msg1_0;
-//   auto result = VsolStuff::StoneSerialize(msg2_0);
-//   auto resultStr = result.toStyledString();
-
-//   Json::Value readValue;
-
-//   Json::CharReaderBuilder builder;
-//   Json::CharReader* reader = builder.newCharReader();
-//   std::string errors;
-
-//   bool ok = reader->parse(
-//     resultStr.c_str(),
-//     resultStr.c_str() + resultStr.size(),
-//     &readValue,
-//     &errors
-//   );
-//   delete reader;
-
-//   if (!ok)
-//   {
-//     std::stringstream ss;
-//     ss << "Json parsing error: " << errors;
-//     throw std::runtime_error(ss.str());
-//   }
-//   std::cout << readValue.get("toto", "Default Value").asString() << std::endl;
-//   return 0;
-// }
-
-
-}
-
--- a/Resources/CodeGeneration/Graveyard/playground4.py	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-testYaml = """
-enum SomeEnum:
- - january
- - feb
-
-struct Message0:
- a: string
-
-struct Message1:
- a: string
- b: int32
- c: vector<Message0>
- d: SomeEnum = january
- e: SomeEnum= january
- f: SomeEnum=january
- g: SomeEnum =january
-  
-
-# github.com/AlDanial/cloc
-header2 : 
-  cloc_version       : 1.67
-  elapsed_seconds    : int32_t
-
-header : 
-  cloc_version       : 1.67
-  elapsed_seconds    : int32_t
-  cloc_url           : vector<map<string,int32>>
-  n_files            : 1
-  n_lines            : 3
-  files_per_second   : 221.393718659277
-  lines_per_second   : 664.181155977831
-  report_file        : IDL.idl.yaml
-IDL :
-  nFiles: 1
-  blank: 0
-  comment: 2
-  code: 1
-EnumSUM: 
-  - aaa
-  - bbb
-
-SUM: 
-  blank: 0
-  comment: 2
-  code: 1
-  nFiles: 1
-"""
-
-import yaml
-
-b = yaml.load(testYaml)
-print(b)
-
-c = {
-  'enum SomeEnum': ['january', 'feb'], 
-  'struct Message0': {'a': 'string'}, 
-  'struct Message1': {
-    'a': 'string', 
-    'b': 'int32', 
-    'c': 'vector<Message0>', 
-    'd': 'vector<map<string,int32>>', 
-    'e': 'SomeEnum= january', 
-    'f': 'SomeEnum=january', 
-    'g': 'SomeEnum =january'
-  }, 
-}
-
-print(c)
\ No newline at end of file
--- a/Resources/CodeGeneration/Graveyard/runts.ps1	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-# echo "+----------------------+"
-# echo "|    playground.ts     |"
-# echo "+----------------------+"
-
-# tsc -t ES2015 .\playground.ts; node .\playground.js
-
-# echo "+----------------------+"
-# echo "|    playground3.ts     |"
-# echo "+----------------------+"
-
-# tsc -t ES2015 .\playground3.ts; node .\playground3.js
-
-echo "+----------------------+"
-echo "|      stonegen        |"
-echo "+----------------------+"
-
-if(-not (test-Path "build")) {
-  mkdir "build"
-}
-
-echo "Generate the TS and CPP wrapper... (to build/)"
-python stonegentool.py -o "." test_data/test1.yaml
-if($LASTEXITCODE -ne 0) {
-  Write-Error ("Code generation failed!")
-  exit $LASTEXITCODE
-}
-
-echo "Compile the TS wrapper to JS... (in build/)"
-tsc --module commonjs --sourceMap -t ES2015 --outDir "build/" VsolMessages_generated.ts
-if($LASTEXITCODE -ne 0) {
-  Write-Error ("Code compilation failed!")
-  exit $LASTEXITCODE
-}
-
-echo "Compile the test app..."
-tsc --module commonjs --sourceMap -t ES2015 --outDir "build/" test_stonegen.ts
-if($LASTEXITCODE -ne 0) {
-  Write-Error ("Code compilation failed!")
-  exit $LASTEXITCODE
-}
-
-browserify "build/test_stonegen.js" "build/VsolMessages_generated.js" -o "build_browser/test_stonegen_fused.js"
-
-cp .\test_stonegen.html .\build_browser\
-
-echo "Run the test app..."
-Push-Location
-cd build_browser
-node .\test_stonegen_fused.js
-Pop-Location
-if($LASTEXITCODE -ne 0) {
-  Write-Error ("Code execution failed!")
-  exit $LASTEXITCODE
-}
-
-
-
--- a/Resources/CodeGeneration/Graveyard/test_stonegen.html	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-<script type="text/javascript" src="test_stonegen_fused.js"></script>
--- a/Resources/CodeGeneration/Graveyard/test_stonegen.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,206 +0,0 @@
-import * as VsolMessages from "./VsolMessages_generated";
-
-function TEST_StoneGen_SerializeComplex() {
-  let msg1_0 = new VsolMessages.Message1();
-  msg1_0.a = 42;
-  msg1_0.b = "Benjamin";
-  msg1_0.c = VsolMessages.EnumMonth0.January;
-  msg1_0.d = true;
-  let msg1_1 = new VsolMessages.Message1();
-  msg1_1.a = 43;
-  msg1_1.b = "Sandrine";
-  msg1_1.c = VsolMessages.EnumMonth0.March;
-  msg1_0.d = false;
-  let result1_0 = msg1_0.StoneSerialize();
-  let resultStr1_0 = JSON.stringify(result1_0);
-  let result1_1 = msg1_1.StoneSerialize();
-  let resultStr1_1 = JSON.stringify(result1_1);
-  // std::string toto;
-  // std::vector<Message1> tata;
-  // std::vector<std::string> tutu;
-  // std::map<int32_t, std::string> titi;
-  // std::map<int32_t, Message1> lulu;
-  let msg2_0 = new VsolMessages.Message2();
-  msg2_0.toto = "Prout zizi";
-  msg2_0.tata.push(msg1_0);
-  msg2_0.tata.push(msg1_1);
-  msg2_0.tutu.push("Mercadet");
-  msg2_0.tutu.push("Poisson");
-  msg2_0.titi["44"] = "key 44";
-  msg2_0.titi["45"] = "key 45";
-  msg2_0.lulu["54"] = msg1_1;
-  msg2_0.lulu["55"] = msg1_0;
-  let result2 = msg2_0.StoneSerialize();
-  let resultStr2 = JSON.stringify(result2);
-  let refResult2 = `{
-"type" : "VsolMessages.Message2",
-"value" : 
-{
-  "lulu" : 
-  {
-    "54" : 
-    {
-      "a" : 43,
-      "b" : "Sandrine",
-      "c" : 2,
-      "d" : true
-    },
-    "55" : 
-    {
-      "a" : 42,
-      "b" : "Benjamin",
-      "c" : 0,
-      "d" : false
-    }
-  },
-  "tata" : 
-  [
-    {
-      "a" : 42,
-      "b" : "Benjamin",
-      "c" : 0,
-      "d" : false
-    },
-    {
-      "a" : 43,
-      "b" : "Sandrine",
-      "c" : 2,
-      "d" : true
-    }
-  ],
-  "titi" : 
-  {
-    "44" : "key 44",
-    "45" : "key 45"
-  },
-  "toto" : "Prout zizi",
-  "tutu" : 
-  [
-    "Mercadet",
-    "Poisson"
-  ]
-}
-}
-`;
-  let refResult2Obj = JSON.parse(refResult2);
-  let resultStr2Obj = JSON.parse(resultStr2);
-  if (false) {
-    if (refResult2Obj !== resultStr2Obj) {
-      console.log("Results are different!");
-      console.log(`refResult2Obj['value']['lulu']['54'] = ${refResult2Obj['value']['lulu']['54']}`);
-      console.log(`refResult2Obj['value']['lulu']['54']['a'] = ${refResult2Obj['value']['lulu']['54']['a']}`);
-      console.log("************************************************************");
-      console.log("**                  REFERENCE OBJ                         **");
-      console.log("************************************************************");
-      console.log(refResult2Obj);
-      console.log("************************************************************");
-      console.log("**                  ACTUAL OBJ                            **");
-      console.log("************************************************************");
-      console.log(resultStr2Obj);
-      console.log("************************************************************");
-      console.log("**                  REFERENCE                             **");
-      console.log("************************************************************");
-      console.log(refResult2);
-      console.log("************************************************************");
-      console.log("**                  ACTUAL                                **");
-      console.log("************************************************************");
-      console.log(resultStr2);
-      throw new Error("Wrong serialization");
-    }
-  }
-  let refResultValue = JSON.parse(resultStr2);
-  console.log(refResultValue);
-}
-class MyDispatcher {
-  message1: VsolMessages.Message1;
-  message2: VsolMessages.Message2;
-
-  HandleMessage1(value: VsolMessages.Message1) {
-    this.message1 = value;
-    return true;
-  }
-  HandleMessage2(value: VsolMessages.Message2) {
-    this.message2 = value;
-    return true;
-  }
-  HandleA(value) {
-    return true;
-  }
-  HandleB(value) {
-    return true;
-  }
-  HandleC(value) {
-    return true;
-  }
-}
-;
-function TEST_StoneGen_DeserializeOkAndNok() {
-  let serializedMessage = `{
-"type" : "VsolMessages.Message2",
-"value" : 
-{
-  "lulu" : 
-  {
-    "54" : 
-    {
-      "a" : 43,
-      "b" : "Sandrine",
-      "c" : 2,
-      "d" : true
-    },
-    "55" : 
-    {
-      "a" : 42,
-      "b" : "Benjamin",
-      "c" : 0,
-      "d" : false
-    }
-  },
-  "tata" : 
-  [
-    {
-      "a" : 42,
-      "b" : "Benjamin",
-      "c" : 0,
-      "d" : false
-    },
-    {
-      "a" : 43,
-      "b" : "Sandrine",
-      "c" : 2,
-      "d" : true
-    }
-  ],
-  "titi" : 
-  {
-    "44" : "key 44",
-    "45" : "key 45"
-  },
-  "toto" : "Prout zizi",
-  "tutu" : 
-  [
-    "Mercadet",
-    "Poisson"
-  ]
-}
-}`;
-  let myDispatcher = new MyDispatcher();
-  let ok = VsolMessages.StoneDispatchToHandler(serializedMessage, myDispatcher);
-  if (!ok) {
-    throw Error("Error when dispatching message!");
-  }
-  if (myDispatcher.message1 != undefined) {
-    throw Error("(myDispatcher.Message1 != undefined)");
-  }
-  if (myDispatcher.message2 == undefined) {
-    throw Error("(myDispatcher.Message2 == undefined)");
-  }
-  console.log("TEST_StoneGen_DeserializeOkAndNok: OK!");
-}
-function main() {
-  console.log("Entering main()");
-  TEST_StoneGen_SerializeComplex();
-  TEST_StoneGen_DeserializeOkAndNok();
-  return 0;
-}
-console.log(`Exit code is: ${main()}`);
\ No newline at end of file
--- a/Resources/CodeGeneration/Graveyard/tsconfig.json	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-{
-  // "extends": "../../../../../orthanc-stone/Platforms/Wasm/tsconfig-stone",
-  "compilerOptions": {
-    // "outFile": "../../../WebApplication-build/to-embed/app.js",
-    // "module": "system",
-    // "sourceMap": false,
-    "lib": [
-      "es2017",
-      "es2017",
-      "dom",
-      "dom.iterable"
-    ]
-  },
-  "include": [
-    // "commands/*.ts",
-    // "logger.ts",
-    // "app.ts",
-    // "main.ts",
-    // "ui.ts",
-    // "popup.ts"
-  ]
-}
--- a/Resources/CodeGeneration/README.md	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-Requirements
-----------------
-
-Install Node and npm.
-
-Then:
-- `npm install browserify`
-- `npm install typescript`
-- `npm install tsify`
-
-`testCppHandler` contains a C++ project that produces an executable 
-slurping a set of text files representing messages defined against 
-the `test_data/testTestStoneCodeGen.yaml' schema and dumping them to `cout`.
-
-'testWasmIntegrated` contains a small Web app demonstrating the 
-interaction between TypeScript and C++ in WASM.
-source ~/apps/emsdk/emsdk_env.sh
-
-
-Install Python and the following packages `pip install pyyaml yamlloader jinja2`
--- a/Resources/CodeGeneration/stonegentool.py	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,605 +0,0 @@
-import json
-import yaml
-import re
-import os
-import sys
-from jinja2 import Template
-from io import StringIO
-import time
-import datetime
-import yamlloader
-
-"""
-         1         2         3         4         5         6         7
-12345678901234567890123456789012345678901234567890123456789012345678901234567890
-"""
-
-# see https://stackoverflow.com/a/2504457/2927708
-def trim(docstring):
-    if not docstring:
-        return ''
-    # Convert tabs to spaces (following the normal Python rules)
-    # and split into a list of lines:
-    lines = docstring.expandtabs().splitlines()
-    # Determine minimum indentation (first line doesn't count):
-    indent = sys.maxsize
-    for line in lines[1:]:
-        stripped = line.lstrip()
-        if stripped:
-            indent = min(indent, len(line) - len(stripped))
-    # Remove indentation (first line is special):
-    trimmed = [lines[0].strip()]
-    if indent < sys.maxsize:
-        for line in lines[1:]:
-            trimmed.append(line[indent:].rstrip())
-    # Strip off trailing and leading blank lines:
-    while trimmed and not trimmed[-1]:
-        trimmed.pop()
-    while trimmed and not trimmed[0]:
-        trimmed.pop(0)
-    # Return a single string:
-    return '\n'.join(trimmed)
-
-class JsonHelpers:
-    """A set of utilities to perform JSON operations"""
-
-    @staticmethod
-    def removeCommentsFromJsonContent(string):
-        """
-      Remove comments from a JSON file
-
-      Comments are not allowed in JSON but, i.e., Orthanc configuration files
-      contains C++ like comments that we need to remove before python can
-      parse the file
-      """
-        # remove all occurrence streamed comments (/*COMMENT */) from string
-        string = re.sub(re.compile("/\*.*?\*/", re.DOTALL), "", string)
-
-        # remove all occurrence singleline comments (//COMMENT\n ) from string
-        string = re.sub(re.compile("//.*?\n"), "", string)
-
-        return string
-
-    @staticmethod
-    def loadJsonWithComments(path):
-        """
-      Reads a JSON file that may contain C++ like comments
-      """
-        with open(path, "r") as fp:
-            fileContent = fp.read()
-        fileContent = JsonHelpers.removeCommentsFromJsonContent(fileContent)
-        return json.loads(fileContent)
-
-class FieldDefinition:
-
-    def __init__(self, name: str, type: str, defaultValue: str):
-        self.name = name
-        self.type = type
-        self.defaultValue = defaultValue
-
-    @staticmethod
-    def fromKeyValue(key: str, value: str):
-
-        if "=" in value:
-            splitValue = value.split(sep="=")
-            type = splitValue[0].strip(" ")
-            defaultValue = splitValue[1].strip(" ")
-        else:
-            type = value
-            defaultValue = None
-
-        return FieldDefinition(name = key, type = type, defaultValue = defaultValue)
-
-
-def LoadSchemaFromJson(filePath):
-    return JsonHelpers.loadJsonWithComments(filePath)
-
-def CanonToCpp(canonicalTypename):
-  # C++: prefix map vector and string with std::map, std::vector and
-  # std::string
-  # replace int32... by int32_t...
-  # replace float32 by float
-  # replace float64 by double
-  retVal = canonicalTypename
-  retVal = retVal.replace("map", "std::map")
-  retVal = retVal.replace("vector", "std::vector")
-  retVal = retVal.replace("set", "std::set")
-  retVal = retVal.replace("string", "std::string")
-  #uint32 and uint64 are handled by int32 and uint32 (because search and replace are done as partial words)
-  retVal = retVal.replace("int32", "int32_t")
-  retVal = retVal.replace("int64", "int64_t")
-  retVal = retVal.replace("float32", "float")
-  retVal = retVal.replace("float64", "double")
-  retVal = retVal.replace("json", "Json::Value")
-  return retVal
-
-def CanonToTs(canonicalTypename):
-  # TS: replace vector with Array and map with Map
-  # string remains string
-  # replace int32... by number
-  # replace float32... by number
-  retVal = canonicalTypename
-  retVal = retVal.replace("map", "Map")
-  retVal = retVal.replace("vector", "Array")
-  retVal = retVal.replace("set", "Set")
-  retVal = retVal.replace("uint32", "number")
-  retVal = retVal.replace("uint64", "number")
-  retVal = retVal.replace("int32", "number")
-  retVal = retVal.replace("int64", "number")
-  retVal = retVal.replace("float32", "number")
-  retVal = retVal.replace("float64", "number")
-  retVal = retVal.replace("bool", "boolean")
-  retVal = retVal.replace("json", "Object")
-  return retVal
-
-def NeedsTsConstruction(enums, tsType):
-  if tsType == 'boolean':
-    return False
-  elif tsType == 'number':
-    return False
-  elif tsType == 'string':
-    return False
-  else:
-    enumNames = []
-    for enum in enums:
-      enumNames.append(enum['name'])
-    if tsType in enumNames:
-      return False
-  return True
-
-def NeedsCppConstruction(canonTypename):
-  return False
-
-def DefaultValueToTs(enums, field:FieldDefinition):
-    tsType = CanonToTs(field.type)
-
-    enumNames = []
-    for enum in enums:
-        enumNames.append(enum['name'])
-
-    if tsType in enumNames:
-        return tsType + "." + field.defaultValue
-    else:
-        return field.defaultValue
-
-def DefaultValueToCpp(root, enums, field:FieldDefinition):
-    cppType = CanonToCpp(field.type)
-
-    enumNames = []
-    for enum in enums:
-        enumNames.append(enum['name'])
-
-    if cppType in enumNames:
-        return root + "::" + cppType + "_" + field.defaultValue
-    else:
-        return field.defaultValue
-
-def RegisterTemplateFunction(template,func):
-  """Makes a function callable by a jinja2 template"""
-  template.globals[func.__name__] = func
-  return func
-
-def MakeTemplate(templateStr):
-  template = Template(templateStr)
-  RegisterTemplateFunction(template,CanonToCpp)
-  RegisterTemplateFunction(template,CanonToTs)
-  RegisterTemplateFunction(template,NeedsTsConstruction)
-  RegisterTemplateFunction(template,NeedsCppConstruction)
-  RegisterTemplateFunction(template, DefaultValueToTs)
-  RegisterTemplateFunction(template, DefaultValueToCpp)
-  return template
-
-def MakeTemplateFromFile(templateFileName):
-
-  with open(templateFileName, "r") as templateFile:
-    templateFileContents = templateFile.read()
-    return MakeTemplate(templateFileContents)
-
-
-def EatToken(sentence):
-    """splits "A,B,C" into "A" and "B,C" where A, B and C are type names
-  (including templates) like "int32", "TotoTutu", or 
-  "map<map<int32,vector<string>>,map<string,int32>>" """
-
-    if sentence.count("<") != sentence.count(">"):
-        raise Exception(
-            "Error in the partial template type list " + str(sentence) + "."
-            + " The number of < and > do not match!"
-        )
-
-    # the template level we're currently in
-    templateLevel = 0
-    for i in range(len(sentence)):
-        if (sentence[i] == ",") and (templateLevel == 0):
-            return (sentence[0:i], sentence[i + 1 :])
-        elif sentence[i] == "<":
-            templateLevel += 1
-        elif sentence[i] == ">":
-            templateLevel -= 1
-    return (sentence, "")
-
-
-def SplitListOfTypes(typename):
-    """Splits something like
-  vector<string>,int32,map<string,map<string,int32>> 
-  in:
-  - vector<string>
-  - int32
-    map<string,map<string,int32>>
-  
-  This is not possible with a regex so 
-  """
-    stillStuffToEat = True
-    tokenList = []
-    restOfString = typename
-    while stillStuffToEat:
-        firstToken, restOfString = EatToken(restOfString)
-        tokenList.append(firstToken)
-        if restOfString == "":
-            stillStuffToEat = False
-    return tokenList
-
-
-templateRegex = \
-  re.compile(r"([a-zA-Z0-9_]*[a-zA-Z0-9_]*)<([a-zA-Z0-9_,:<>]+)>")
-
-
-def ParseTemplateType(typename):
-    """ If the type is a template like "SOMETHING<SOME<THING,EL<SE>>>", 
-    then it returns (true,"SOMETHING","SOME<THING,EL<SE>>")
-  otherwise it returns (false,"","")"""
-
-    # let's remove all whitespace from the type
-    # split without argument uses any whitespace string as separator
-    # (space, tab, newline, return or formfeed)
-    typename = "".join(typename.split())
-    matches = templateRegex.match(typename)
-    if matches == None:
-        return (False, "", [])
-    else:
-        m = matches
-        assert len(m.groups()) == 2
-        # we need to split with the commas that are outside of the
-        # defined types. Simply splitting at commas won't work
-        listOfDependentTypes = SplitListOfTypes(m.group(2))
-        return (True, m.group(1), listOfDependentTypes)
-
-def GetStructFields(struct):
-  """This filters out the special metadata key from the struct fields"""
-  return [k for k in struct.keys() if k != '__handler']
-
-def ComputeOrderFromTypeTree(
-  ancestors, 
-  genOrder, 
-  shortTypename, schema):
-
-  if shortTypename in ancestors:
-    raise Exception(
-      "Cyclic dependency chain found: the last of " + str(ancestors) +
-      + " depends on " + str(shortTypename) + " that is already in the list."
-    )
-
-  if not (shortTypename in genOrder):
-    (isTemplate, _, dependentTypenames) = ParseTemplateType(shortTypename)
-    if isTemplate:
-      # if it is a template, it HAS dependent types... They can be 
-      # anything (primitive, collection, enum, structs..). 
-      # Let's process them!
-      for dependentTypename in dependentTypenames:
-        # childAncestors = ancestors.copy()  NO TEMPLATE ANCESTOR!!!
-        # childAncestors.append(typename)
-        ComputeOrderFromTypeTree(
-            ancestors, genOrder, dependentTypename, schema
-        )
-    else:
-      # If it is not template, we are only interested if it is a 
-      # dependency that we must take into account in the dep graph,
-      # i.e., a struct.
-      if IsShortStructType(shortTypename, schema):
-        struct = schema[GetLongTypename(shortTypename, schema)]
-        # The keys in the struct dict are the member names
-        # The values in the struct dict are the member types
-        if struct:
-          # we reach this if struct is not None AND not empty
-          for field in GetStructFields(struct):
-            # we fill the chain of dependent types (starting here)
-            ancestors.append(shortTypename)
-            ComputeOrderFromTypeTree(
-              ancestors, genOrder, struct[field], schema)
-            # don't forget to restore it!
-            ancestors.pop()
-        
-        # now we're pretty sure our dependencies have been processed,
-        # we can start marking our code for generation (it might 
-        # already have been done if someone referenced us earlier)
-        if not shortTypename in genOrder:
-          genOrder.append(shortTypename)
-
-# +-----------------------+
-# |   Utility functions   |
-# +-----------------------+
-
-def IsShortStructType(typename, schema):
-  fullStructName = "struct " + typename
-  return (fullStructName in schema)
-
-def GetLongTypename(shortTypename, schema):
-  if shortTypename.startswith("enum "):
-    raise RuntimeError('shortTypename.startswith("enum "):')
-  enumName = "enum " + shortTypename
-  isEnum = enumName in schema
-
-  if shortTypename.startswith("struct "):
-    raise RuntimeError('shortTypename.startswith("struct "):')
-  structName = "struct " + shortTypename
-  isStruct = ("struct " + shortTypename) in schema
-
-  if isEnum and isStruct:
-    raise RuntimeError('Enums and structs cannot have the same name')
-
-  if isEnum:
-    return enumName
-  if isStruct:
-    return structName
-
-def IsTypename(fullName):
-  return (fullName.startswith("enum ") or fullName.startswith("struct "))
-
-def IsEnumType(fullName):
-  return fullName.startswith("enum ")
-
-def IsStructType(fullName):
-  return fullName.startswith("struct ")
-
-def GetShortTypename(fullTypename):
-  if fullTypename.startswith("struct "):
-    return fullTypename[7:] 
-  elif fullTypename.startswith("enum"):
-    return fullTypename[5:] 
-  else:
-    raise RuntimeError \
-      ('fullTypename should start with either "struct " or "enum "')
-
-def CheckSchemaSchema(schema):
-  if not "rootName" in schema:
-      raise Exception("schema lacks the 'rootName' key")
-  for name in schema.keys():
-    if (not IsEnumType(name)) and (not IsStructType(name)) and \
-      (name != 'rootName'):
-      raise RuntimeError \
-        ('Type "' + str(name) + '" should start with "enum " or "struct "')
-
-  # TODO: check enum fields are unique (in whole namespace)
-  # TODO: check struct fields are unique (in each struct)
-  # TODO: check that in the source schema, there are spaces after each colon
-
-nonTypeKeys = ['rootName']
-def GetTypesInSchema(schema):
-  """Returns the top schema keys that are actual type names"""
-  typeList = [k for k in schema if k not in nonTypeKeys]
-  return typeList
-
-# +-----------------------+
-# | Main processing logic |
-# +-----------------------+
-
-def ComputeRequiredDeclarationOrder(schema):
-  # sanity check
-  CheckSchemaSchema(schema)
-
-  # we traverse the type dependency graph and we fill a queue with
-  # the required struct types, in a bottom-up fashion, to compute
-  # the declaration order
-  # The genOrder list contains the struct full names in the order
-  # where they must be defined.
-  # We do not care about the enums here... They do not depend upon
-  # anything and we'll handle them, in their original declaration 
-  # order, at the start
-  genOrder = []
-  for fullName in GetTypesInSchema(schema):
-    if IsStructType(fullName):
-      realName = GetShortTypename(fullName)
-      ancestors = []
-      ComputeOrderFromTypeTree(ancestors, genOrder, realName, schema)
-  return genOrder
-
-def GetStructFields(fieldDict):
-  """Returns the regular (non __handler) struct fields"""
-  # the following happens for empty structs
-  if fieldDict == None:
-    return fieldDict
-  ret = {}
-  for k,v in fieldDict.items():
-    if k != "__handler":
-      ret[k] = FieldDefinition.fromKeyValue(k, v)
-    if k.startswith("__") and k != "__handler":
-      raise RuntimeError("Fields starting with __ (double underscore) are reserved names!")
-  return ret
-
-def GetStructMetadata(fieldDict):
-  """Returns the __handler struct fields (there are default values that
-     can be overridden by entries in the schema
-     Not tested because it's a fail-safe: if something is broken in this, 
-     dependent projects will not build."""
-  metadataDict = {}
-  metadataDict['handleInCpp'] = False
-  metadataDict['handleInTypescript'] = False
-  
-  if fieldDict != None:
-    for k,v in fieldDict.items():
-      if k.startswith("__") and k != "__handler":
-        raise RuntimeError("Fields starting with __ (double underscore) are reserved names")
-      if k == "__handler":
-        if type(v) == list:
-          for i in v:
-            if i == "cpp":
-              metadataDict['handleInCpp'] = True
-            elif i == "ts":
-              metadataDict['handleInTypescript'] = True
-            else:
-              raise RuntimeError("Error in schema. Allowed values for __handler are \"cpp\" or \"ts\"")
-        elif type(v) == str:
-          if v == "cpp":
-            metadataDict['handleInCpp'] = True
-          elif v == "ts":
-            metadataDict['handleInTypescript'] = True
-          else:
-            raise RuntimeError("Error in schema. Allowed values for __handler are \"cpp\" or \"ts\" (or a list of both)")
-        else:
-            raise RuntimeError("Error in schema. Allowed values for __handler are \"cpp\" or \"ts\" (or a list of both)")
-  return metadataDict
-
-def ProcessSchema(schema, genOrder):
-  # sanity check
-  CheckSchemaSchema(schema)
-
-  # let's doctor the schema to clean it up a bit
-  # order DOES NOT matter for enums, even though it's a list
-  enums = []
-  for fullName in schema.keys():
-    if IsEnumType(fullName):
-      # convert "enum Toto" to "Toto"
-      typename = GetShortTypename(fullName)
-      enum = {}
-      enum['name'] = typename
-      assert(type(schema[fullName]) == list)
-      enum['fields'] = schema[fullName] # must be a list
-      enums.append(enum)
-
-  # now that the order has been established, we actually store\
-  # the structs in the correct order
-  # the structs are like:
-  # example = [
-  #   {
-  #     "name": "Message1",
-  #     "fields": {
-  #       "someMember":"int32",
-  #       "someOtherMember":"vector<string>"
-  #     }
-  #   },
-  #   { 
-  #     "name": "Message2",
-  #     "fields": {
-  #       "someMember":"int32",
-  #       "someOtherMember22":"vector<Message1>"
-  #     }
-  #   }
-  # ]
-
-  structs = []
-  for i in range(len(genOrder)):
-    # this is already the short name
-    typename = genOrder[i]
-    fieldDict = schema["struct " + typename]
-    struct = {}
-    struct['name'] = typename
-    struct['fields'] = GetStructFields(fieldDict)
-    struct['__meta__'] = GetStructMetadata(fieldDict)
-    structs.append(struct)
-  
-  templatingDict = {}
-  templatingDict['enums'] = enums
-  templatingDict['structs'] = structs
-  templatingDict['rootName'] = schema['rootName']
-
-  return templatingDict
-
-# +-----------------------+
-# |    Write to files     |
-# +-----------------------+
-
-# def WriteStreamsToFiles(rootName: str, genc: Dict[str, StringIO]) \
-#   -> None:
-#   pass
-
-def LoadSchema(fn):
-  # latin-1 is a trick, when we do NOT care about NON-ascii chars but
-  # we wish to avoid using a decoding error handler
-  # (see http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html#files-in-an-ascii-compatible-encoding-best-effort-is-acceptable)
-  # TL;DR: all 256 values are mapped to characters in latin-1 so the file 
-  # contents never cause an error.
-  with open(fn, 'r', encoding='latin-1') as f:
-    schemaText = f.read()
-    assert(type(schemaText) == str)
-  return LoadSchemaFromString(schemaText = schemaText)
-
-def LoadSchemaFromString(schemaText:str):
-    # ensure there is a space after each colon. Otherwise, dicts could be
-    # erroneously recognized as an array of strings containing ':'
-    for i in range(len(schemaText)-1):
-      ch = schemaText[i]
-      nextCh = schemaText[i+1]
-      if ch == ':':
-        if not (nextCh == ' ' or nextCh == '\n'):
-          lineNumber = schemaText.count("\n",0,i) + 1
-          raise RuntimeError("Error at line " + str(lineNumber) + " in the schema: colons must be followed by a space or a newline!")
-    schema = yaml.load(schemaText, Loader = yamlloader.ordereddict.SafeLoader)
-    return schema
-
-def GetTemplatingDictFromSchemaFilename(fn):
-  return GetTemplatingDictFromSchema(LoadSchema(fn))
-
-def GetTemplatingDictFromSchema(schema):
-  genOrder = ComputeRequiredDeclarationOrder(schema)
-  templatingDict = ProcessSchema(schema, genOrder)
-  currentDT = datetime.datetime.now()
-  templatingDict['currentDatetime'] = str(currentDT)
-  return templatingDict
-
-# +-----------------------+
-# |      ENTRY POINT      |
-# +-----------------------+
-def Process(schemaFile, outDir):
-  tdico = GetTemplatingDictFromSchemaFilename(schemaFile)
-
-  tsTemplateFile = \
-    os.path.join(os.path.dirname(__file__), 'template.in.ts.j2')
-  template = MakeTemplateFromFile(tsTemplateFile)
-  renderedTsCode = template.render(**tdico)
-  outputTsFile = os.path.join( \
-    outDir,str(tdico['rootName']) + "_generated.ts")
-  with open(outputTsFile,"wt",encoding='utf8') as outFile:
-    outFile.write(renderedTsCode)
-
-  cppTemplateFile = \
-    os.path.join(os.path.dirname(__file__), 'template.in.h.j2')
-  template = MakeTemplateFromFile(cppTemplateFile)
-  renderedCppCode = template.render(**tdico)
-  outputCppFile = os.path.join( \
-    outDir, str(tdico['rootName']) + "_generated.hpp")
-  with open(outputCppFile,"wt",encoding='utf8') as outFile:
-    outFile.write(renderedCppCode)
-
-if __name__ == "__main__":
-  import argparse
-
-  parser = argparse.ArgumentParser(
-    usage="""stonegentool.py [-h] [-o OUT_DIR] [-v] input_schema
-    EXAMPLE: python stonegentool.py -o "generated_files/" """
-      + """ "mainSchema.yaml,App Specific Commands.json" """
-  )
-  parser.add_argument("input_schema", type=str, \
-    help="path to the schema file")
-  parser.add_argument(
-    "-o",
-    "--out_dir",
-    type=str,
-    default=".",
-    help="""path of the directory where the files 
-                          will be generated. Default is current
-                          working folder""",
-  )
-  parser.add_argument(
-    "-v",
-    "--verbosity",
-    action="count",
-    default=0,
-    help="""increase output verbosity (0 == errors 
-                          only, 1 == some verbosity, 2 == nerd
-                          mode""",
-  )
-
-  args = parser.parse_args()
-  schemaFile = args.input_schema
-  outDir = args.out_dir
-  Process(schemaFile, outDir)
--- a/Resources/CodeGeneration/stonegentool_test.py	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,438 +0,0 @@
-#
-#        1         2         3         4         5         6         7         8
-# 345678901234567890123456789012345678901234567890123456789012345678901234567890
-#
-
-from stonegentool import \
-EatToken,SplitListOfTypes,ParseTemplateType,ProcessSchema, \
-CheckSchemaSchema,LoadSchema,trim,ComputeRequiredDeclarationOrder, \
-GetTemplatingDictFromSchemaFilename,MakeTemplate,MakeTemplateFromFile,LoadSchemaFromString,GetTemplatingDictFromSchema
-import unittest
-import os
-import re
-import pprint
-from jinja2 import Template
-
-def RemoveDateTimeLine(s : str):
-  # regex are non-multiline by default, and $ does NOT match the end of the line
-  s2 = re.sub(r"^// autogenerated by stonegentool on .*\n","",s)
-  return s2
-
-class TestStonegentool(unittest.TestCase):
-  def test_EatToken_empty(self):
-    c = r""
-    a,b = EatToken(c)
-    self.assertEqual(a,r"")
-    self.assertEqual(b,r"")
-
-  def test_EatToken_simpleNonTemplate(self):
-    c = r"int32"
-    a,b = EatToken(c)
-    self.assertEqual(a,r"int32")
-    self.assertEqual(b,r"")
-
-  def test_EatToken_simpleTemplate(self):
-    c = r"vector<string>"
-    a,b = EatToken(c)
-    self.assertEqual(a,r"vector<string>")
-    self.assertEqual(b,r"")
-
-  def test_EatToken_complexTemplate(self):
-    c = r"vector<map<int64,string>>,vector<map<int32,string>>"
-    a,b = EatToken(c)
-    self.assertEqual(a,r"vector<map<int64,string>>")
-    self.assertEqual(b,r"vector<map<int32,string>>")
-
-  def test_EatToken_complexTemplates(self):
-    c = r"vector<map<vector<string>,map<int32,string>>>,map<int32,string>,map<map<int32,string>,string>"
-    a,b = EatToken(c)
-    self.assertEqual(a,r"vector<map<vector<string>,map<int32,string>>>")
-    self.assertEqual(b,r"map<int32,string>,map<map<int32,string>,string>")
-    a,b = EatToken(b)
-    self.assertEqual(a,r"map<int32,string>")
-    self.assertEqual(b,r"map<map<int32,string>,string>")
-
-  def test_SplitListOfTypes(self):
-    c = r"vector<map<vector<string>,map<int32,string>>>,map<int32,string>,map<map<int32,string>,string>"
-    lot = SplitListOfTypes(c)
-    self.assertEqual(3,len(lot))
-    self.assertEqual("vector<map<vector<string>,map<int32,string>>>",lot[0])
-    self.assertEqual("map<int32,string>",lot[1])
-    self.assertEqual("map<map<int32,string>,string>",lot[2])
-
-  def test_SplitListOfTypes_bogus(self):
-    c = r"vector<map<vector<string>,map<int32,string>>,map<int32,string>,map<map<int32,string>,string"
-    self.assertRaises(Exception,SplitListOfTypes,c) # the argument c must be passed to assertRaises, not as a normal call of SplitListOfTypes
-    
-  def test_ParseTemplateType_true(self):
-    c = "map<vector<map<int,vector<string>>>,map<vector<int>,vector<string>>>"
-    (ok,a,b) = ParseTemplateType(c)
-    self.assertEqual(ok,True)
-    self.assertEqual(a,"map")
-    self.assertEqual(b,["vector<map<int,vector<string>>>","map<vector<int>,vector<string>>"])
-
-    (ok2,a2,b2) = ParseTemplateType(b[0])
-    self.assertEqual(ok2,True)
-    self.assertEqual(a2,"vector")
-    self.assertEqual(b2,["map<int,vector<string>>"])
-
-    (ok3,a3,b3) = ParseTemplateType(b[1])
-    self.assertEqual(ok3,True)
-    self.assertEqual(a3,"map")
-    self.assertEqual(b3,["vector<int>","vector<string>"])
-
-    (ok4,a4,b4) = ParseTemplateType(b2[0])
-    self.assertEqual(ok4,True)
-    self.assertEqual(a4,"map")
-    self.assertEqual(b4,["int","vector<string>"])
-    
-  def test_ParseSchema(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
-    obj = LoadSchema(fn)
-    # we're happy if it does not crash :)
-    CheckSchemaSchema(obj)
-
-  def test_ComputeRequiredDeclarationOrder(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
-    obj = LoadSchema(fn)
-    genOrder: str = ComputeRequiredDeclarationOrder(obj)
-    self.assertEqual(5,len(genOrder))
-    self.assertEqual("A",genOrder[0])
-    self.assertEqual("B",genOrder[1])
-    self.assertEqual("C",genOrder[2])
-    self.assertEqual("Message1",genOrder[3])
-    self.assertEqual("Message2",genOrder[4])
-
-  # def test_GeneratePreambleEnumerationAndStructs(self):
-  #   fn = os.path.join(os.path.dirname(__file__), 'test', 'test1.jsonc')
-  #   obj = LoadSchema(fn)
-  #   (_,genc,_) = ProcessSchema(obj)
-
-  def test_genEnums(self):
-    self.maxDiff = None
-    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
-    obj = LoadSchema(fn)
-    genOrder: str = ComputeRequiredDeclarationOrder(obj)
-    processedSchema = ProcessSchema(obj, genOrder)
-    self.assertTrue('rootName' in processedSchema)
-
-    structs = {}
-    for v in processedSchema['structs']:
-      structs[v['name']] = v
-    enums = {}
-    for v in processedSchema['enums']:
-      enums[v['name']] = v
-
-    self.assertTrue('C' in structs)
-    self.assertTrue('someBs' in structs['C']['fields'])
-    self.assertTrue('CrispType' in enums)
-    self.assertTrue('Message1' in structs)
-    self.assertEqual('int32', structs['Message1']['fields']['memberInt32'].type)
-    self.assertEqual('string', structs['Message1']['fields']['memberString'].type)
-    self.assertEqual('EnumMonth0', structs['Message1']['fields']['memberEnumMonth'].type)
-    self.assertEqual('bool', structs['Message1']['fields']['memberBool'].type)
-    self.assertEqual('float32', structs['Message1']['fields']['memberFloat32'].type)
-    self.assertEqual('float64', structs['Message1']['fields']['memberFloat64'].type)
-
-  def test_GenerateTypeScriptEnums(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
-    tdico = GetTemplatingDictFromSchemaFilename(fn)
-    template = Template("""  // end of generic methods
-{% for enum in enums%}  export enum {{enum['name']}} {
-{% for key in enum['fields']%}    {{key}},
-{%endfor%}  };
-
-{%endfor%}""")
-    renderedCode = template.render(**tdico)
-    renderedCodeRef = """  // end of generic methods
-  export enum MovieType {
-    RomCom,
-    Horror,
-    ScienceFiction,
-    Vegetables,
-  };
-
-  export enum CrispType {
-    SaltAndPepper,
-    CreamAndChives,
-    Paprika,
-    Barbecue,
-  };
-
-  export enum EnumMonth0 {
-    January,
-    February,
-    March,
-  };
-
-"""
-    self.assertEqual(renderedCodeRef,renderedCode)
-
-  def test_GenerateCplusplusEnums(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
-    tdico = GetTemplatingDictFromSchemaFilename(fn)
-    template = Template("""  // end of generic methods
-{% for enum in enums%}  enum {{enum['name']}} {
-{% for key in enum['fields']%}    {{key}},
-{%endfor%}  };
-
-{%endfor%}""")
-    renderedCode = template.render(**tdico)
-    renderedCodeRef = """  // end of generic methods
-  enum MovieType {
-    RomCom,
-    Horror,
-    ScienceFiction,
-    Vegetables,
-  };
-
-  enum CrispType {
-    SaltAndPepper,
-    CreamAndChives,
-    Paprika,
-    Barbecue,
-  };
-
-  enum EnumMonth0 {
-    January,
-    February,
-    March,
-  };
-
-"""
-    self.assertEqual(renderedCodeRef,renderedCode)
-
-  def test_generateTsStructType(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
-    tdico = GetTemplatingDictFromSchemaFilename(fn)
-
-#     template = MakeTemplate("""  // end of generic methods
-# {% for struct in struct%}  export class {{struct['name']}} {
-#   {% for key in struct['fields']%}    {{key}}:{{struct['fields'][key]}},
-#   {% endfor %}  
-#     constructor() {
-#   {% for key in struct['fields']%}
-#     {% if NeedsConstruction(struct['fields']['key'])}
-#       {{key}} = new {{CanonToTs(struct['fields']['key'])}};
-#     {% end if %}
-#   {% endfor %}
-#     }
-# {% endfor %}
-#     public StoneSerialize(): string {
-#       let container: object = {};
-#       container['type'] = '{{rootName}}.{{struct['name']}}';
-#       container['value'] = this;
-#       return JSON.stringify(container);
-#     }  };""")
-    template = MakeTemplate("""  // end of generic methods
-{% for struct in structs%}  export class {{struct['name']}} {
-{% for key in struct['fields']%}    {{key}}:{{CanonToTs(struct['fields'][key]['type'])}};
-{% endfor %}
-    constructor() {
-{% for key in struct['fields']%}      this.{{key}} = new {{CanonToTs(struct['fields'][key]['type'])}}();
-{% endfor %}    }
-
-    public StoneSerialize(): string {
-      let container: object = {};
-      container['type'] = '{{rootName}}.{{struct['name']}}';
-      container['value'] = this;
-      return JSON.stringify(container);
-    }
-  };
-
-{% endfor %}""")
-    renderedCode = template.render(**tdico)
-    renderedCodeRef = """  // end of generic methods
-  export class A {
-    someStrings:Array<string>;
-    someInts2:Array<number>;
-    movies:Array<MovieType>;
-
-    constructor() {
-      this.someStrings = new Array<string>();
-      this.someInts2 = new Array<number>();
-      this.movies = new Array<MovieType>();
-    }
-
-    public StoneSerialize(): string {
-      let container: object = {};
-      container['type'] = 'TestStoneCodeGen.A';
-      container['value'] = this;
-      return JSON.stringify(container);
-    }
-  };
-
-  export class B {
-    someAs:Array<A>;
-    someInts:Array<number>;
-
-    constructor() {
-      this.someAs = new Array<A>();
-      this.someInts = new Array<number>();
-    }
-
-    public StoneSerialize(): string {
-      let container: object = {};
-      container['type'] = 'TestStoneCodeGen.B';
-      container['value'] = this;
-      return JSON.stringify(container);
-    }
-  };
-
-  export class C {
-    someBs:Array<B>;
-    ddd:Array<string>;
-
-    constructor() {
-      this.someBs = new Array<B>();
-      this.ddd = new Array<string>();
-    }
-
-    public StoneSerialize(): string {
-      let container: object = {};
-      container['type'] = 'TestStoneCodeGen.C';
-      container['value'] = this;
-      return JSON.stringify(container);
-    }
-  };
-
-  export class Message1 {
-    memberInt32:number;
-    memberString:string;
-    memberEnumMonth:EnumMonth0;
-    memberBool:boolean;
-    memberFloat32:number;
-    memberFloat64:number;
-
-    constructor() {
-      this.memberInt32 = new number();
-      this.memberString = new string();
-      this.memberEnumMonth = new EnumMonth0();
-      this.memberBool = new boolean();
-      this.memberFloat32 = new number();
-      this.memberFloat64 = new number();
-    }
-
-    public StoneSerialize(): string {
-      let container: object = {};
-      container['type'] = 'TestStoneCodeGen.Message1';
-      container['value'] = this;
-      return JSON.stringify(container);
-    }
-  };
-
-  export class Message2 {
-    memberString:string;
-    memberStringWithDefault:string;
-    memberVectorOfMessage1:Array<Message1>;
-    memberVectorOfString:Array<string>;
-    memberMapStringString:Map<string, string>;
-    memberMapStringStruct:Map<string, Message1>;
-    memberMapEnumFloat:Map<CrispType, number>;
-    memberEnumMovieType:MovieType;
-    memberJson:Object;
-
-    constructor() {
-      this.memberString = new string();
-      this.memberStringWithDefault = new string();
-      this.memberVectorOfMessage1 = new Array<Message1>();
-      this.memberVectorOfString = new Array<string>();
-      this.memberMapStringString = new Map<string, string>();
-      this.memberMapStringStruct = new Map<string, Message1>();
-      this.memberMapEnumFloat = new Map<CrispType, number>();
-      this.memberEnumMovieType = new MovieType();
-      this.memberJson = new Object();
-    }
-
-    public StoneSerialize(): string {
-      let container: object = {};
-      container['type'] = 'TestStoneCodeGen.Message2';
-      container['value'] = this;
-      return JSON.stringify(container);
-    }
-  };
-
-"""
-    # print(renderedCode)
-    self.maxDiff = None
-    self.assertEqual(renderedCodeRef, renderedCode)
-
-  def test_generateWholeTsFile(self):
-    schemaFile = \
-      os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
-    tdico = GetTemplatingDictFromSchemaFilename(schemaFile)
-    tsTemplateFile = \
-      os.path.join(os.path.dirname(__file__), 'template.in.ts.j2')
-    template = MakeTemplateFromFile(tsTemplateFile)
-    renderedCode = template.render(**tdico)
-    print(renderedCode)
-
-  def test_GenerateTypeScriptHandlerInterface(self):
-    pass
-
-  def test_GenerateCppHandlerInterface(self):
-    pass
-
-  def test_GenerateTypeScriptDispatcher(self):
-    pass
-
-  def test_GenerateCppDispatcher(self):
-    pass
-
-  def test_StringDefaultValueInTs(self):
-    schema = LoadSchemaFromString("""
-                                  rootName: MyTest
-                                  struct Toto:
-                                    withoutDefault: string
-                                    withDefault: string = \"tutu\"
-                                  """)
-    tdico = GetTemplatingDictFromSchema(schema)
-
-    tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.ts.j2')
-    template = MakeTemplateFromFile(tsTemplateFile)
-    renderedCode = template.render(**tdico)
-    self.assertIn("withDefault = \"tutu\"", renderedCode)
-    # print(renderedCode)
-
-    cppTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.h.j2')
-    template = MakeTemplateFromFile(cppTemplateFile)
-    renderedCode = template.render(**tdico)
-    print(renderedCode)
-    self.assertIn("withDefault = \"tutu\"", renderedCode)
-
-
-  def test_EnumDefaultValue(self):
-    schema = LoadSchemaFromString("""
-                                  rootName: MyTest
-                                  enum MyEnum:
-                                    - Toto
-                                    - Tutu
-                                  struct Toto:
-                                    withoutDefault: MyEnum
-                                    withDefault: MyEnum = Toto
-                                  """)
-    tdico = GetTemplatingDictFromSchema(schema)
-
-    tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.ts.j2')
-    template = MakeTemplateFromFile(tsTemplateFile)
-    renderedCode = template.render(**tdico)
-    # print(renderedCode)
-    self.assertIn("withDefault = MyEnum.Toto", renderedCode)
-
-    tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.h.j2')
-    template = MakeTemplateFromFile(tsTemplateFile)
-    renderedCode = template.render(**tdico)
-    self.assertIn("withDefault = MyTest::MyEnum_Toto", renderedCode)
-    # print(renderedCode)
-
-
-# def test(self):
-#   s = 'hello world'
-#   self.assertEqual(s.split(), ['hello', 'world'])
-#   # check that s.split fails when the separator is not a string
-#   with self.assertRaises(TypeError):
-#   s.split(2)
-
-if __name__ == '__main__':
-  unittest.main()
-
--- a/Resources/CodeGeneration/template.in.h.j2	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,551 +0,0 @@
-/*
-         1         2         3         4         5         6         7
-12345678901234567890123456789012345678901234567890123456789012345678901234567890
-
-Generated on {{currentDatetime}} by stonegentool
-
-*/
-#pragma once
-
-#include <exception>
-#include <iostream>
-#include <string>
-#include <sstream>
-#include <assert.h>
-#include <memory>
-#include <set>
-#include <json/json.h>
-
-//#define STONEGEN_NO_CPP11 1
-
-#ifdef STONEGEN_NO_CPP11
-#define StoneSmartPtr std::unique_ptr
-#else 
-#define StoneSmartPtr std::unique_ptr
-#endif 
-
-namespace {{rootName}}
-{
-  /** Throws in case of problem */
-  inline void _StoneDeserializeValue(int32_t& destValue, const Json::Value& jsonValue)
-  {
-    if (!jsonValue.isNull())
-    {
-      destValue = jsonValue.asInt();
-    }
-  }
-
-  inline Json::Value _StoneSerializeValue(int32_t value)
-  {
-    Json::Value result(value);
-    return result;
-  }
-
-  inline void _StoneDeserializeValue(int64_t& destValue, const Json::Value& jsonValue)
-  {
-    if (!jsonValue.isNull())
-    {
-      destValue = jsonValue.asInt64();
-    }
-  }
-
-  inline Json::Value _StoneSerializeValue(int64_t value)
-  {
-    Json::Value result(static_cast<Json::Value::Int64>(value));
-    return result;
-  }
-
-  inline void _StoneDeserializeValue(uint32_t& destValue, const Json::Value& jsonValue)
-  {
-    if (!jsonValue.isNull())
-    {
-      destValue = jsonValue.asUInt();
-    }
-  }
-
-  inline Json::Value _StoneSerializeValue(uint32_t value)
-  {
-    Json::Value result(value);
-    return result;
-  }
-
-  inline void _StoneDeserializeValue(uint64_t& destValue, const Json::Value& jsonValue)
-  {
-    if (!jsonValue.isNull())
-    {
-      destValue = jsonValue.asUInt64();
-    }
-  }
-
-  inline Json::Value _StoneSerializeValue(uint64_t value)
-  {
-    Json::Value result(static_cast<Json::Value::UInt64>(value));
-    return result;
-  }
-
-  inline void _StoneDeserializeValue(Json::Value& destValue, const Json::Value& jsonValue)
-  {
-    destValue = jsonValue;
-  }
-
-  inline Json::Value _StoneSerializeValue(Json::Value value)
-  {
-    return value;
-  }
-
-  /** Throws in case of problem */
-  inline void _StoneDeserializeValue(double& destValue, const Json::Value& jsonValue)
-  {
-    if (!jsonValue.isNull())
-    {
-      destValue = jsonValue.asDouble();
-    }
-  }
-
-  inline Json::Value _StoneSerializeValue(double value)
-  {
-    Json::Value result(value);
-    return result;
-  }
-
-  /** Throws in case of problem */
-  inline void _StoneDeserializeValue(float& destValue, const Json::Value& jsonValue)
-  {
-    if (!jsonValue.isNull())
-    {
-      destValue = jsonValue.asFloat();
-    }
-  }
-
-  inline Json::Value _StoneSerializeValue(float value)
-  {
-    Json::Value result(value);
-    return result;
-  }
-
-  /** Throws in case of problem */
-  inline void _StoneDeserializeValue(bool& destValue, const Json::Value& jsonValue)
-  {
-    if (!jsonValue.isNull())
-    {
-      destValue = jsonValue.asBool();
-    }
-  }
-
-  inline Json::Value _StoneSerializeValue(bool value)
-  {
-    Json::Value result(value);
-    return result;
-  }
-
-  /** Throws in case of problem */
-  inline void _StoneDeserializeValue(
-       std::string& destValue
-     , const Json::Value& jsonValue)
-  {
-    if (!jsonValue.isNull())
-    {
-      destValue = jsonValue.asString();
-    }
-  }
-
-  inline Json::Value _StoneSerializeValue(const std::string& value)
-  {
-    // the following is better than 
-    Json::Value result(value.data(),value.data()+value.size());
-    return result;
-  }
-
-  inline std::string MakeIndent(size_t indent)
-  {
-    char* txt = reinterpret_cast<char*>(malloc(indent+1)); // NO EXCEPTION BELOW!!!!!!!!!!!!
-    for(size_t i = 0; i < indent; ++i)
-      txt[i] = ' ';
-    txt[indent] = 0;
-    std::string retVal(txt);
-    free(txt); // NO EXCEPTION ABOVE !!!!!!!!!!
-    return retVal;
-  }
-
-  // generic dumper
-  template<typename T>
-  std::ostream& StoneDumpValue(std::ostream& out, const T& value, size_t indent)
-  {
-    out << MakeIndent(indent) << value;
-    return out;
-  }
-
-  // string dumper
-  inline std::ostream& StoneDumpValue(std::ostream& out, const std::string& value, size_t indent)
-  {
-    out << MakeIndent(indent) << "\"" << value  << "\"";
-    return out;
-  }
-
-  inline std::string ToString(const std::string& str)
-  {
-    return str;
-  }
-
-  inline void FromString(std::string& value, std::string strValue)
-  {
-    value = strValue;
-  }
-
-
-  /** Throws in case of problem */
-  template<typename TK, typename TV>
-  void _StoneDeserializeValue(
-    std::map<TK, TV>& destValue, const Json::Value& jsonValue)
-  {
-    if (!jsonValue.isNull())
-    {
-      destValue.clear();
-      for (
-        Json::Value::const_iterator itr = jsonValue.begin();
-        itr != jsonValue.end();
-        itr++)
-      {
-        std::string strKey;
-        _StoneDeserializeValue(strKey, itr.key());
-        TK key;
-        FromString(key, strKey);  // if you have a compile error here, it means that your type is not suitable to be the key of a map (or you should overwrite the FromString/ToString in template.in.h.j2)
-
-        TV innerDestValue;
-        _StoneDeserializeValue(innerDestValue, *itr);
-
-        destValue[key] = innerDestValue;
-      }
-    }
-  }
-
-  template<typename TK, typename TV>
-  Json::Value _StoneSerializeValue(const std::map<TK, TV>& value)
-  {
-    Json::Value result(Json::objectValue);
-
-    for (typename std::map<TK, TV>::const_iterator it = value.cbegin();
-      it != value.cend(); ++it)
-    {
-      // it->first it->second
-      result[ToString(it->first)] = _StoneSerializeValue(it->second);
-    }
-    return result;
-  }
-
-  template<typename TK, typename TV>
-  std::ostream& StoneDumpValue(std::ostream& out, const std::map<TK, TV>& value, size_t indent)
-  {
-    out << MakeIndent(indent) << "{\n";
-    for (typename std::map<TK, TV>::const_iterator it = value.cbegin();
-      it != value.cend(); ++it)
-    {
-      out << MakeIndent(indent+2) << "\"" << it->first << "\" : ";
-      StoneDumpValue(out, it->second, indent+2);
-      out << ", \n";
-    }
-    out << MakeIndent(indent) << "}\n";
-    return out;
-  }
-
-  /** Throws in case of problem */
-  template<typename T>
-  void _StoneDeserializeValue(
-    std::vector<T>& destValue, const Json::Value& jsonValue)
-  {
-    if (!jsonValue.isNull() && jsonValue.isArray())
-    {
-      destValue.clear();
-      destValue.reserve(jsonValue.size());
-      for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++)
-      {
-        T innerDestValue;
-        _StoneDeserializeValue(innerDestValue, jsonValue[i]);
-        destValue.push_back(innerDestValue);
-      }
-    }
-  }
-
-  template<typename T>
-  Json::Value _StoneSerializeValue(const std::vector<T>& value)
-  {
-    Json::Value result(Json::arrayValue);
-    for (size_t i = 0; i < value.size(); ++i)
-    {
-      result.append(_StoneSerializeValue(value[i]));
-    }
-    return result;
-  }
-
-  template<typename T>
-  std::ostream& StoneDumpValue(std::ostream& out, const std::vector<T>& value, size_t indent)
-  {
-    out << MakeIndent(indent) << "[\n";
-    for (size_t i = 0; i < value.size(); ++i)
-    {
-      StoneDumpValue(out, value[i], indent+2);
-      out << ", \n";
-    }
-    out << MakeIndent(indent) << "]\n";
-    return out;
-  }
-
-  /** Throws in case of problem */
-  template<typename T>
-  void _StoneDeserializeValue(
-    std::set<T>& destValue, const Json::Value& jsonValue)
-  {
-    if (!jsonValue.isNull() && jsonValue.isArray())
-    {
-      destValue.clear();
-      for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++)
-      {
-        T innerDestValue;
-        _StoneDeserializeValue(innerDestValue, jsonValue[i]);
-        destValue.insert(innerDestValue);
-      }
-    }
-  }
-
-  template<typename T>
-  Json::Value _StoneSerializeValue(const std::set<T>& value)
-  {
-    Json::Value result(Json::arrayValue);
-    for (typename std::set<T>::const_iterator it = value.begin(); it != value.end(); ++it)
-    {
-      result.append(_StoneSerializeValue(*it));
-    }
-    return result;
-  }
-
-  template<typename T>
-  std::ostream& StoneDumpValue(std::ostream& out, const std::set<T>& value, size_t indent)
-  {
-    out << MakeIndent(indent) << "[\n";
-    for (typename std::set<T>::const_iterator it = value.begin(); it != value.end(); ++it)
-    {
-      StoneDumpValue(out, *it, indent+2);
-      out << ", \n";
-    }
-    out << MakeIndent(indent) << "]\n";
-    return out;
-  }
-
-  inline void StoneCheckSerializedValueTypeGeneric(const Json::Value& value)
-  {
-    if ((!value.isMember("type")) || (!value["type"].isString()))
-    {
-      std::stringstream ss;
-      ss << "Cannot deserialize value ('type' key invalid)";
-      throw std::runtime_error(ss.str());
-    }
-  }
-
-  inline void StoneCheckSerializedValueType(
-    const Json::Value& value, std::string typeStr)
-  {
-    StoneCheckSerializedValueTypeGeneric(value);
-
-    std::string actTypeStr = value["type"].asString();
-    if (actTypeStr != typeStr)
-    {
-      std::stringstream ss;
-      ss << "Cannot deserialize type" << actTypeStr
-        << "into " << typeStr;
-      throw std::runtime_error(ss.str());
-    }
-  }
-
-  // end of generic methods
-
-// end of generic methods
-{% for enum in enums%}
-  enum {{enum['name']}} {
-{% for key in enum['fields']%}    {{enum['name']}}_{{key}},
-{%endfor%}  };
-
-  inline std::string ToString(const {{enum['name']}}& value)
-  {
-{% for key in enum['fields']%}    if( value == {{enum['name']}}_{{key}})
-    {
-      return std::string("{{key}}");
-    }
-{%endfor%}    std::stringstream ss;
-    ss << "Value \"" << value << "\" cannot be converted to {{enum['name']}}. Possible values are: "
-{% for key in enum['fields']%}        << " {{key}} = " << static_cast<int64_t>({{enum['name']}}_{{key}})  << ", " 
-{% endfor %}        << std::endl;
-    std::string msg = ss.str();
-    throw std::runtime_error(msg);
-  }
-
-  inline void FromString({{enum['name']}}& value, std::string strValue)
-  {
-{% for key in enum['fields']%}    if( strValue == std::string("{{key}}") )
-    {
-      value = {{enum['name']}}_{{key}};
-      return;
-    }
-{%endfor%}
-    std::stringstream ss;
-    ss << "String \"" << strValue << "\" cannot be converted to {{enum['name']}}. Possible values are: {% for key in enum['fields']%}{{key}} {% endfor %}";
-    std::string msg = ss.str();
-    throw std::runtime_error(msg);
-  }
-
-
-  inline void _StoneDeserializeValue(
-    {{enum['name']}}& destValue, const Json::Value& jsonValue)
-  {
-    if (!jsonValue.isNull())
-    {
-      FromString(destValue, jsonValue.asString());
-    }
-  }
-
-  inline Json::Value _StoneSerializeValue(const {{enum['name']}}& value)
-  {
-    std::string strValue = ToString(value);
-    return Json::Value(strValue);
-  }
-
-  inline std::ostream& StoneDumpValue(std::ostream& out, const {{enum['name']}}& value, size_t indent = 0)
-  {
-{% for key in enum['fields']%}    if( value == {{enum['name']}}_{{key}})
-    {
-      out << MakeIndent(indent) << "{{key}}";
-    }
-{%endfor%}    return out;
-  }
-
-{%endfor%}
-{% for struct in structs%}
-#ifdef _MSC_VER
-#pragma region {{struct['name']}}
-#endif //_MSC_VER
-
-  struct {{struct['name']}}
-  {
-{% if struct %}{% if struct['fields'] %}{% for key in struct['fields'] %}    {{CanonToCpp(struct['fields'][key]['type'])}} {{key}};
-{% endfor %}{% endif %}{% endif %}
-    {{struct['name']}}({% if struct %}{% if struct['fields'] %}{% for key in struct['fields'] %}{{CanonToCpp(struct['fields'][key]['type'])}} {{key}} = {% if struct['fields'][key]['defaultValue'] %}{{DefaultValueToCpp(rootName,enums,struct['fields'][key])}} {%else%} {{CanonToCpp(struct['fields'][key]['type'])}}() {%endif%} {{ ", " if not loop.last }}{% endfor %}{% endif %}{% endif %})
-    {
-{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}      this->{{key}} = {{key}};
-{% endfor %}{% endif %}{% endif %}    }
-  };
-
-  inline void _StoneDeserializeValue({{struct['name']}}& destValue, const Json::Value& value)
-  {
-    if (!value.isNull())
-    {
-{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}      _StoneDeserializeValue(destValue.{{key}}, value["{{key}}"]);
-{% endfor %}{% endif %}{% endif %}    }
-  }
-
-  inline Json::Value _StoneSerializeValue(const {{struct['name']}}& value)
-  {
-    Json::Value result(Json::objectValue);
-{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}    result["{{key}}"] = _StoneSerializeValue(value.{{key}});
-{% endfor %}{% endif %}{% endif %}
-    return result;
-  }
-
-  inline std::ostream& StoneDumpValue(std::ostream& out, const {{struct['name']}}& value, size_t indent = 0)
-  {
-    out << MakeIndent(indent) << "{\n";
-{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}    out << MakeIndent(indent+2) << "{{key}}: ";
-    StoneDumpValue(out, value.{{key}},indent+2);
-    out << ", \n";
-{% endfor %}{% endif %}{% endif %}
-    out << MakeIndent(indent) << "}";
-    return out;
-  }
-
-  inline void StoneDeserialize({{struct['name']}}& destValue, const Json::Value& value)
-  {
-    StoneCheckSerializedValueType(value, "{{rootName}}.{{struct['name']}}");
-    _StoneDeserializeValue(destValue, value["value"]);
-  }
-
-  inline Json::Value StoneSerializeToJson(const {{struct['name']}}& value)
-  {
-    Json::Value result(Json::objectValue);
-    result["type"] = "{{rootName}}.{{struct['name']}}";
-    result["value"] = _StoneSerializeValue(value);
-    return result;
-  }
-
-  inline std::string StoneSerialize(const {{struct['name']}}& value)
-  {
-    Json::Value resultJson = StoneSerializeToJson(value);
-    std::string resultStr = resultJson.toStyledString();
-    return resultStr;
-  }
-
-#ifdef _MSC_VER
-#pragma endregion {{struct['name']}}
-#endif //_MSC_VER
-{% endfor %}
-#ifdef _MSC_VER
-#pragma region Dispatching code
-#endif //_MSC_VER
-
-  class IHandler
-  {
-  public:
-{% for struct in structs%}{% if struct['__meta__'].handleInCpp %}    virtual bool Handle(const {{struct['name']}}& value) = 0;
-{% endif %}{% endfor %}  };
-
-  /** Service function for StoneDispatchToHandler */
-  inline bool StoneDispatchJsonToHandler(
-    const Json::Value& jsonValue, IHandler* handler)
-  {
-    StoneCheckSerializedValueTypeGeneric(jsonValue);
-    std::string type = jsonValue["type"].asString();
-    if (type == "")
-    {
-      // this should never ever happen
-      throw std::runtime_error("Caught empty type while dispatching");
-    }
-{% for struct in structs%}{% if struct['__meta__'].handleInCpp %}    else if (type == "{{rootName}}.{{struct['name']}}")
-    {
-      {{struct['name']}} value;
-      _StoneDeserializeValue(value, jsonValue["value"]);
-      return handler->Handle(value);
-    }
-{% endif %}{% endfor %}    else
-    {
-      return false;
-    }
-  }
-
-  /** Takes a serialized type and passes this to the handler */
-  inline bool StoneDispatchToHandler(std::string strValue, IHandler* handler)
-  {
-    Json::Value readValue;
-
-    Json::CharReaderBuilder builder;
-    Json::CharReader* reader = builder.newCharReader();
-
-    StoneSmartPtr<Json::CharReader> ptr(reader);
-
-    std::string errors;
-
-    bool ok = reader->parse(
-      strValue.c_str(),
-      strValue.c_str() + strValue.size(),
-      &readValue,
-      &errors
-    );
-    if (!ok)
-    {
-      std::stringstream ss;
-      ss << "Jsoncpp parsing error: " << errors;
-      throw std::runtime_error(ss.str());
-    }
-    return StoneDispatchJsonToHandler(readValue, handler);
-  }
-
-#ifdef _MSC_VER
-#pragma endregion Dispatching code
-#endif //_MSC_VER
-}
--- a/Resources/CodeGeneration/template.in.ts.j2	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,136 +0,0 @@
-/*
-         1         2         3         4         5         6         7
-12345678901234567890123456789012345678901234567890123456789012345678901234567890
-
-Generated on {{currentDatetime}} by stonegentool
-
-*/
-
-function StoneCheckSerializedValueType(value: any, typeStr: string)
-{
-  StoneCheckSerializedValueTypeGeneric(value);
-
-  if (value['type'] != typeStr)
-  {
-    throw new Error(
-      `Cannot deserialize type ${value['type']} into ${typeStr}`);
-  }
-}
-
-function isString(val: any) :boolean
-{
-  return ((typeof val === 'string') || (val instanceof String));
-}
-
-function StoneCheckSerializedValueTypeGeneric(value: any)
-{
-  // console.//log("+-------------------------------------------------+");
-  // console.//log("|            StoneCheckSerializedValueTypeGeneric |");
-  // console.//log("+-------------------------------------------------+");
-  // console.//log("value = ");
-  // console.//log(value);
-  if ( (!('type' in value)) || (!isString(value.type)) )
-  {
-    throw new Error(
-      "Cannot deserialize value ('type' key invalid)");
-  }
-}
-
-// end of generic methods
-{% for enum in enums%}
-export enum {{enum['name']}} {
-{% for key in enum['fields']%}  {{key}} = "{{key}}"{% if not loop.last %},{%endif%}
-{%endfor%}};
-
-export function {{enum['name']}}_FromString(strValue:string) : {{enum['name']}}
-{
-{% for key in enum['fields'] %}  if( strValue == "{{key}}" )
-  {
-    return {{enum['name']}}.{{key}};
-  }
-{%endfor%}
-  let msg : string =  `String ${strValue} cannot be converted to {{enum['name']}}. Possible values are: {% for key in enum['fields']%}{{key}}{% if not loop.last %}, {%endif%}{% endfor %}`;
-  throw new Error(msg);
-}
-
-export function {{enum['name']}}_ToString(value:{{enum['name']}}) : string
-{
-{% for key in enum['fields'] %}  if( value == {{enum['name']}}.{{key}} )
-  {
-    return "{{key}}";
-  }
-{%endfor%}
-  let msg : string = `Value ${value} cannot be converted to {{enum['name']}}. Possible values are: `;
-{% for key in enum['fields']%}  {
-    let _{{key}}_enumValue : string = {{enum['name']}}.{{key}}; // enums are strings in stonecodegen, so this will work.
-    let msg_{{key}} : string = `{{key}} (${_{{key}}_enumValue}){% if not loop.last %}, {%endif%}`;
-    msg = msg + msg_{{key}};
-  }
-{%endfor%}  throw new Error(msg);
-}
-{%endfor%}
-
-
-{% for struct in structs%}export class {{struct['name']}} {
-{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}  {{key}}:{{CanonToTs(struct['fields'][key]['type'])}};
-{% endfor %}{% endif %}{% endif %}
-  constructor() {
-{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}{% if NeedsTsConstruction(enums,CanonToTs(struct['fields'][key]['type'])) %}    this.{{key}} = new {{CanonToTs(struct['fields'][key]['type'])}}();
-{% endif %}
-{% if struct['fields'][key]['defaultValue'] %}    this.{{key}} = {{DefaultValueToTs(enums,struct['fields'][key])}};
-{% endif %}{% endfor %}{% endif %}{% endif %}  }
-
-  public StoneSerialize(): string {
-    let container: object = {};
-    container['type'] = '{{rootName}}.{{struct['name']}}';
-    container['value'] = this;
-    return JSON.stringify(container);
-  }
-
-  public static StoneDeserialize(valueStr: string) : {{struct['name']}}
-  {
-    let value: any = JSON.parse(valueStr);
-    StoneCheckSerializedValueType(value, '{{rootName}}.{{struct['name']}}');
-    let result: {{struct['name']}} = value['value'] as {{struct['name']}};
-    return result;
-  }
-}
-{% endfor %}
-export interface IHandler {
-{% for struct in structs%}{% if struct['__meta__'].handleInTypescript %}  Handle{{struct['name']}}(value:  {{struct['name']}}): boolean;
-{% endif %}{% endfor %}};
-
-/** Service function for StoneDispatchToHandler */
-export function StoneDispatchJsonToHandler(
-  jsonValue: any, handler: IHandler): boolean
-{
-  StoneCheckSerializedValueTypeGeneric(jsonValue);
-  let type: string = jsonValue["type"];
-  if (type == "")
-  {
-    // this should never ever happen
-    throw new Error("Caught empty type while dispatching");
-  }
-{% for struct in structs%}{% if struct['__meta__'].handleInTypescript %}  else if (type == "{{rootName}}.{{struct['name']}}")
-  {
-    let value = jsonValue["value"] as {{struct['name']}};
-    return handler.Handle{{struct['name']}}(value);
-  }
-{% endif %}{% endfor %}  else
-  {
-    return false;
-  }
-}
-
-/** Takes a serialized type and passes this to the handler */
-export function StoneDispatchToHandler(
-  strValue: string, handler: IHandler): boolean
-{
-  // console.//log("+------------------------------------------------+");
-  // console.//log("|            StoneDispatchToHandler              |");
-  // console.//log("+------------------------------------------------+");
-  // console.//log("strValue = ");
-  // console.//log(strValue);
-  let jsonValue: any = JSON.parse(strValue)
-  return StoneDispatchJsonToHandler(jsonValue, handler);
-}
--- a/Resources/CodeGeneration/testCppHandler/CMakeLists.txt	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-cmake_minimum_required(VERSION 2.8)
-
-project(testCppHandler)
-
-set(testCppHandler_Codegen_Deps
-  ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml 
-  ${CMAKE_CURRENT_LIST_DIR}/../template.in.h.j2
-) 
-
-add_custom_command(
-    OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/VsolMessages_generated.hpp
-    COMMAND python ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml
-    DEPENDS ${testCppHandler_Codegen_Deps}
-)
-
-include(${CMAKE_BINARY_DIR}/conanbuildinfo_multi.cmake)
-conan_basic_setup()
-
-add_executable(testCppHandler main.cpp ${CMAKE_CURRENT_BINARY_DIR}/VsolMessages_generated.hpp ${testCppHandler_Codegen_Deps})
-
-target_include_directories(testCppHandler PUBLIC ${CMAKE_BINARY_DIR})
-
-conan_target_link_libraries(testCppHandler)
-
-set_property(TARGET testCppHandler PROPERTY CXX_STANDARD 17)
-
-install(TARGETS testCppHandler DESTINATION bin)
-
--- a/Resources/CodeGeneration/testCppHandler/README.md	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-Requirements
-==============
-- Install Python 3.x (tested with 3.7)
-- Install conan with `pip install conan` (tested with 1.12.2)
-- Install CMake (tested with 3.12)
-- Under Windows: Visual Studio 2017
-- Under *nix*: Ninja
-
-How to build under *nix*
-===============================
-- Navigate to `testCppHandler` folder
-- `conan install . -g cmake`
-- `mkdir build`
-- `cd build`
-- `cmake -G "Ninja" ..`
-- `cmake --build . --config Debug` or - `cmake --build . --config Release`
-
-How to build under Windows with Visual Studio
-==============================================
-- Navigate to repo root
-- `mkdir build`
-- `cd build`
-- `conan install .. -g cmake_multi -s build_type=Release`
-- `conan install .. -g cmake_multi -s build_type=Debug`
-- `cmake -G "Visual Studio 15 2017 Win64" ..`  (modify for your current Visual Studio version)
-- `cmake --build . --config Debug` or - `cmake --build . --config Release`
-
-How to execute the test
-=======================
-- `cd test_data && testCppHandler --pattern=*.json`
-
-
-
-
--- a/Resources/CodeGeneration/testCppHandler/conanfile.txt	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-[requires]
-jsoncpp/1.8.4@theirix/stable
-gtest/1.8.1@bincrafters/stable
-boost/1.69.0@conan/stable
--- a/Resources/CodeGeneration/testCppHandler/main.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-#include <string>
-#include <fstream>
-#include <filesystem>
-#include <regex>
-using namespace std;
-namespace fs = std::filesystem;
-
-#include <boost/program_options.hpp>
-using namespace boost::program_options;
-
-#include "TestStoneCodeGen_generated.hpp"
-
-/**
-Transforms `str` by replacing occurrences of `oldStr` with `newStr`, using 
-plain text (*not* regular expressions.)
-*/
-static inline void ReplaceInString(
-  string& str,
-  const std::string& oldStr,
-  const std::string& newStr)
-{
-  std::string::size_type pos = 0u;
-  while ((pos = str.find(oldStr, pos)) != std::string::npos) {
-    str.replace(pos, oldStr.length(), newStr);
-    pos += newStr.length();
-  }
-}
-
-string SlurpFile(const string& fileName)
-{
-  ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate);
-
-  ifstream::pos_type fileSize = ifs.tellg();
-  ifs.seekg(0, ios::beg);
-
-  vector<char> bytes(fileSize);
-  ifs.read(bytes.data(), fileSize);
-
-  return string(bytes.data(), fileSize);
-}
-
-class MyHandler : public TestStoneCodeGen::IHandler
-{
-public:
-  virtual bool Handle(const TestStoneCodeGen::A& value) override
-  {
-    TestStoneCodeGen::StoneDumpValue(cout, value);
-    return true;
-  }
-  virtual bool Handle(const TestStoneCodeGen::B& value) override
-  {
-    TestStoneCodeGen::StoneDumpValue(cout, value);
-    return true;
-  }
-  virtual bool Handle(const TestStoneCodeGen::C& value) override
-  {
-    TestStoneCodeGen::StoneDumpValue(cout, value);
-    return true;
-  }
-  virtual bool Handle(const TestStoneCodeGen::Message1& value) override
-  {
-    TestStoneCodeGen::StoneDumpValue(cout, value);
-    return true;
-  }
-  virtual bool Handle(const TestStoneCodeGen::Message2& value) override
-  {
-    TestStoneCodeGen::StoneDumpValue(cout, value);
-    return true;
-  }
-};
-
-template<typename T>
-void ProcessPath(T filePath)
-{
-  cout << "+--------------------------------------------+\n";
-  cout << "| Processing: " << filePath.path().string() << "\n";
-  cout << "+--------------------------------------------+\n";
-  MyHandler handler;
-  auto contents = SlurpFile(filePath.path().string());
-  TestStoneCodeGen::StoneDispatchToHandler(contents, &handler);
-}
-
-int main(int argc, char** argv)
-{
-  try
-  {
-
-    options_description desc("Allowed options");
-    desc.add_options()
-      // First parameter describes option name/short name
-      // The second is parameter to option
-      // The third is description
-      ("help,h", "print usage message")
-      ("pattern,p", value<string>(), "pattern for input")
-      ;
-
-    variables_map vm;
-    store(parse_command_line(argc, argv, desc), vm);
-
-    if (vm.count("help"))
-    {
-      cout << desc << "\n";
-      return 0;
-    }
-
-    notify(vm);
-
-    string pattern = vm["pattern"].as<string>();
-
-    // tranform globbing pattern into regex
-    // we should deal with -, ., *...
-    string regexPatternStr = pattern;
-    cout << "Pattern is: " << regexPatternStr << endl;
-    ReplaceInString(regexPatternStr, "\\", "\\\\");
-    ReplaceInString(regexPatternStr, "-", "\\-");
-    ReplaceInString(regexPatternStr, ".", "\\.");
-    ReplaceInString(regexPatternStr, "*", ".*");
-    ReplaceInString(regexPatternStr, "?", ".");
-    cout << "Corresponding regex is: " << regexPatternStr << endl;
-
-    regex regexPattern(regexPatternStr);
-
-    for (auto& p : fs::directory_iterator("."))
-    {
-      auto fileName = p.path().filename().string();
-      if (regex_match(fileName, regexPattern))
-      {
-        ProcessPath(p);
-      }
-    }
-    return 0;
-
-
-  }
-  catch (exception& e)
-  {
-    cerr << e.what() << "\n";
-  }
-}
\ No newline at end of file
--- a/Resources/CodeGeneration/testCppHandler/test_data/test_Message2.json	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-{
-  "type": "TestStoneCodeGen.Message2",
-  "value": {
-    "memberVectorOfMessage1": [
-      {
-        "memberInt32": 42,
-        "memberString": "Benjamin",
-        "memberEnumMonth": "January",
-        "memberBool": false,
-        "memberFloat32": 0.1,
-        "memberFloat64": -0.2
-      },
-      {
-        "memberInt32": 43,
-        "memberString": "Sandrine",
-        "memberEnumMonth": "March"
-      }
-    ],
-    "memberVectorOfString": [
-      "Mercadet",
-      "Poisson"
-    ],
-    "memberMapStringString": {
-      "44": "key 44",
-      "45": "key 45"
-    },
-    "memberMapStringStruct": {
-      "54": {
-        "memberInt32": 43,
-        "memberString": "Sandrine",
-        "memberEnumMonth": "March"
-      },
-      "55": {
-        "memberInt32": 42,
-        "memberString": "Benjamin",
-        "memberEnumMonth": "January",
-        "memberBool": false
-      }
-    },
-    "memberString": "Prout zizi",
-    "memberMapEnumFloat" : {
-      "SaltAndPepper" : 0.1,
-      "CreamAndChives" : -0.2
-    },
-    "memberJson" : {"custom-key": "custom-value"}
-  }
-}
\ No newline at end of file
--- a/Resources/CodeGeneration/testWasmIntegrated/CMakeLists.txt	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-cmake_minimum_required(VERSION 2.8)
-
-project(testWasmIntegratedCpp)
-
-set(WASM_FLAGS "-s WASM=1 -O0 -g0")
-set(WASM_MODULE_NAME "TestWasmIntegratedModule" CACHE STRING "Name of the WebAssembly module")
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}")
-#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js  -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${CMAKE_CURRENT_LIST_DIR}/DefaultLibrary.js -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0 -s EXPORT_NAME='\"${WASM_MODULE_NAME}\"' -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=536870912 -s TOTAL_STACK=128000000")  # 512MB + resize
-
-add_definitions(-DORTHANC_ENABLE_WASM=1)
-
-set(testWasmIntegratedCpp_Codegen_Deps
-  ${CMAKE_CURRENT_LIST_DIR}/testWasmIntegratedCpp_api.yaml 
-  ${CMAKE_CURRENT_LIST_DIR}/../template.in.h.j2
-  ${CMAKE_CURRENT_LIST_DIR}/../template.in.ts.j2
-) 
-
-set(jsoncppRootDir ${CMAKE_CURRENT_LIST_DIR}/jsoncpp-1.8.4)
-
-add_custom_command(
-    OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.hpp ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.ts
-    COMMAND python3 ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml
-    DEPENDS ${testCppHandler_Codegen_Deps} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml 
-)
-
-add_executable(testWasmIntegratedCpp
-  main.cpp
-  ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.hpp
-  ${jsoncppRootDir}/jsoncpp.cpp
-  ${testCppHandler_Codegen_Deps})
-
-target_include_directories(testWasmIntegratedCpp  PUBLIC ${CMAKE_BINARY_DIR})
-target_include_directories(testWasmIntegratedCpp  PUBLIC ${jsoncppRootDir})
-
-set_property(TARGET testWasmIntegratedCpp PROPERTY CXX_STANDARD 11)
-
-
--- a/Resources/CodeGeneration/testWasmIntegrated/DefaultLibrary.js	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-// this file contains the JS method you want to expose to C++ code
-
-mergeInto(LibraryManager.library, {
-  // each time the Application updates its status, it may signal it through this method. i.e, to change the status of a button in the web interface
-  // It needs to be put in this file so that the emscripten SDK linker knows where to find it.
-  SendFreeTextFromCppJS: function(statusUpdateMessage) {
-    var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage);
-    window.SendFreeTextFromCpp(statusUpdateMessage_);
-  },
-  SendMessageFromCppJS: function(statusUpdateMessage) {
-    var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage);
-    window.SendMessageFromCpp(statusUpdateMessage_);
-  }
-});
-
--- a/Resources/CodeGeneration/testWasmIntegrated/build-web.sh	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-#!/bin/bash
-set -e
-
-mkdir -p build-final
-
-# compile TS to JS
-tsc --module commonjs --sourceMap -t ES2015 --outDir "build-tsc/" build-wasm/TestStoneCodeGen_generated.ts testWasmIntegrated.ts 
-
-# bundle JS files to final build dir 
-browserify "build-tsc/build-wasm/testWasmIntegratedCpp_generated.js" "build-tsc/testWasmIntegrated.js" -o "build-final/testWasmIntegratedApp.js"
-
-# copy WASM loader JS file to final build dir 
-cp build-wasm/testWasmIntegratedCpp.js build-final/
-
-# copy HTML start page to output dir
-cp testWasmIntegrated.html build-final/
-
-
-# copy styles to output dir
-cp styles.css build-final/
-
-# copy WASM binary to output dir
-cp build-wasm/testWasmIntegratedCpp.wasm  build-final/
-
-cp ../test_data/testTestStoneCodeGen.yaml build-final/
-cp ../testCppHandler/test_data/test_Message2.json build-final/cppHandler_test_Message2.json
-
-echo "...Serving files at http://127.0.0.1:8080/build-final/testWasmIntegrated.html"
-
-sudo python3 serve.py
-
--- a/Resources/CodeGeneration/testWasmIntegrated/build.sh	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-#!/bin/bash
-
-set -e
-
-mkdir -p build-wasm
-cd build-wasm
-
-# shellcheck source="$HOME/apps/emsdk/emsdk_env.sh"
-source "$HOME/apps/emsdk/emsdk_env.sh"
-cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_WASM=ON ..
-
-ninja
-
-cd ..
-
-./build-web.sh
-
--- a/Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/json/json-forwards.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,344 +0,0 @@
-/// Json-cpp amalgamated forward header (http://jsoncpp.sourceforge.net/).
-/// It is intended to be used with #include "json/json-forwards.h"
-/// This header provides forward declaration for all JsonCpp types.
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: LICENSE
-// //////////////////////////////////////////////////////////////////////
-
-/*
-The JsonCpp library's source code, including accompanying documentation, 
-tests and demonstration applications, are licensed under the following
-conditions...
-
-Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all 
-jurisdictions which recognize such a disclaimer. In such jurisdictions, 
-this software is released into the Public Domain.
-
-In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
-2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
-The JsonCpp Authors, and is released under the terms of the MIT License (see below).
-
-In jurisdictions which recognize Public Domain property, the user of this 
-software may choose to accept it either as 1) Public Domain, 2) under the 
-conditions of the MIT License (see below), or 3) under the terms of dual 
-Public Domain/MIT License conditions described here, as they choose.
-
-The MIT License is about as close to Public Domain as a license can get, and is
-described in clear, concise terms at:
-
-   http://en.wikipedia.org/wiki/MIT_License
-   
-The full text of the MIT License follows:
-
-========================================================================
-Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use, copy,
-modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-========================================================================
-(END LICENSE TEXT)
-
-The MIT license is compatible with both the GPL and commercial
-software, affording one all of the rights of Public Domain with the
-minor nuisance of being required to keep the above copyright notice
-and license text in the source code. Note also that by accepting the
-Public Domain "license" you can re-license your copy using whatever
-license you like.
-
-*/
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: LICENSE
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-#ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED
-# define JSON_FORWARD_AMALGAMATED_H_INCLUDED
-/// If defined, indicates that the source file is amalgamated
-/// to prevent private header inclusion.
-#define JSON_IS_AMALGAMATION
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: include/json/config.h
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef JSON_CONFIG_H_INCLUDED
-#define JSON_CONFIG_H_INCLUDED
-#include <cstddef>
-#include <cstdint>
-#include <istream>
-#include <memory>
-#include <ostream>
-#include <sstream>
-#include <string>
-#include <type_traits>
-
-/// If defined, indicates that json library is embedded in CppTL library.
-//# define JSON_IN_CPPTL 1
-
-/// If defined, indicates that json may leverage CppTL library
-//#  define JSON_USE_CPPTL 1
-/// If defined, indicates that cpptl vector based map should be used instead of
-/// std::map
-/// as Value container.
-//#  define JSON_USE_CPPTL_SMALLMAP 1
-
-// If non-zero, the library uses exceptions to report bad input instead of C
-// assertion macros. The default is to use exceptions.
-#ifndef JSON_USE_EXCEPTION
-#define JSON_USE_EXCEPTION 1
-#endif
-
-/// If defined, indicates that the source file is amalgamated
-/// to prevent private header inclusion.
-/// Remarks: it is automatically defined in the generated amalgamated header.
-// #define JSON_IS_AMALGAMATION
-
-#ifdef JSON_IN_CPPTL
-#include <cpptl/config.h>
-#ifndef JSON_USE_CPPTL
-#define JSON_USE_CPPTL 1
-#endif
-#endif
-
-#ifdef JSON_IN_CPPTL
-#define JSON_API CPPTL_API
-#elif defined(JSON_DLL_BUILD)
-#if defined(_MSC_VER) || defined(__MINGW32__)
-#define JSON_API __declspec(dllexport)
-#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
-#endif // if defined(_MSC_VER)
-#elif defined(JSON_DLL)
-#if defined(_MSC_VER) || defined(__MINGW32__)
-#define JSON_API __declspec(dllimport)
-#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
-#endif // if defined(_MSC_VER)
-#endif // ifdef JSON_IN_CPPTL
-#if !defined(JSON_API)
-#define JSON_API
-#endif
-
-#if defined(_MSC_VER) && _MSC_VER < 1800
-#error                                                                         \
-    "ERROR:  Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities"
-#endif
-
-#if defined(_MSC_VER) && _MSC_VER < 1900
-// As recommended at
-// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
-extern JSON_API int
-msvc_pre1900_c99_snprintf(char* outBuf, size_t size, const char* format, ...);
-#define jsoncpp_snprintf msvc_pre1900_c99_snprintf
-#else
-#define jsoncpp_snprintf std::snprintf
-#endif
-
-// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
-// integer
-// Storages, and 64 bits integer support is disabled.
-// #define JSON_NO_INT64 1
-
-#if defined(_MSC_VER) // MSVC
-#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
-#endif // defined(_MSC_VER)
-
-// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
-// C++11 should be used directly in JSONCPP.
-#define JSONCPP_OVERRIDE override
-
-#if __cplusplus >= 201103L
-#define JSONCPP_NOEXCEPT noexcept
-#define JSONCPP_OP_EXPLICIT explicit
-#elif defined(_MSC_VER) && _MSC_VER < 1900
-#define JSONCPP_NOEXCEPT throw()
-#define JSONCPP_OP_EXPLICIT explicit
-#elif defined(_MSC_VER) && _MSC_VER >= 1900
-#define JSONCPP_NOEXCEPT noexcept
-#define JSONCPP_OP_EXPLICIT explicit
-#else
-#define JSONCPP_NOEXCEPT throw()
-#define JSONCPP_OP_EXPLICIT
-#endif
-
-#ifndef JSON_HAS_RVALUE_REFERENCES
-
-#if defined(_MSC_VER)
-#define JSON_HAS_RVALUE_REFERENCES 1
-#endif // MSVC >= 2013
-
-#ifdef __clang__
-#if __has_feature(cxx_rvalue_references)
-#define JSON_HAS_RVALUE_REFERENCES 1
-#endif // has_feature
-
-#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
-#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
-#define JSON_HAS_RVALUE_REFERENCES 1
-#endif // GXX_EXPERIMENTAL
-
-#endif // __clang__ || __GNUC__
-
-#endif // not defined JSON_HAS_RVALUE_REFERENCES
-
-#ifndef JSON_HAS_RVALUE_REFERENCES
-#define JSON_HAS_RVALUE_REFERENCES 0
-#endif
-
-#ifdef __clang__
-#if __has_extension(attribute_deprecated_with_message)
-#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
-#endif
-#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
-#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
-#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
-#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
-#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
-#endif // GNUC version
-#endif // __clang__ || __GNUC__
-
-#if !defined(JSONCPP_DEPRECATED)
-#define JSONCPP_DEPRECATED(message)
-#endif // if !defined(JSONCPP_DEPRECATED)
-
-#if __GNUC__ >= 6
-#define JSON_USE_INT64_DOUBLE_CONVERSION 1
-#endif
-
-#if !defined(JSON_IS_AMALGAMATION)
-
-#include "allocator.h"
-#include "version.h"
-
-#endif // if !defined(JSON_IS_AMALGAMATION)
-
-namespace Json {
-typedef int Int;
-typedef unsigned int UInt;
-#if defined(JSON_NO_INT64)
-typedef int LargestInt;
-typedef unsigned int LargestUInt;
-#undef JSON_HAS_INT64
-#else                 // if defined(JSON_NO_INT64)
-// For Microsoft Visual use specific types as long long is not supported
-#if defined(_MSC_VER) // Microsoft Visual Studio
-typedef __int64 Int64;
-typedef unsigned __int64 UInt64;
-#else                 // if defined(_MSC_VER) // Other platforms, use long long
-typedef int64_t Int64;
-typedef uint64_t UInt64;
-#endif                // if defined(_MSC_VER)
-typedef Int64 LargestInt;
-typedef UInt64 LargestUInt;
-#define JSON_HAS_INT64
-#endif // if defined(JSON_NO_INT64)
-
-template <typename T>
-using Allocator = typename std::conditional<JSONCPP_USING_SECURE_MEMORY,
-                                            SecureAllocator<T>,
-                                            std::allocator<T>>::type;
-using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
-using IStringStream = std::basic_istringstream<String::value_type,
-                                               String::traits_type,
-                                               String::allocator_type>;
-using OStringStream = std::basic_ostringstream<String::value_type,
-                                               String::traits_type,
-                                               String::allocator_type>;
-using IStream = std::istream;
-using OStream = std::ostream;
-} // namespace Json
-
-// Legacy names (formerly macros).
-using JSONCPP_STRING = Json::String;
-using JSONCPP_ISTRINGSTREAM = Json::IStringStream;
-using JSONCPP_OSTRINGSTREAM = Json::OStringStream;
-using JSONCPP_ISTREAM = Json::IStream;
-using JSONCPP_OSTREAM = Json::OStream;
-
-#endif // JSON_CONFIG_H_INCLUDED
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: include/json/config.h
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: include/json/forwards.h
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef JSON_FORWARDS_H_INCLUDED
-#define JSON_FORWARDS_H_INCLUDED
-
-#if !defined(JSON_IS_AMALGAMATION)
-#include "config.h"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-
-namespace Json {
-
-// writer.h
-class FastWriter;
-class StyledWriter;
-
-// reader.h
-class Reader;
-
-// features.h
-class Features;
-
-// value.h
-typedef unsigned int ArrayIndex;
-class StaticString;
-class Path;
-class PathArgument;
-class Value;
-class ValueIteratorBase;
-class ValueIterator;
-class ValueConstIterator;
-
-} // namespace Json
-
-#endif // JSON_FORWARDS_H_INCLUDED
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: include/json/forwards.h
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-#endif //ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED
--- a/Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/json/json.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2366 +0,0 @@
-/// Json-cpp amalgamated header (http://jsoncpp.sourceforge.net/).
-/// It is intended to be used with #include "json/json.h"
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: LICENSE
-// //////////////////////////////////////////////////////////////////////
-
-/*
-The JsonCpp library's source code, including accompanying documentation, 
-tests and demonstration applications, are licensed under the following
-conditions...
-
-Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all 
-jurisdictions which recognize such a disclaimer. In such jurisdictions, 
-this software is released into the Public Domain.
-
-In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
-2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
-The JsonCpp Authors, and is released under the terms of the MIT License (see below).
-
-In jurisdictions which recognize Public Domain property, the user of this 
-software may choose to accept it either as 1) Public Domain, 2) under the 
-conditions of the MIT License (see below), or 3) under the terms of dual 
-Public Domain/MIT License conditions described here, as they choose.
-
-The MIT License is about as close to Public Domain as a license can get, and is
-described in clear, concise terms at:
-
-   http://en.wikipedia.org/wiki/MIT_License
-   
-The full text of the MIT License follows:
-
-========================================================================
-Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use, copy,
-modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-========================================================================
-(END LICENSE TEXT)
-
-The MIT license is compatible with both the GPL and commercial
-software, affording one all of the rights of Public Domain with the
-minor nuisance of being required to keep the above copyright notice
-and license text in the source code. Note also that by accepting the
-Public Domain "license" you can re-license your copy using whatever
-license you like.
-
-*/
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: LICENSE
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-#ifndef JSON_AMALGAMATED_H_INCLUDED
-# define JSON_AMALGAMATED_H_INCLUDED
-/// If defined, indicates that the source file is amalgamated
-/// to prevent private header inclusion.
-#define JSON_IS_AMALGAMATION
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: include/json/version.h
-// //////////////////////////////////////////////////////////////////////
-
-// DO NOT EDIT. This file (and "version") is generated by CMake.
-// Run CMake configure step to update it.
-#ifndef JSON_VERSION_H_INCLUDED
-#define JSON_VERSION_H_INCLUDED
-
-#define JSONCPP_VERSION_STRING "1.8.4"
-#define JSONCPP_VERSION_MAJOR 1
-#define JSONCPP_VERSION_MINOR 8
-#define JSONCPP_VERSION_PATCH 4
-#define JSONCPP_VERSION_QUALIFIER
-#define JSONCPP_VERSION_HEXA                                                   \
-  ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) |             \
-   (JSONCPP_VERSION_PATCH << 8))
-
-#ifdef JSONCPP_USING_SECURE_MEMORY
-#undef JSONCPP_USING_SECURE_MEMORY
-#endif
-#define JSONCPP_USING_SECURE_MEMORY 0
-// If non-zero, the library zeroes any memory that it has allocated before
-// it frees its memory.
-
-#endif // JSON_VERSION_H_INCLUDED
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: include/json/version.h
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: include/json/allocator.h
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef CPPTL_JSON_ALLOCATOR_H_INCLUDED
-#define CPPTL_JSON_ALLOCATOR_H_INCLUDED
-
-#include <cstring>
-#include <memory>
-
-#pragma pack(push, 8)
-
-namespace Json {
-template <typename T> class SecureAllocator {
-public:
-  // Type definitions
-  using value_type = T;
-  using pointer = T*;
-  using const_pointer = const T*;
-  using reference = T&;
-  using const_reference = const T&;
-  using size_type = std::size_t;
-  using difference_type = std::ptrdiff_t;
-
-  /**
-   * Allocate memory for N items using the standard allocator.
-   */
-  pointer allocate(size_type n) {
-    // allocate using "global operator new"
-    return static_cast<pointer>(::operator new(n * sizeof(T)));
-  }
-
-  /**
-   * Release memory which was allocated for N items at pointer P.
-   *
-   * The memory block is filled with zeroes before being released.
-   * The pointer argument is tagged as "volatile" to prevent the
-   * compiler optimizing out this critical step.
-   */
-  void deallocate(volatile pointer p, size_type n) {
-    std::memset(p, 0, n * sizeof(T));
-    // free using "global operator delete"
-    ::operator delete(p);
-  }
-
-  /**
-   * Construct an item in-place at pointer P.
-   */
-  template <typename... Args> void construct(pointer p, Args&&... args) {
-    // construct using "placement new" and "perfect forwarding"
-    ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
-  }
-
-  size_type max_size() const { return size_t(-1) / sizeof(T); }
-
-  pointer address(reference x) const { return std::addressof(x); }
-
-  const_pointer address(const_reference x) const { return std::addressof(x); }
-
-  /**
-   * Destroy an item in-place at pointer P.
-   */
-  void destroy(pointer p) {
-    // destroy using "explicit destructor"
-    p->~T();
-  }
-
-  // Boilerplate
-  SecureAllocator() {}
-  template <typename U> SecureAllocator(const SecureAllocator<U>&) {}
-  template <typename U> struct rebind { using other = SecureAllocator<U>; };
-};
-
-template <typename T, typename U>
-bool operator==(const SecureAllocator<T>&, const SecureAllocator<U>&) {
-  return true;
-}
-
-template <typename T, typename U>
-bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
-  return false;
-}
-
-} // namespace Json
-
-#pragma pack(pop)
-
-#endif // CPPTL_JSON_ALLOCATOR_H_INCLUDED
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: include/json/allocator.h
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: include/json/config.h
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef JSON_CONFIG_H_INCLUDED
-#define JSON_CONFIG_H_INCLUDED
-#include <cstddef>
-#include <cstdint>
-#include <istream>
-#include <memory>
-#include <ostream>
-#include <sstream>
-#include <string>
-#include <type_traits>
-
-/// If defined, indicates that json library is embedded in CppTL library.
-//# define JSON_IN_CPPTL 1
-
-/// If defined, indicates that json may leverage CppTL library
-//#  define JSON_USE_CPPTL 1
-/// If defined, indicates that cpptl vector based map should be used instead of
-/// std::map
-/// as Value container.
-//#  define JSON_USE_CPPTL_SMALLMAP 1
-
-// If non-zero, the library uses exceptions to report bad input instead of C
-// assertion macros. The default is to use exceptions.
-#ifndef JSON_USE_EXCEPTION
-#define JSON_USE_EXCEPTION 1
-#endif
-
-/// If defined, indicates that the source file is amalgamated
-/// to prevent private header inclusion.
-/// Remarks: it is automatically defined in the generated amalgamated header.
-// #define JSON_IS_AMALGAMATION
-
-#ifdef JSON_IN_CPPTL
-#include <cpptl/config.h>
-#ifndef JSON_USE_CPPTL
-#define JSON_USE_CPPTL 1
-#endif
-#endif
-
-#ifdef JSON_IN_CPPTL
-#define JSON_API CPPTL_API
-#elif defined(JSON_DLL_BUILD)
-#if defined(_MSC_VER) || defined(__MINGW32__)
-#define JSON_API __declspec(dllexport)
-#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
-#endif // if defined(_MSC_VER)
-#elif defined(JSON_DLL)
-#if defined(_MSC_VER) || defined(__MINGW32__)
-#define JSON_API __declspec(dllimport)
-#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
-#endif // if defined(_MSC_VER)
-#endif // ifdef JSON_IN_CPPTL
-#if !defined(JSON_API)
-#define JSON_API
-#endif
-
-#if defined(_MSC_VER) && _MSC_VER < 1800
-#error                                                                         \
-    "ERROR:  Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities"
-#endif
-
-#if defined(_MSC_VER) && _MSC_VER < 1900
-// As recommended at
-// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
-extern JSON_API int
-msvc_pre1900_c99_snprintf(char* outBuf, size_t size, const char* format, ...);
-#define jsoncpp_snprintf msvc_pre1900_c99_snprintf
-#else
-#define jsoncpp_snprintf std::snprintf
-#endif
-
-// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
-// integer
-// Storages, and 64 bits integer support is disabled.
-// #define JSON_NO_INT64 1
-
-#if defined(_MSC_VER) // MSVC
-#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
-#endif // defined(_MSC_VER)
-
-// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
-// C++11 should be used directly in JSONCPP.
-#define JSONCPP_OVERRIDE override
-
-#if __cplusplus >= 201103L
-#define JSONCPP_NOEXCEPT noexcept
-#define JSONCPP_OP_EXPLICIT explicit
-#elif defined(_MSC_VER) && _MSC_VER < 1900
-#define JSONCPP_NOEXCEPT throw()
-#define JSONCPP_OP_EXPLICIT explicit
-#elif defined(_MSC_VER) && _MSC_VER >= 1900
-#define JSONCPP_NOEXCEPT noexcept
-#define JSONCPP_OP_EXPLICIT explicit
-#else
-#define JSONCPP_NOEXCEPT throw()
-#define JSONCPP_OP_EXPLICIT
-#endif
-
-#ifndef JSON_HAS_RVALUE_REFERENCES
-
-#if defined(_MSC_VER)
-#define JSON_HAS_RVALUE_REFERENCES 1
-#endif // MSVC >= 2013
-
-#ifdef __clang__
-#if __has_feature(cxx_rvalue_references)
-#define JSON_HAS_RVALUE_REFERENCES 1
-#endif // has_feature
-
-#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
-#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
-#define JSON_HAS_RVALUE_REFERENCES 1
-#endif // GXX_EXPERIMENTAL
-
-#endif // __clang__ || __GNUC__
-
-#endif // not defined JSON_HAS_RVALUE_REFERENCES
-
-#ifndef JSON_HAS_RVALUE_REFERENCES
-#define JSON_HAS_RVALUE_REFERENCES 0
-#endif
-
-#ifdef __clang__
-#if __has_extension(attribute_deprecated_with_message)
-#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
-#endif
-#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
-#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
-#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
-#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
-#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
-#endif // GNUC version
-#endif // __clang__ || __GNUC__
-
-#if !defined(JSONCPP_DEPRECATED)
-#define JSONCPP_DEPRECATED(message)
-#endif // if !defined(JSONCPP_DEPRECATED)
-
-#if __GNUC__ >= 6
-#define JSON_USE_INT64_DOUBLE_CONVERSION 1
-#endif
-
-#if !defined(JSON_IS_AMALGAMATION)
-
-#include "allocator.h"
-#include "version.h"
-
-#endif // if !defined(JSON_IS_AMALGAMATION)
-
-namespace Json {
-typedef int Int;
-typedef unsigned int UInt;
-#if defined(JSON_NO_INT64)
-typedef int LargestInt;
-typedef unsigned int LargestUInt;
-#undef JSON_HAS_INT64
-#else                 // if defined(JSON_NO_INT64)
-// For Microsoft Visual use specific types as long long is not supported
-#if defined(_MSC_VER) // Microsoft Visual Studio
-typedef __int64 Int64;
-typedef unsigned __int64 UInt64;
-#else                 // if defined(_MSC_VER) // Other platforms, use long long
-typedef int64_t Int64;
-typedef uint64_t UInt64;
-#endif                // if defined(_MSC_VER)
-typedef Int64 LargestInt;
-typedef UInt64 LargestUInt;
-#define JSON_HAS_INT64
-#endif // if defined(JSON_NO_INT64)
-
-template <typename T>
-using Allocator = typename std::conditional<JSONCPP_USING_SECURE_MEMORY,
-                                            SecureAllocator<T>,
-                                            std::allocator<T>>::type;
-using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
-using IStringStream = std::basic_istringstream<String::value_type,
-                                               String::traits_type,
-                                               String::allocator_type>;
-using OStringStream = std::basic_ostringstream<String::value_type,
-                                               String::traits_type,
-                                               String::allocator_type>;
-using IStream = std::istream;
-using OStream = std::ostream;
-} // namespace Json
-
-// Legacy names (formerly macros).
-using JSONCPP_STRING = Json::String;
-using JSONCPP_ISTRINGSTREAM = Json::IStringStream;
-using JSONCPP_OSTRINGSTREAM = Json::OStringStream;
-using JSONCPP_ISTREAM = Json::IStream;
-using JSONCPP_OSTREAM = Json::OStream;
-
-#endif // JSON_CONFIG_H_INCLUDED
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: include/json/config.h
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: include/json/forwards.h
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef JSON_FORWARDS_H_INCLUDED
-#define JSON_FORWARDS_H_INCLUDED
-
-#if !defined(JSON_IS_AMALGAMATION)
-#include "config.h"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-
-namespace Json {
-
-// writer.h
-class FastWriter;
-class StyledWriter;
-
-// reader.h
-class Reader;
-
-// features.h
-class Features;
-
-// value.h
-typedef unsigned int ArrayIndex;
-class StaticString;
-class Path;
-class PathArgument;
-class Value;
-class ValueIteratorBase;
-class ValueIterator;
-class ValueConstIterator;
-
-} // namespace Json
-
-#endif // JSON_FORWARDS_H_INCLUDED
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: include/json/forwards.h
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: include/json/features.h
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef CPPTL_JSON_FEATURES_H_INCLUDED
-#define CPPTL_JSON_FEATURES_H_INCLUDED
-
-#if !defined(JSON_IS_AMALGAMATION)
-#include "forwards.h"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-
-#pragma pack(push, 8)
-
-namespace Json {
-
-/** \brief Configuration passed to reader and writer.
- * This configuration object can be used to force the Reader or Writer
- * to behave in a standard conforming way.
- */
-class JSON_API Features {
-public:
-  /** \brief A configuration that allows all features and assumes all strings
-   * are UTF-8.
-   * - C & C++ comments are allowed
-   * - Root object can be any JSON value
-   * - Assumes Value strings are encoded in UTF-8
-   */
-  static Features all();
-
-  /** \brief A configuration that is strictly compatible with the JSON
-   * specification.
-   * - Comments are forbidden.
-   * - Root object must be either an array or an object value.
-   * - Assumes Value strings are encoded in UTF-8
-   */
-  static Features strictMode();
-
-  /** \brief Initialize the configuration like JsonConfig::allFeatures;
-   */
-  Features();
-
-  /// \c true if comments are allowed. Default: \c true.
-  bool allowComments_{true};
-
-  /// \c true if root must be either an array or an object value. Default: \c
-  /// false.
-  bool strictRoot_{false};
-
-  /// \c true if dropped null placeholders are allowed. Default: \c false.
-  bool allowDroppedNullPlaceholders_{false};
-
-  /// \c true if numeric object key are allowed. Default: \c false.
-  bool allowNumericKeys_{false};
-};
-
-} // namespace Json
-
-#pragma pack(pop)
-
-#endif // CPPTL_JSON_FEATURES_H_INCLUDED
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: include/json/features.h
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: include/json/value.h
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef CPPTL_JSON_H_INCLUDED
-#define CPPTL_JSON_H_INCLUDED
-
-#if !defined(JSON_IS_AMALGAMATION)
-#include "forwards.h"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-#include <exception>
-#include <string>
-#include <vector>
-
-#ifndef JSON_USE_CPPTL_SMALLMAP
-#include <map>
-#else
-#include <cpptl/smallmap.h>
-#endif
-#ifdef JSON_USE_CPPTL
-#include <cpptl/forwards.h>
-#endif
-
-// Conditional NORETURN attribute on the throw functions would:
-// a) suppress false positives from static code analysis
-// b) possibly improve optimization opportunities.
-#if !defined(JSONCPP_NORETURN)
-#if defined(_MSC_VER)
-#define JSONCPP_NORETURN __declspec(noreturn)
-#elif defined(__GNUC__)
-#define JSONCPP_NORETURN __attribute__((__noreturn__))
-#else
-#define JSONCPP_NORETURN
-#endif
-#endif
-
-// Disable warning C4251: <data member>: <type> needs to have dll-interface to
-// be used by...
-#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
-#pragma warning(push)
-#pragma warning(disable : 4251)
-#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
-
-#pragma pack(push, 8)
-
-/** \brief JSON (JavaScript Object Notation).
- */
-namespace Json {
-
-/** Base class for all exceptions we throw.
- *
- * We use nothing but these internally. Of course, STL can throw others.
- */
-class JSON_API Exception : public std::exception {
-public:
-  Exception(String msg);
-  ~Exception() JSONCPP_NOEXCEPT override;
-  char const* what() const JSONCPP_NOEXCEPT override;
-
-protected:
-  String msg_;
-};
-
-/** Exceptions which the user cannot easily avoid.
- *
- * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input
- *
- * \remark derived from Json::Exception
- */
-class JSON_API RuntimeError : public Exception {
-public:
-  RuntimeError(String const& msg);
-};
-
-/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros.
- *
- * These are precondition-violations (user bugs) and internal errors (our bugs).
- *
- * \remark derived from Json::Exception
- */
-class JSON_API LogicError : public Exception {
-public:
-  LogicError(String const& msg);
-};
-
-/// used internally
-JSONCPP_NORETURN void throwRuntimeError(String const& msg);
-/// used internally
-JSONCPP_NORETURN void throwLogicError(String const& msg);
-
-/** \brief Type of the value held by a Value object.
- */
-enum ValueType {
-  nullValue = 0, ///< 'null' value
-  intValue,      ///< signed integer value
-  uintValue,     ///< unsigned integer value
-  realValue,     ///< double value
-  stringValue,   ///< UTF-8 string value
-  booleanValue,  ///< bool value
-  arrayValue,    ///< array value (ordered list)
-  objectValue    ///< object value (collection of name/value pairs).
-};
-
-enum CommentPlacement {
-  commentBefore = 0,      ///< a comment placed on the line before a value
-  commentAfterOnSameLine, ///< a comment just after a value on the same line
-  commentAfter, ///< a comment on the line after a value (only make sense for
-  /// root value)
-  numberOfCommentPlacement
-};
-
-/** \brief Type of precision for formatting of real values.
- */
-enum PrecisionType {
-  significantDigits = 0, ///< we set max number of significant digits in string
-  decimalPlaces          ///< we set max number of digits after "." in string
-};
-
-//# ifdef JSON_USE_CPPTL
-//   typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;
-//   typedef CppTL::AnyEnumerator<const Value &> EnumValues;
-//# endif
-
-/** \brief Lightweight wrapper to tag static string.
- *
- * Value constructor and objectValue member assignment takes advantage of the
- * StaticString and avoid the cost of string duplication when storing the
- * string or the member name.
- *
- * Example of usage:
- * \code
- * Json::Value aValue( StaticString("some text") );
- * Json::Value object;
- * static const StaticString code("code");
- * object[code] = 1234;
- * \endcode
- */
-class JSON_API StaticString {
-public:
-  explicit StaticString(const char* czstring) : c_str_(czstring) {}
-
-  operator const char*() const { return c_str_; }
-
-  const char* c_str() const { return c_str_; }
-
-private:
-  const char* c_str_;
-};
-
-/** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
- *
- * This class is a discriminated union wrapper that can represents a:
- * - signed integer [range: Value::minInt - Value::maxInt]
- * - unsigned integer (range: 0 - Value::maxUInt)
- * - double
- * - UTF-8 string
- * - boolean
- * - 'null'
- * - an ordered list of Value
- * - collection of name/value pairs (javascript object)
- *
- * The type of the held value is represented by a #ValueType and
- * can be obtained using type().
- *
- * Values of an #objectValue or #arrayValue can be accessed using operator[]()
- * methods.
- * Non-const methods will automatically create the a #nullValue element
- * if it does not exist.
- * The sequence of an #arrayValue will be automatically resized and initialized
- * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
- *
- * The get() methods can be used to obtain default value in the case the
- * required element does not exist.
- *
- * It is possible to iterate over the list of member keys of an object using
- * the getMemberNames() method.
- *
- * \note #Value string-length fit in size_t, but keys must be < 2^30.
- * (The reason is an implementation detail.) A #CharReader will raise an
- * exception if a bound is exceeded to avoid security holes in your app,
- * but the Value API does *not* check bounds. That is the responsibility
- * of the caller.
- */
-class JSON_API Value {
-  friend class ValueIteratorBase;
-
-public:
-  typedef std::vector<String> Members;
-  typedef ValueIterator iterator;
-  typedef ValueConstIterator const_iterator;
-  typedef Json::UInt UInt;
-  typedef Json::Int Int;
-#if defined(JSON_HAS_INT64)
-  typedef Json::UInt64 UInt64;
-  typedef Json::Int64 Int64;
-#endif // defined(JSON_HAS_INT64)
-  typedef Json::LargestInt LargestInt;
-  typedef Json::LargestUInt LargestUInt;
-  typedef Json::ArrayIndex ArrayIndex;
-
-  // Required for boost integration, e. g. BOOST_TEST
-  typedef std::string value_type;
-
-  static const Value& null; ///< We regret this reference to a global instance;
-                            ///< prefer the simpler Value().
-  static const Value& nullRef; ///< just a kludge for binary-compatibility; same
-                               ///< as null
-  static Value const& nullSingleton(); ///< Prefer this to null or nullRef.
-
-  /// Minimum signed integer value that can be stored in a Json::Value.
-  static const LargestInt minLargestInt;
-  /// Maximum signed integer value that can be stored in a Json::Value.
-  static const LargestInt maxLargestInt;
-  /// Maximum unsigned integer value that can be stored in a Json::Value.
-  static const LargestUInt maxLargestUInt;
-
-  /// Minimum signed int value that can be stored in a Json::Value.
-  static const Int minInt;
-  /// Maximum signed int value that can be stored in a Json::Value.
-  static const Int maxInt;
-  /// Maximum unsigned int value that can be stored in a Json::Value.
-  static const UInt maxUInt;
-
-#if defined(JSON_HAS_INT64)
-  /// Minimum signed 64 bits int value that can be stored in a Json::Value.
-  static const Int64 minInt64;
-  /// Maximum signed 64 bits int value that can be stored in a Json::Value.
-  static const Int64 maxInt64;
-  /// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
-  static const UInt64 maxUInt64;
-#endif // defined(JSON_HAS_INT64)
-
-  /// Default precision for real value for string representation.
-  static const UInt defaultRealPrecision;
-
-// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler
-// when using gcc and clang backend compilers.  CZString
-// cannot be defined as private.  See issue #486
-#ifdef __NVCC__
-public:
-#else
-private:
-#endif
-#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
-  class CZString {
-  public:
-    enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy };
-    CZString(ArrayIndex index);
-    CZString(char const* str, unsigned length, DuplicationPolicy allocate);
-    CZString(CZString const& other);
-#if JSON_HAS_RVALUE_REFERENCES
-    CZString(CZString&& other);
-#endif
-    ~CZString();
-    CZString& operator=(const CZString& other);
-
-#if JSON_HAS_RVALUE_REFERENCES
-    CZString& operator=(CZString&& other);
-#endif
-
-    bool operator<(CZString const& other) const;
-    bool operator==(CZString const& other) const;
-    ArrayIndex index() const;
-    // const char* c_str() const; ///< \deprecated
-    char const* data() const;
-    unsigned length() const;
-    bool isStaticString() const;
-
-  private:
-    void swap(CZString& other);
-
-    struct StringStorage {
-      unsigned policy_ : 2;
-      unsigned length_ : 30; // 1GB max
-    };
-
-    char const* cstr_; // actually, a prefixed string, unless policy is noDup
-    union {
-      ArrayIndex index_;
-      StringStorage storage_;
-    };
-  };
-
-public:
-#ifndef JSON_USE_CPPTL_SMALLMAP
-  typedef std::map<CZString, Value> ObjectValues;
-#else
-  typedef CppTL::SmallMap<CZString, Value> ObjectValues;
-#endif // ifndef JSON_USE_CPPTL_SMALLMAP
-#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
-
-public:
-  /** \brief Create a default Value of the given type.
-
-    This is a very useful constructor.
-    To create an empty array, pass arrayValue.
-    To create an empty object, pass objectValue.
-    Another Value can then be set to this one by assignment.
-This is useful since clear() and resize() will not alter types.
-
-    Examples:
-\code
-Json::Value null_value; // null
-Json::Value arr_value(Json::arrayValue); // []
-Json::Value obj_value(Json::objectValue); // {}
-\endcode
-  */
-  Value(ValueType type = nullValue);
-  Value(Int value);
-  Value(UInt value);
-#if defined(JSON_HAS_INT64)
-  Value(Int64 value);
-  Value(UInt64 value);
-#endif // if defined(JSON_HAS_INT64)
-  Value(double value);
-  Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)
-  Value(const char* begin, const char* end); ///< Copy all, incl zeroes.
-  /** \brief Constructs a value from a static string.
-
-   * Like other value string constructor but do not duplicate the string for
-   * internal storage. The given string must remain alive after the call to this
-   * constructor.
-   * \note This works only for null-terminated strings. (We cannot change the
-   *   size of this class, so we have nowhere to store the length,
-   *   which might be computed later for various operations.)
-   *
-   * Example of usage:
-   * \code
-   * static StaticString foo("some text");
-   * Json::Value aValue(foo);
-   * \endcode
-   */
-  Value(const StaticString& value);
-  Value(const String& value); ///< Copy data() til size(). Embedded
-                              ///< zeroes too.
-#ifdef JSON_USE_CPPTL
-  Value(const CppTL::ConstString& value);
-#endif
-  Value(bool value);
-  Value(const Value& other);
-  Value(Value&& other);
-  ~Value();
-
-  /// \note Overwrite existing comments. To preserve comments, use
-  /// #swapPayload().
-  Value& operator=(const Value& other);
-  Value& operator=(Value&& other);
-
-  /// Swap everything.
-  void swap(Value& other);
-  /// Swap values but leave comments and source offsets in place.
-  void swapPayload(Value& other);
-
-  /// copy everything.
-  void copy(const Value& other);
-  /// copy values but leave comments and source offsets in place.
-  void copyPayload(const Value& other);
-
-  ValueType type() const;
-
-  /// Compare payload only, not comments etc.
-  bool operator<(const Value& other) const;
-  bool operator<=(const Value& other) const;
-  bool operator>=(const Value& other) const;
-  bool operator>(const Value& other) const;
-  bool operator==(const Value& other) const;
-  bool operator!=(const Value& other) const;
-  int compare(const Value& other) const;
-
-  const char* asCString() const; ///< Embedded zeroes could cause you trouble!
-#if JSONCPP_USING_SECURE_MEMORY
-  unsigned getCStringLength() const; // Allows you to understand the length of
-                                     // the CString
-#endif
-  String asString() const; ///< Embedded zeroes are possible.
-  /** Get raw char* of string-value.
-   *  \return false if !string. (Seg-fault if str or end are NULL.)
-   */
-  bool getString(char const** begin, char const** end) const;
-#ifdef JSON_USE_CPPTL
-  CppTL::ConstString asConstString() const;
-#endif
-  Int asInt() const;
-  UInt asUInt() const;
-#if defined(JSON_HAS_INT64)
-  Int64 asInt64() const;
-  UInt64 asUInt64() const;
-#endif // if defined(JSON_HAS_INT64)
-  LargestInt asLargestInt() const;
-  LargestUInt asLargestUInt() const;
-  float asFloat() const;
-  double asDouble() const;
-  bool asBool() const;
-
-  bool isNull() const;
-  bool isBool() const;
-  bool isInt() const;
-  bool isInt64() const;
-  bool isUInt() const;
-  bool isUInt64() const;
-  bool isIntegral() const;
-  bool isDouble() const;
-  bool isNumeric() const;
-  bool isString() const;
-  bool isArray() const;
-  bool isObject() const;
-
-  bool isConvertibleTo(ValueType other) const;
-
-  /// Number of values in array or object
-  ArrayIndex size() const;
-
-  /// \brief Return true if empty array, empty object, or null;
-  /// otherwise, false.
-  bool empty() const;
-
-  /// Return !isNull()
-  JSONCPP_OP_EXPLICIT operator bool() const;
-
-  /// Remove all object members and array elements.
-  /// \pre type() is arrayValue, objectValue, or nullValue
-  /// \post type() is unchanged
-  void clear();
-
-  /// Resize the array to newSize elements.
-  /// New elements are initialized to null.
-  /// May only be called on nullValue or arrayValue.
-  /// \pre type() is arrayValue or nullValue
-  /// \post type() is arrayValue
-  void resize(ArrayIndex newSize);
-
-  /// Access an array element (zero based index ).
-  /// If the array contains less than index element, then null value are
-  /// inserted
-  /// in the array so that its size is index+1.
-  /// (You may need to say 'value[0u]' to get your compiler to distinguish
-  ///  this from the operator[] which takes a string.)
-  Value& operator[](ArrayIndex index);
-
-  /// Access an array element (zero based index ).
-  /// If the array contains less than index element, then null value are
-  /// inserted
-  /// in the array so that its size is index+1.
-  /// (You may need to say 'value[0u]' to get your compiler to distinguish
-  ///  this from the operator[] which takes a string.)
-  Value& operator[](int index);
-
-  /// Access an array element (zero based index )
-  /// (You may need to say 'value[0u]' to get your compiler to distinguish
-  ///  this from the operator[] which takes a string.)
-  const Value& operator[](ArrayIndex index) const;
-
-  /// Access an array element (zero based index )
-  /// (You may need to say 'value[0u]' to get your compiler to distinguish
-  ///  this from the operator[] which takes a string.)
-  const Value& operator[](int index) const;
-
-  /// If the array contains at least index+1 elements, returns the element
-  /// value,
-  /// otherwise returns defaultValue.
-  Value get(ArrayIndex index, const Value& defaultValue) const;
-  /// Return true if index < size().
-  bool isValidIndex(ArrayIndex index) const;
-  /// \brief Append value to array at the end.
-  ///
-  /// Equivalent to jsonvalue[jsonvalue.size()] = value;
-  Value& append(const Value& value);
-
-#if JSON_HAS_RVALUE_REFERENCES
-  Value& append(Value&& value);
-#endif
-
-  /// Access an object value by name, create a null member if it does not exist.
-  /// \note Because of our implementation, keys are limited to 2^30 -1 chars.
-  ///  Exceeding that will cause an exception.
-  Value& operator[](const char* key);
-  /// Access an object value by name, returns null if there is no member with
-  /// that name.
-  const Value& operator[](const char* key) const;
-  /// Access an object value by name, create a null member if it does not exist.
-  /// \param key may contain embedded nulls.
-  Value& operator[](const String& key);
-  /// Access an object value by name, returns null if there is no member with
-  /// that name.
-  /// \param key may contain embedded nulls.
-  const Value& operator[](const String& key) const;
-  /** \brief Access an object value by name, create a null member if it does not
-   exist.
-
-   * If the object has no entry for that name, then the member name used to
-   store
-   * the new entry is not duplicated.
-   * Example of use:
-   * \code
-   * Json::Value object;
-   * static const StaticString code("code");
-   * object[code] = 1234;
-   * \endcode
-   */
-  Value& operator[](const StaticString& key);
-#ifdef JSON_USE_CPPTL
-  /// Access an object value by name, create a null member if it does not exist.
-  Value& operator[](const CppTL::ConstString& key);
-  /// Access an object value by name, returns null if there is no member with
-  /// that name.
-  const Value& operator[](const CppTL::ConstString& key) const;
-#endif
-  /// Return the member named key if it exist, defaultValue otherwise.
-  /// \note deep copy
-  Value get(const char* key, const Value& defaultValue) const;
-  /// Return the member named key if it exist, defaultValue otherwise.
-  /// \note deep copy
-  /// \note key may contain embedded nulls.
-  Value
-  get(const char* begin, const char* end, const Value& defaultValue) const;
-  /// Return the member named key if it exist, defaultValue otherwise.
-  /// \note deep copy
-  /// \param key may contain embedded nulls.
-  Value get(const String& key, const Value& defaultValue) const;
-#ifdef JSON_USE_CPPTL
-  /// Return the member named key if it exist, defaultValue otherwise.
-  /// \note deep copy
-  Value get(const CppTL::ConstString& key, const Value& defaultValue) const;
-#endif
-  /// Most general and efficient version of isMember()const, get()const,
-  /// and operator[]const
-  /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
-  Value const* find(char const* begin, char const* end) const;
-  /// Most general and efficient version of object-mutators.
-  /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
-  /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
-  Value const* demand(char const* begin, char const* end);
-  /// \brief Remove and return the named member.
-  ///
-  /// Do nothing if it did not exist.
-  /// \pre type() is objectValue or nullValue
-  /// \post type() is unchanged
-  void removeMember(const char* key);
-  /// Same as removeMember(const char*)
-  /// \param key may contain embedded nulls.
-  void removeMember(const String& key);
-  /// Same as removeMember(const char* begin, const char* end, Value* removed),
-  /// but 'key' is null-terminated.
-  bool removeMember(const char* key, Value* removed);
-  /** \brief Remove the named map member.
-
-      Update 'removed' iff removed.
-      \param key may contain embedded nulls.
-      \return true iff removed (no exceptions)
-  */
-  bool removeMember(String const& key, Value* removed);
-  /// Same as removeMember(String const& key, Value* removed)
-  bool removeMember(const char* begin, const char* end, Value* removed);
-  /** \brief Remove the indexed array element.
-
-      O(n) expensive operations.
-      Update 'removed' iff removed.
-      \return true if removed (no exceptions)
-  */
-  bool removeIndex(ArrayIndex index, Value* removed);
-
-  /// Return true if the object has a member named key.
-  /// \note 'key' must be null-terminated.
-  bool isMember(const char* key) const;
-  /// Return true if the object has a member named key.
-  /// \param key may contain embedded nulls.
-  bool isMember(const String& key) const;
-  /// Same as isMember(String const& key)const
-  bool isMember(const char* begin, const char* end) const;
-#ifdef JSON_USE_CPPTL
-  /// Return true if the object has a member named key.
-  bool isMember(const CppTL::ConstString& key) const;
-#endif
-
-  /// \brief Return a list of the member names.
-  ///
-  /// If null, return an empty list.
-  /// \pre type() is objectValue or nullValue
-  /// \post if type() was nullValue, it remains nullValue
-  Members getMemberNames() const;
-
-  //# ifdef JSON_USE_CPPTL
-  //      EnumMemberNames enumMemberNames() const;
-  //      EnumValues enumValues() const;
-  //# endif
-
-  /// \deprecated Always pass len.
-  JSONCPP_DEPRECATED("Use setComment(String const&) instead.")
-  void setComment(const char* comment, CommentPlacement placement);
-  /// Comments must be //... or /* ... */
-  void setComment(const char* comment, size_t len, CommentPlacement placement);
-  /// Comments must be //... or /* ... */
-  void setComment(const String& comment, CommentPlacement placement);
-  bool hasComment(CommentPlacement placement) const;
-  /// Include delimiters and embedded newlines.
-  String getComment(CommentPlacement placement) const;
-
-  String toStyledString() const;
-
-  const_iterator begin() const;
-  const_iterator end() const;
-
-  iterator begin();
-  iterator end();
-
-  // Accessors for the [start, limit) range of bytes within the JSON text from
-  // which this value was parsed, if any.
-  void setOffsetStart(ptrdiff_t start);
-  void setOffsetLimit(ptrdiff_t limit);
-  ptrdiff_t getOffsetStart() const;
-  ptrdiff_t getOffsetLimit() const;
-
-private:
-  void setType(ValueType v) { bits_.value_type_ = v; }
-  bool isAllocated() const { return bits_.allocated_; }
-  void setIsAllocated(bool v) { bits_.allocated_ = v; }
-
-  void initBasic(ValueType type, bool allocated = false);
-  void dupPayload(const Value& other);
-  void releasePayload();
-  void dupMeta(const Value& other);
-
-  Value& resolveReference(const char* key);
-  Value& resolveReference(const char* key, const char* end);
-
-  struct CommentInfo {
-    CommentInfo();
-    ~CommentInfo();
-
-    void setComment(const char* text, size_t len);
-
-    char* comment_{nullptr};
-  };
-
-  // struct MemberNamesTransform
-  //{
-  //   typedef const char *result_type;
-  //   const char *operator()( const CZString &name ) const
-  //   {
-  //      return name.c_str();
-  //   }
-  //};
-
-  union ValueHolder {
-    LargestInt int_;
-    LargestUInt uint_;
-    double real_;
-    bool bool_;
-    char* string_; // if allocated_, ptr to { unsigned, char[] }.
-    ObjectValues* map_;
-  } value_;
-
-  struct {
-    // Really a ValueType, but types should agree for bitfield packing.
-    unsigned int value_type_ : 8;
-    // Unless allocated_, string_ must be null-terminated.
-    unsigned int allocated_ : 1;
-  } bits_;
-
-  CommentInfo* comments_;
-
-  // [start, limit) byte offsets in the source JSON text from which this Value
-  // was extracted.
-  ptrdiff_t start_;
-  ptrdiff_t limit_;
-};
-
-/** \brief Experimental and untested: represents an element of the "path" to
- * access a node.
- */
-class JSON_API PathArgument {
-public:
-  friend class Path;
-
-  PathArgument();
-  PathArgument(ArrayIndex index);
-  PathArgument(const char* key);
-  PathArgument(const String& key);
-
-private:
-  enum Kind { kindNone = 0, kindIndex, kindKey };
-  String key_;
-  ArrayIndex index_{};
-  Kind kind_{kindNone};
-};
-
-/** \brief Experimental and untested: represents a "path" to access a node.
- *
- * Syntax:
- * - "." => root node
- * - ".[n]" => elements at index 'n' of root node (an array value)
- * - ".name" => member named 'name' of root node (an object value)
- * - ".name1.name2.name3"
- * - ".[0][1][2].name1[3]"
- * - ".%" => member name is provided as parameter
- * - ".[%]" => index is provied as parameter
- */
-class JSON_API Path {
-public:
-  Path(const String& path,
-       const PathArgument& a1 = PathArgument(),
-       const PathArgument& a2 = PathArgument(),
-       const PathArgument& a3 = PathArgument(),
-       const PathArgument& a4 = PathArgument(),
-       const PathArgument& a5 = PathArgument());
-
-  const Value& resolve(const Value& root) const;
-  Value resolve(const Value& root, const Value& defaultValue) const;
-  /// Creates the "path" to access the specified node and returns a reference on
-  /// the node.
-  Value& make(Value& root) const;
-
-private:
-  typedef std::vector<const PathArgument*> InArgs;
-  typedef std::vector<PathArgument> Args;
-
-  void makePath(const String& path, const InArgs& in);
-  void addPathInArg(const String& path,
-                    const InArgs& in,
-                    InArgs::const_iterator& itInArg,
-                    PathArgument::Kind kind);
-  static void invalidPath(const String& path, int location);
-
-  Args args_;
-};
-
-/** \brief base class for Value iterators.
- *
- */
-class JSON_API ValueIteratorBase {
-public:
-  typedef std::bidirectional_iterator_tag iterator_category;
-  typedef unsigned int size_t;
-  typedef int difference_type;
-  typedef ValueIteratorBase SelfType;
-
-  bool operator==(const SelfType& other) const { return isEqual(other); }
-
-  bool operator!=(const SelfType& other) const { return !isEqual(other); }
-
-  difference_type operator-(const SelfType& other) const {
-    return other.computeDistance(*this);
-  }
-
-  /// Return either the index or the member name of the referenced value as a
-  /// Value.
-  Value key() const;
-
-  /// Return the index of the referenced Value, or -1 if it is not an
-  /// arrayValue.
-  UInt index() const;
-
-  /// Return the member name of the referenced Value, or "" if it is not an
-  /// objectValue.
-  /// \note Avoid `c_str()` on result, as embedded zeroes are possible.
-  String name() const;
-
-  /// Return the member name of the referenced Value. "" if it is not an
-  /// objectValue.
-  /// \deprecated This cannot be used for UTF-8 strings, since there can be
-  /// embedded nulls.
-  JSONCPP_DEPRECATED("Use `key = name();` instead.")
-  char const* memberName() const;
-  /// Return the member name of the referenced Value, or NULL if it is not an
-  /// objectValue.
-  /// \note Better version than memberName(). Allows embedded nulls.
-  char const* memberName(char const** end) const;
-
-protected:
-  Value& deref() const;
-
-  void increment();
-
-  void decrement();
-
-  difference_type computeDistance(const SelfType& other) const;
-
-  bool isEqual(const SelfType& other) const;
-
-  void copy(const SelfType& other);
-
-private:
-  Value::ObjectValues::iterator current_;
-  // Indicates that iterator is for a null value.
-  bool isNull_{true};
-
-public:
-  // For some reason, BORLAND needs these at the end, rather
-  // than earlier. No idea why.
-  ValueIteratorBase();
-  explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);
-};
-
-/** \brief const iterator for object and array value.
- *
- */
-class JSON_API ValueConstIterator : public ValueIteratorBase {
-  friend class Value;
-
-public:
-  typedef const Value value_type;
-  // typedef unsigned int size_t;
-  // typedef int difference_type;
-  typedef const Value& reference;
-  typedef const Value* pointer;
-  typedef ValueConstIterator SelfType;
-
-  ValueConstIterator();
-  ValueConstIterator(ValueIterator const& other);
-
-private:
-  /*! \internal Use by Value to create an iterator.
-   */
-  explicit ValueConstIterator(const Value::ObjectValues::iterator& current);
-
-public:
-  SelfType& operator=(const ValueIteratorBase& other);
-
-  SelfType operator++(int) {
-    SelfType temp(*this);
-    ++*this;
-    return temp;
-  }
-
-  SelfType operator--(int) {
-    SelfType temp(*this);
-    --*this;
-    return temp;
-  }
-
-  SelfType& operator--() {
-    decrement();
-    return *this;
-  }
-
-  SelfType& operator++() {
-    increment();
-    return *this;
-  }
-
-  reference operator*() const { return deref(); }
-
-  pointer operator->() const { return &deref(); }
-};
-
-/** \brief Iterator for object and array value.
- */
-class JSON_API ValueIterator : public ValueIteratorBase {
-  friend class Value;
-
-public:
-  typedef Value value_type;
-  typedef unsigned int size_t;
-  typedef int difference_type;
-  typedef Value& reference;
-  typedef Value* pointer;
-  typedef ValueIterator SelfType;
-
-  ValueIterator();
-  explicit ValueIterator(const ValueConstIterator& other);
-  ValueIterator(const ValueIterator& other);
-
-private:
-  /*! \internal Use by Value to create an iterator.
-   */
-  explicit ValueIterator(const Value::ObjectValues::iterator& current);
-
-public:
-  SelfType& operator=(const SelfType& other);
-
-  SelfType operator++(int) {
-    SelfType temp(*this);
-    ++*this;
-    return temp;
-  }
-
-  SelfType operator--(int) {
-    SelfType temp(*this);
-    --*this;
-    return temp;
-  }
-
-  SelfType& operator--() {
-    decrement();
-    return *this;
-  }
-
-  SelfType& operator++() {
-    increment();
-    return *this;
-  }
-
-  reference operator*() const { return deref(); }
-
-  pointer operator->() const { return &deref(); }
-};
-
-inline void swap(Value& a, Value& b) { a.swap(b); }
-
-} // namespace Json
-
-#pragma pack(pop)
-
-#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
-#pragma warning(pop)
-#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
-
-#endif // CPPTL_JSON_H_INCLUDED
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: include/json/value.h
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: include/json/reader.h
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef CPPTL_JSON_READER_H_INCLUDED
-#define CPPTL_JSON_READER_H_INCLUDED
-
-#if !defined(JSON_IS_AMALGAMATION)
-#include "features.h"
-#include "value.h"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-#include <deque>
-#include <iosfwd>
-#include <istream>
-#include <stack>
-#include <string>
-
-// Disable warning C4251: <data member>: <type> needs to have dll-interface to
-// be used by...
-#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
-#pragma warning(push)
-#pragma warning(disable : 4251)
-#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
-
-#pragma pack(push, 8)
-
-namespace Json {
-
-/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
- *Value.
- *
- * \deprecated Use CharReader and CharReaderBuilder.
- */
-class JSON_API Reader {
-public:
-  typedef char Char;
-  typedef const Char* Location;
-
-  /** \brief An error tagged with where in the JSON text it was encountered.
-   *
-   * The offsets give the [start, limit) range of bytes within the text. Note
-   * that this is bytes, not codepoints.
-   *
-   */
-  struct StructuredError {
-    ptrdiff_t offset_start;
-    ptrdiff_t offset_limit;
-    String message;
-  };
-
-  /** \brief Constructs a Reader allowing all features
-   * for parsing.
-   */
-  JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
-  Reader();
-
-  /** \brief Constructs a Reader allowing the specified feature set
-   * for parsing.
-   */
-  JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
-  Reader(const Features& features);
-
-  /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
-   * document.
-   * \param document UTF-8 encoded string containing the document to read.
-   * \param root [out] Contains the root value of the document if it was
-   *             successfully parsed.
-   * \param collectComments \c true to collect comment and allow writing them
-   * back during
-   *                        serialization, \c false to discard comments.
-   *                        This parameter is ignored if
-   * Features::allowComments_
-   *                        is \c false.
-   * \return \c true if the document was successfully parsed, \c false if an
-   * error occurred.
-   */
-  bool
-  parse(const std::string& document, Value& root, bool collectComments = true);
-
-  /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
-   document.
-   * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
-   document to read.
-   * \param endDoc Pointer on the end of the UTF-8 encoded string of the
-   document to read.
-   *               Must be >= beginDoc.
-   * \param root [out] Contains the root value of the document if it was
-   *             successfully parsed.
-   * \param collectComments \c true to collect comment and allow writing them
-   back during
-   *                        serialization, \c false to discard comments.
-   *                        This parameter is ignored if
-   Features::allowComments_
-   *                        is \c false.
-   * \return \c true if the document was successfully parsed, \c false if an
-   error occurred.
-   */
-  bool parse(const char* beginDoc,
-             const char* endDoc,
-             Value& root,
-             bool collectComments = true);
-
-  /// \brief Parse from input stream.
-  /// \see Json::operator>>(std::istream&, Json::Value&).
-  bool parse(IStream& is, Value& root, bool collectComments = true);
-
-  /** \brief Returns a user friendly string that list errors in the parsed
-   * document.
-   * \return Formatted error message with the list of errors with their location
-   * in
-   *         the parsed document. An empty string is returned if no error
-   * occurred
-   *         during parsing.
-   * \deprecated Use getFormattedErrorMessages() instead (typo fix).
-   */
-  JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.")
-  String getFormatedErrorMessages() const;
-
-  /** \brief Returns a user friendly string that list errors in the parsed
-   * document.
-   * \return Formatted error message with the list of errors with their location
-   * in
-   *         the parsed document. An empty string is returned if no error
-   * occurred
-   *         during parsing.
-   */
-  String getFormattedErrorMessages() const;
-
-  /** \brief Returns a vector of structured erros encounted while parsing.
-   * \return A (possibly empty) vector of StructuredError objects. Currently
-   *         only one error can be returned, but the caller should tolerate
-   * multiple
-   *         errors.  This can occur if the parser recovers from a non-fatal
-   *         parse error and then encounters additional errors.
-   */
-  std::vector<StructuredError> getStructuredErrors() const;
-
-  /** \brief Add a semantic error message.
-   * \param value JSON Value location associated with the error
-   * \param message The error message.
-   * \return \c true if the error was successfully added, \c false if the
-   * Value offset exceeds the document size.
-   */
-  bool pushError(const Value& value, const String& message);
-
-  /** \brief Add a semantic error message with extra context.
-   * \param value JSON Value location associated with the error
-   * \param message The error message.
-   * \param extra Additional JSON Value location to contextualize the error
-   * \return \c true if the error was successfully added, \c false if either
-   * Value offset exceeds the document size.
-   */
-  bool pushError(const Value& value, const String& message, const Value& extra);
-
-  /** \brief Return whether there are any errors.
-   * \return \c true if there are no errors to report \c false if
-   * errors have occurred.
-   */
-  bool good() const;
-
-private:
-  enum TokenType {
-    tokenEndOfStream = 0,
-    tokenObjectBegin,
-    tokenObjectEnd,
-    tokenArrayBegin,
-    tokenArrayEnd,
-    tokenString,
-    tokenNumber,
-    tokenTrue,
-    tokenFalse,
-    tokenNull,
-    tokenArraySeparator,
-    tokenMemberSeparator,
-    tokenComment,
-    tokenError
-  };
-
-  class Token {
-  public:
-    TokenType type_;
-    Location start_;
-    Location end_;
-  };
-
-  class ErrorInfo {
-  public:
-    Token token_;
-    String message_;
-    Location extra_;
-  };
-
-  typedef std::deque<ErrorInfo> Errors;
-
-  bool readToken(Token& token);
-  void skipSpaces();
-  bool match(Location pattern, int patternLength);
-  bool readComment();
-  bool readCStyleComment();
-  bool readCppStyleComment();
-  bool readString();
-  void readNumber();
-  bool readValue();
-  bool readObject(Token& token);
-  bool readArray(Token& token);
-  bool decodeNumber(Token& token);
-  bool decodeNumber(Token& token, Value& decoded);
-  bool decodeString(Token& token);
-  bool decodeString(Token& token, String& decoded);
-  bool decodeDouble(Token& token);
-  bool decodeDouble(Token& token, Value& decoded);
-  bool decodeUnicodeCodePoint(Token& token,
-                              Location& current,
-                              Location end,
-                              unsigned int& unicode);
-  bool decodeUnicodeEscapeSequence(Token& token,
-                                   Location& current,
-                                   Location end,
-                                   unsigned int& unicode);
-  bool addError(const String& message, Token& token, Location extra = nullptr);
-  bool recoverFromError(TokenType skipUntilToken);
-  bool addErrorAndRecover(const String& message,
-                          Token& token,
-                          TokenType skipUntilToken);
-  void skipUntilSpace();
-  Value& currentValue();
-  Char getNextChar();
-  void
-  getLocationLineAndColumn(Location location, int& line, int& column) const;
-  String getLocationLineAndColumn(Location location) const;
-  void addComment(Location begin, Location end, CommentPlacement placement);
-  void skipCommentTokens(Token& token);
-
-  static bool containsNewLine(Location begin, Location end);
-  static String normalizeEOL(Location begin, Location end);
-
-  typedef std::stack<Value*> Nodes;
-  Nodes nodes_;
-  Errors errors_;
-  String document_;
-  Location begin_{};
-  Location end_{};
-  Location current_{};
-  Location lastValueEnd_{};
-  Value* lastValue_{};
-  String commentsBefore_;
-  Features features_;
-  bool collectComments_{};
-}; // Reader
-
-/** Interface for reading JSON from a char array.
- */
-class JSON_API CharReader {
-public:
-  virtual ~CharReader() = default;
-  /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
-   document.
-   * The document must be a UTF-8 encoded string containing the document to
-   read.
-   *
-   * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
-   document to read.
-   * \param endDoc Pointer on the end of the UTF-8 encoded string of the
-   document to read.
-   *        Must be >= beginDoc.
-   * \param root [out] Contains the root value of the document if it was
-   *             successfully parsed.
-   * \param errs [out] Formatted error messages (if not NULL)
-   *        a user friendly string that lists errors in the parsed
-   * document.
-   * \return \c true if the document was successfully parsed, \c false if an
-   error occurred.
-   */
-  virtual bool parse(char const* beginDoc,
-                     char const* endDoc,
-                     Value* root,
-                     String* errs) = 0;
-
-  class JSON_API Factory {
-  public:
-    virtual ~Factory() = default;
-    /** \brief Allocate a CharReader via operator new().
-     * \throw std::exception if something goes wrong (e.g. invalid settings)
-     */
-    virtual CharReader* newCharReader() const = 0;
-  }; // Factory
-};   // CharReader
-
-/** \brief Build a CharReader implementation.
-
-Usage:
-\code
-  using namespace Json;
-  CharReaderBuilder builder;
-  builder["collectComments"] = false;
-  Value value;
-  String errs;
-  bool ok = parseFromStream(builder, std::cin, &value, &errs);
-\endcode
-*/
-class JSON_API CharReaderBuilder : public CharReader::Factory {
-public:
-  // Note: We use a Json::Value so that we can add data-members to this class
-  // without a major version bump.
-  /** Configuration of this builder.
-    These are case-sensitive.
-    Available settings (case-sensitive):
-    - `"collectComments": false or true`
-      - true to collect comment and allow writing them
-        back during serialization, false to discard comments.
-        This parameter is ignored if allowComments is false.
-    - `"allowComments": false or true`
-      - true if comments are allowed.
-    - `"strictRoot": false or true`
-      - true if root must be either an array or an object value
-    - `"allowDroppedNullPlaceholders": false or true`
-      - true if dropped null placeholders are allowed. (See
-    StreamWriterBuilder.)
-    - `"allowNumericKeys": false or true`
-      - true if numeric object keys are allowed.
-    - `"allowSingleQuotes": false or true`
-      - true if '' are allowed for strings (both keys and values)
-    - `"stackLimit": integer`
-      - Exceeding stackLimit (recursive depth of `readValue()`) will
-        cause an exception.
-      - This is a security issue (seg-faults caused by deeply nested JSON),
-        so the default is low.
-    - `"failIfExtra": false or true`
-      - If true, `parse()` returns false when extra non-whitespace trails
-        the JSON value in the input string.
-    - `"rejectDupKeys": false or true`
-      - If true, `parse()` returns false when a key is duplicated within an
-    object.
-    - `"allowSpecialFloats": false or true`
-      - If true, special float values (NaNs and infinities) are allowed
-        and their values are lossfree restorable.
-
-    You can examine 'settings_` yourself
-    to see the defaults. You can also write and read them just like any
-    JSON Value.
-    \sa setDefaults()
-    */
-  Json::Value settings_;
-
-  CharReaderBuilder();
-  ~CharReaderBuilder() override;
-
-  CharReader* newCharReader() const override;
-
-  /** \return true if 'settings' are legal and consistent;
-   *   otherwise, indicate bad settings via 'invalid'.
-   */
-  bool validate(Json::Value* invalid) const;
-
-  /** A simple way to update a specific setting.
-   */
-  Value& operator[](const String& key);
-
-  /** Called by ctor, but you can use this to reset settings_.
-   * \pre 'settings' != NULL (but Json::null is fine)
-   * \remark Defaults:
-   * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults
-   */
-  static void setDefaults(Json::Value* settings);
-  /** Same as old Features::strictMode().
-   * \pre 'settings' != NULL (but Json::null is fine)
-   * \remark Defaults:
-   * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode
-   */
-  static void strictMode(Json::Value* settings);
-};
-
-/** Consume entire stream and use its begin/end.
- * Someday we might have a real StreamReader, but for now this
- * is convenient.
- */
-bool JSON_API parseFromStream(CharReader::Factory const&,
-                              IStream&,
-                              Value* root,
-                              std::string* errs);
-
-/** \brief Read from 'sin' into 'root'.
-
- Always keep comments from the input JSON.
-
- This can be used to read a file into a particular sub-object.
- For example:
- \code
- Json::Value root;
- cin >> root["dir"]["file"];
- cout << root;
- \endcode
- Result:
- \verbatim
- {
- "dir": {
-     "file": {
-     // The input stream JSON would be nested here.
-     }
- }
- }
- \endverbatim
- \throw std::exception on parse error.
- \see Json::operator<<()
-*/
-JSON_API IStream& operator>>(IStream&, Value&);
-
-} // namespace Json
-
-#pragma pack(pop)
-
-#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
-#pragma warning(pop)
-#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
-
-#endif // CPPTL_JSON_READER_H_INCLUDED
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: include/json/reader.h
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: include/json/writer.h
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef JSON_WRITER_H_INCLUDED
-#define JSON_WRITER_H_INCLUDED
-
-#if !defined(JSON_IS_AMALGAMATION)
-#include "value.h"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-#include <ostream>
-#include <string>
-#include <vector>
-
-// Disable warning C4251: <data member>: <type> needs to have dll-interface to
-// be used by...
-#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable : 4251)
-#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
-
-#pragma pack(push, 8)
-
-namespace Json {
-
-class Value;
-
-/**
-
-Usage:
-\code
-  using namespace Json;
-  void writeToStdout(StreamWriter::Factory const& factory, Value const& value) {
-    std::unique_ptr<StreamWriter> const writer(
-      factory.newStreamWriter());
-    writer->write(value, &std::cout);
-    std::cout << std::endl;  // add lf and flush
-  }
-\endcode
-*/
-class JSON_API StreamWriter {
-protected:
-  OStream* sout_; // not owned; will not delete
-public:
-  StreamWriter();
-  virtual ~StreamWriter();
-  /** Write Value into document as configured in sub-class.
-      Do not take ownership of sout, but maintain a reference during function.
-      \pre sout != NULL
-      \return zero on success (For now, we always return zero, so check the
-     stream instead.) \throw std::exception possibly, depending on configuration
-   */
-  virtual int write(Value const& root, OStream* sout) = 0;
-
-  /** \brief A simple abstract factory.
-   */
-  class JSON_API Factory {
-  public:
-    virtual ~Factory();
-    /** \brief Allocate a CharReader via operator new().
-     * \throw std::exception if something goes wrong (e.g. invalid settings)
-     */
-    virtual StreamWriter* newStreamWriter() const = 0;
-  }; // Factory
-};   // StreamWriter
-
-/** \brief Write into stringstream, then return string, for convenience.
- * A StreamWriter will be created from the factory, used, and then deleted.
- */
-String JSON_API writeString(StreamWriter::Factory const& factory,
-                            Value const& root);
-
-/** \brief Build a StreamWriter implementation.
-
-Usage:
-\code
-  using namespace Json;
-  Value value = ...;
-  StreamWriterBuilder builder;
-  builder["commentStyle"] = "None";
-  builder["indentation"] = "   ";  // or whatever you like
-  std::unique_ptr<Json::StreamWriter> writer(
-      builder.newStreamWriter());
-  writer->write(value, &std::cout);
-  std::cout << std::endl;  // add lf and flush
-\endcode
-*/
-class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
-public:
-  // Note: We use a Json::Value so that we can add data-members to this class
-  // without a major version bump.
-  /** Configuration of this builder.
-    Available settings (case-sensitive):
-    - "commentStyle": "None" or "All"
-    - "indentation":  "<anything>".
-      - Setting this to an empty string also omits newline characters.
-    - "enableYAMLCompatibility": false or true
-      - slightly change the whitespace around colons
-    - "dropNullPlaceholders": false or true
-      - Drop the "null" string from the writer's output for nullValues.
-        Strictly speaking, this is not valid JSON. But when the output is being
-        fed to a browser's JavaScript, it makes for smaller output and the
-        browser can handle the output just fine.
-    - "useSpecialFloats": false or true
-      - If true, outputs non-finite floating point values in the following way:
-        NaN values as "NaN", positive infinity as "Infinity", and negative
-    infinity as "-Infinity".
-    - "precision": int
-      - Number of precision digits for formatting of real values.
-    - "precisionType": "significant"(default) or "decimal"
-      - Type of precision for formatting of real values.
-
-    You can examine 'settings_` yourself
-    to see the defaults. You can also write and read them just like any
-    JSON Value.
-    \sa setDefaults()
-    */
-  Json::Value settings_;
-
-  StreamWriterBuilder();
-  ~StreamWriterBuilder() override;
-
-  /**
-   * \throw std::exception if something goes wrong (e.g. invalid settings)
-   */
-  StreamWriter* newStreamWriter() const override;
-
-  /** \return true if 'settings' are legal and consistent;
-   *   otherwise, indicate bad settings via 'invalid'.
-   */
-  bool validate(Json::Value* invalid) const;
-  /** A simple way to update a specific setting.
-   */
-  Value& operator[](const String& key);
-
-  /** Called by ctor, but you can use this to reset settings_.
-   * \pre 'settings' != NULL (but Json::null is fine)
-   * \remark Defaults:
-   * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults
-   */
-  static void setDefaults(Json::Value* settings);
-};
-
-/** \brief Abstract class for writers.
- * \deprecated Use StreamWriter. (And really, this is an implementation detail.)
- */
-class JSONCPP_DEPRECATED("Use StreamWriter instead") JSON_API Writer {
-public:
-  virtual ~Writer();
-
-  virtual String write(const Value& root) = 0;
-};
-
-/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format
- *without formatting (not human friendly).
- *
- * The JSON document is written in a single line. It is not intended for 'human'
- *consumption,
- * but may be useful to support feature such as RPC where bandwidth is limited.
- * \sa Reader, Value
- * \deprecated Use StreamWriterBuilder.
- */
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable : 4996) // Deriving from deprecated class
-#endif
-class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter
-    : public Writer {
-public:
-  FastWriter();
-  ~FastWriter() override = default;
-
-  void enableYAMLCompatibility();
-
-  /** \brief Drop the "null" string from the writer's output for nullValues.
-   * Strictly speaking, this is not valid JSON. But when the output is being
-   * fed to a browser's JavaScript, it makes for smaller output and the
-   * browser can handle the output just fine.
-   */
-  void dropNullPlaceholders();
-
-  void omitEndingLineFeed();
-
-public: // overridden from Writer
-  String write(const Value& root) override;
-
-private:
-  void writeValue(const Value& value);
-
-  String document_;
-  bool yamlCompatibilityEnabled_{false};
-  bool dropNullPlaceholders_{false};
-  bool omitEndingLineFeed_{false};
-};
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
-/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
- *human friendly way.
- *
- * The rules for line break and indent are as follow:
- * - Object value:
- *     - if empty then print {} without indent and line break
- *     - if not empty the print '{', line break & indent, print one value per
- *line
- *       and then unindent and line break and print '}'.
- * - Array value:
- *     - if empty then print [] without indent and line break
- *     - if the array contains no object value, empty array or some other value
- *types,
- *       and all the values fit on one lines, then print the array on a single
- *line.
- *     - otherwise, it the values do not fit on one line, or the array contains
- *       object or non empty array, then print one value per line.
- *
- * If the Value have comments then they are outputed according to their
- *#CommentPlacement.
- *
- * \sa Reader, Value, Value::setComment()
- * \deprecated Use StreamWriterBuilder.
- */
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable : 4996) // Deriving from deprecated class
-#endif
-class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API
-    StyledWriter : public Writer {
-public:
-  StyledWriter();
-  ~StyledWriter() override = default;
-
-public: // overridden from Writer
-  /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
-   * \param root Value to serialize.
-   * \return String containing the JSON document that represents the root value.
-   */
-  String write(const Value& root) override;
-
-private:
-  void writeValue(const Value& value);
-  void writeArrayValue(const Value& value);
-  bool isMultilineArray(const Value& value);
-  void pushValue(const String& value);
-  void writeIndent();
-  void writeWithIndent(const String& value);
-  void indent();
-  void unindent();
-  void writeCommentBeforeValue(const Value& root);
-  void writeCommentAfterValueOnSameLine(const Value& root);
-  static bool hasCommentForValue(const Value& value);
-  static String normalizeEOL(const String& text);
-
-  typedef std::vector<String> ChildValues;
-
-  ChildValues childValues_;
-  String document_;
-  String indentString_;
-  unsigned int rightMargin_{74};
-  unsigned int indentSize_{3};
-  bool addChildValues_{false};
-};
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
-/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
- human friendly way,
-     to a stream rather than to a string.
- *
- * The rules for line break and indent are as follow:
- * - Object value:
- *     - if empty then print {} without indent and line break
- *     - if not empty the print '{', line break & indent, print one value per
- line
- *       and then unindent and line break and print '}'.
- * - Array value:
- *     - if empty then print [] without indent and line break
- *     - if the array contains no object value, empty array or some other value
- types,
- *       and all the values fit on one lines, then print the array on a single
- line.
- *     - otherwise, it the values do not fit on one line, or the array contains
- *       object or non empty array, then print one value per line.
- *
- * If the Value have comments then they are outputed according to their
- #CommentPlacement.
- *
- * \sa Reader, Value, Value::setComment()
- * \deprecated Use StreamWriterBuilder.
- */
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable : 4996) // Deriving from deprecated class
-#endif
-class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API
-    StyledStreamWriter {
-public:
-  /**
-   * \param indentation Each level will be indented by this amount extra.
-   */
-  StyledStreamWriter(String indentation = "\t");
-  ~StyledStreamWriter() = default;
-
-public:
-  /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
-   * \param out Stream to write to. (Can be ostringstream, e.g.)
-   * \param root Value to serialize.
-   * \note There is no point in deriving from Writer, since write() should not
-   * return a value.
-   */
-  void write(OStream& out, const Value& root);
-
-private:
-  void writeValue(const Value& value);
-  void writeArrayValue(const Value& value);
-  bool isMultilineArray(const Value& value);
-  void pushValue(const String& value);
-  void writeIndent();
-  void writeWithIndent(const String& value);
-  void indent();
-  void unindent();
-  void writeCommentBeforeValue(const Value& root);
-  void writeCommentAfterValueOnSameLine(const Value& root);
-  static bool hasCommentForValue(const Value& value);
-  static String normalizeEOL(const String& text);
-
-  typedef std::vector<String> ChildValues;
-
-  ChildValues childValues_;
-  OStream* document_;
-  String indentString_;
-  unsigned int rightMargin_{74};
-  String indentation_;
-  bool addChildValues_ : 1;
-  bool indented_ : 1;
-};
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
-#if defined(JSON_HAS_INT64)
-String JSON_API valueToString(Int value);
-String JSON_API valueToString(UInt value);
-#endif // if defined(JSON_HAS_INT64)
-String JSON_API valueToString(LargestInt value);
-String JSON_API valueToString(LargestUInt value);
-String JSON_API
-valueToString(double value,
-              unsigned int precision = Value::defaultRealPrecision,
-              PrecisionType precisionType = PrecisionType::significantDigits);
-String JSON_API valueToString(bool value);
-String JSON_API valueToQuotedString(const char* value);
-
-/// \brief Output using the StyledStreamWriter.
-/// \see Json::operator>>()
-JSON_API OStream& operator<<(OStream&, const Value& root);
-
-} // namespace Json
-
-#pragma pack(pop)
-
-#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
-#pragma warning(pop)
-#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
-
-#endif // JSON_WRITER_H_INCLUDED
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: include/json/writer.h
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: include/json/assertions.h
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED
-#define CPPTL_JSON_ASSERTIONS_H_INCLUDED
-
-#include <cstdlib>
-#include <sstream>
-
-#if !defined(JSON_IS_AMALGAMATION)
-#include "config.h"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-
-/** It should not be possible for a maliciously designed file to
- *  cause an abort() or seg-fault, so these macros are used only
- *  for pre-condition violations and internal logic errors.
- */
-#if JSON_USE_EXCEPTION
-
-// @todo <= add detail about condition in exception
-#define JSON_ASSERT(condition)                                                 \
-  {                                                                            \
-    if (!(condition)) {                                                        \
-      Json::throwLogicError("assert json failed");                             \
-    }                                                                          \
-  }
-
-#define JSON_FAIL_MESSAGE(message)                                             \
-  {                                                                            \
-    OStringStream oss;                                                         \
-    oss << message;                                                            \
-    Json::throwLogicError(oss.str());                                          \
-    abort();                                                                   \
-  }
-
-#else // JSON_USE_EXCEPTION
-
-#define JSON_ASSERT(condition) assert(condition)
-
-// The call to assert() will show the failure message in debug builds. In
-// release builds we abort, for a core-dump or debugger.
-#define JSON_FAIL_MESSAGE(message)                                             \
-  {                                                                            \
-    OStringStream oss;                                                         \
-    oss << message;                                                            \
-    assert(false && oss.str().c_str());                                        \
-    abort();                                                                   \
-  }
-
-#endif
-
-#define JSON_ASSERT_MESSAGE(condition, message)                                \
-  if (!(condition)) {                                                          \
-    JSON_FAIL_MESSAGE(message);                                                \
-  }
-
-#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: include/json/assertions.h
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-#endif //ifndef JSON_AMALGAMATED_H_INCLUDED
--- a/Resources/CodeGeneration/testWasmIntegrated/jsoncpp-1.8.4/jsoncpp.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5418 +0,0 @@
-/// Json-cpp amalgamated source (http://jsoncpp.sourceforge.net/).
-/// It is intended to be used with #include "json/json.h"
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: LICENSE
-// //////////////////////////////////////////////////////////////////////
-
-/*
-The JsonCpp library's source code, including accompanying documentation, 
-tests and demonstration applications, are licensed under the following
-conditions...
-
-Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all 
-jurisdictions which recognize such a disclaimer. In such jurisdictions, 
-this software is released into the Public Domain.
-
-In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
-2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
-The JsonCpp Authors, and is released under the terms of the MIT License (see below).
-
-In jurisdictions which recognize Public Domain property, the user of this 
-software may choose to accept it either as 1) Public Domain, 2) under the 
-conditions of the MIT License (see below), or 3) under the terms of dual 
-Public Domain/MIT License conditions described here, as they choose.
-
-The MIT License is about as close to Public Domain as a license can get, and is
-described in clear, concise terms at:
-
-   http://en.wikipedia.org/wiki/MIT_License
-   
-The full text of the MIT License follows:
-
-========================================================================
-Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use, copy,
-modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-========================================================================
-(END LICENSE TEXT)
-
-The MIT license is compatible with both the GPL and commercial
-software, affording one all of the rights of Public Domain with the
-minor nuisance of being required to keep the above copyright notice
-and license text in the source code. Note also that by accepting the
-Public Domain "license" you can re-license your copy using whatever
-license you like.
-
-*/
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: LICENSE
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-#include "json/json.h"
-
-#ifndef JSON_IS_AMALGAMATION
-#error "Compile with -I PATH_TO_JSON_DIRECTORY"
-#endif
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: src/lib_json/json_tool.h
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
-#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
-
-#if !defined(JSON_IS_AMALGAMATION)
-#include <json/config.h>
-#endif
-
-// Also support old flag NO_LOCALE_SUPPORT
-#ifdef NO_LOCALE_SUPPORT
-#define JSONCPP_NO_LOCALE_SUPPORT
-#endif
-
-#ifndef JSONCPP_NO_LOCALE_SUPPORT
-#include <clocale>
-#endif
-
-/* This header provides common string manipulation support, such as UTF-8,
- * portable conversion from/to string...
- *
- * It is an internal header that must not be exposed.
- */
-
-namespace Json {
-static inline char getDecimalPoint() {
-#ifdef JSONCPP_NO_LOCALE_SUPPORT
-  return '\0';
-#else
-  struct lconv* lc = localeconv();
-  return lc ? *(lc->decimal_point) : '\0';
-#endif
-}
-
-/// Converts a unicode code-point to UTF-8.
-static inline String codePointToUTF8(unsigned int cp) {
-  String result;
-
-  // based on description from http://en.wikipedia.org/wiki/UTF-8
-
-  if (cp <= 0x7f) {
-    result.resize(1);
-    result[0] = static_cast<char>(cp);
-  } else if (cp <= 0x7FF) {
-    result.resize(2);
-    result[1] = static_cast<char>(0x80 | (0x3f & cp));
-    result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
-  } else if (cp <= 0xFFFF) {
-    result.resize(3);
-    result[2] = static_cast<char>(0x80 | (0x3f & cp));
-    result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
-    result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));
-  } else if (cp <= 0x10FFFF) {
-    result.resize(4);
-    result[3] = static_cast<char>(0x80 | (0x3f & cp));
-    result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
-    result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
-    result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
-  }
-
-  return result;
-}
-
-enum {
-  /// Constant that specify the size of the buffer that must be passed to
-  /// uintToString.
-  uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1
-};
-
-// Defines a char buffer for use with uintToString().
-typedef char UIntToStringBuffer[uintToStringBufferSize];
-
-/** Converts an unsigned integer to string.
- * @param value Unsigned integer to convert to string
- * @param current Input/Output string buffer.
- *        Must have at least uintToStringBufferSize chars free.
- */
-static inline void uintToString(LargestUInt value, char*& current) {
-  *--current = 0;
-  do {
-    *--current = static_cast<char>(value % 10U + static_cast<unsigned>('0'));
-    value /= 10;
-  } while (value != 0);
-}
-
-/** Change ',' to '.' everywhere in buffer.
- *
- * We had a sophisticated way, but it did not work in WinCE.
- * @see https://github.com/open-source-parsers/jsoncpp/pull/9
- */
-template <typename Iter> Iter fixNumericLocale(Iter begin, Iter end) {
-  for (; begin != end; ++begin) {
-    if (*begin == ',') {
-      *begin = '.';
-    }
-  }
-  return begin;
-}
-
-template <typename Iter> void fixNumericLocaleInput(Iter begin, Iter end) {
-  char decimalPoint = getDecimalPoint();
-  if (decimalPoint == '\0' || decimalPoint == '.') {
-    return;
-  }
-  for (; begin != end; ++begin) {
-    if (*begin == '.') {
-      *begin = decimalPoint;
-    }
-  }
-}
-
-/**
- * Return iterator that would be the new end of the range [begin,end), if we
- * were to delete zeros in the end of string, but not the last zero before '.'.
- */
-template <typename Iter> Iter fixZerosInTheEnd(Iter begin, Iter end) {
-  for (; begin != end; --end) {
-    if (*(end - 1) != '0') {
-      return end;
-    }
-    // Don't delete the last zero before the decimal point.
-    if (begin != (end - 1) && *(end - 2) == '.') {
-      return end;
-    }
-  }
-  return end;
-}
-
-} // namespace Json
-
-#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: src/lib_json/json_tool.h
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: src/lib_json/json_reader.cpp
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors
-// Copyright (C) 2016 InfoTeCS JSC. All rights reserved.
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#if !defined(JSON_IS_AMALGAMATION)
-#include "json_tool.h"
-#include <json/assertions.h>
-#include <json/reader.h>
-#include <json/value.h>
-#endif // if !defined(JSON_IS_AMALGAMATION)
-#include <cassert>
-#include <cstring>
-#include <istream>
-#include <limits>
-#include <memory>
-#include <set>
-#include <sstream>
-#include <utility>
-
-#include <cstdio>
-#if __cplusplus >= 201103L
-
-#if !defined(sscanf)
-#define sscanf std::sscanf
-#endif
-
-#endif //__cplusplus
-
-#if defined(_MSC_VER)
-#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
-#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
-#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
-#endif //_MSC_VER
-
-#if defined(_MSC_VER)
-// Disable warning about strdup being deprecated.
-#pragma warning(disable : 4996)
-#endif
-
-// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile
-// time to change the stack limit
-#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
-#define JSONCPP_DEPRECATED_STACK_LIMIT 1000
-#endif
-
-static size_t const stackLimit_g =
-    JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue()
-
-namespace Json {
-
-#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
-typedef std::unique_ptr<CharReader> CharReaderPtr;
-#else
-typedef std::unique_ptr<CharReader> CharReaderPtr;
-#endif
-
-// Implementation of class Features
-// ////////////////////////////////
-
-Features::Features() = default;
-
-Features Features::all() { return {}; }
-
-Features Features::strictMode() {
-  Features features;
-  features.allowComments_ = false;
-  features.strictRoot_ = true;
-  features.allowDroppedNullPlaceholders_ = false;
-  features.allowNumericKeys_ = false;
-  return features;
-}
-
-// Implementation of class Reader
-// ////////////////////////////////
-
-bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
-  for (; begin < end; ++begin)
-    if (*begin == '\n' || *begin == '\r')
-      return true;
-  return false;
-}
-
-// Class Reader
-// //////////////////////////////////////////////////////////////////
-
-Reader::Reader()
-    : errors_(), document_(), commentsBefore_(), features_(Features::all()) {}
-
-Reader::Reader(const Features& features)
-    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
-      lastValue_(), commentsBefore_(), features_(features), collectComments_() {
-}
-
-bool Reader::parse(const std::string& document,
-                   Value& root,
-                   bool collectComments) {
-  document_.assign(document.begin(), document.end());
-  const char* begin = document_.c_str();
-  const char* end = begin + document_.length();
-  return parse(begin, end, root, collectComments);
-}
-
-bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
-  // std::istream_iterator<char> begin(is);
-  // std::istream_iterator<char> end;
-  // Those would allow streamed input from a file, if parse() were a
-  // template function.
-
-  // Since String is reference-counted, this at least does not
-  // create an extra copy.
-  String doc;
-  std::getline(is, doc, (char)EOF);
-  return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
-}
-
-bool Reader::parse(const char* beginDoc,
-                   const char* endDoc,
-                   Value& root,
-                   bool collectComments) {
-  if (!features_.allowComments_) {
-    collectComments = false;
-  }
-
-  begin_ = beginDoc;
-  end_ = endDoc;
-  collectComments_ = collectComments;
-  current_ = begin_;
-  lastValueEnd_ = nullptr;
-  lastValue_ = nullptr;
-  commentsBefore_.clear();
-  errors_.clear();
-  while (!nodes_.empty())
-    nodes_.pop();
-  nodes_.push(&root);
-
-  bool successful = readValue();
-  Token token;
-  skipCommentTokens(token);
-  if (collectComments_ && !commentsBefore_.empty())
-    root.setComment(commentsBefore_, commentAfter);
-  if (features_.strictRoot_) {
-    if (!root.isArray() && !root.isObject()) {
-      // Set error location to start of doc, ideally should be first token found
-      // in doc
-      token.type_ = tokenError;
-      token.start_ = beginDoc;
-      token.end_ = endDoc;
-      addError(
-          "A valid JSON document must be either an array or an object value.",
-          token);
-      return false;
-    }
-  }
-  return successful;
-}
-
-bool Reader::readValue() {
-  // readValue() may call itself only if it calls readObject() or ReadArray().
-  // These methods execute nodes_.push() just before and nodes_.pop)() just
-  // after calling readValue(). parse() executes one nodes_.push(), so > instead
-  // of >=.
-  if (nodes_.size() > stackLimit_g)
-    throwRuntimeError("Exceeded stackLimit in readValue().");
-
-  Token token;
-  skipCommentTokens(token);
-  bool successful = true;
-
-  if (collectComments_ && !commentsBefore_.empty()) {
-    currentValue().setComment(commentsBefore_, commentBefore);
-    commentsBefore_.clear();
-  }
-
-  switch (token.type_) {
-  case tokenObjectBegin:
-    successful = readObject(token);
-    currentValue().setOffsetLimit(current_ - begin_);
-    break;
-  case tokenArrayBegin:
-    successful = readArray(token);
-    currentValue().setOffsetLimit(current_ - begin_);
-    break;
-  case tokenNumber:
-    successful = decodeNumber(token);
-    break;
-  case tokenString:
-    successful = decodeString(token);
-    break;
-  case tokenTrue: {
-    Value v(true);
-    currentValue().swapPayload(v);
-    currentValue().setOffsetStart(token.start_ - begin_);
-    currentValue().setOffsetLimit(token.end_ - begin_);
-  } break;
-  case tokenFalse: {
-    Value v(false);
-    currentValue().swapPayload(v);
-    currentValue().setOffsetStart(token.start_ - begin_);
-    currentValue().setOffsetLimit(token.end_ - begin_);
-  } break;
-  case tokenNull: {
-    Value v;
-    currentValue().swapPayload(v);
-    currentValue().setOffsetStart(token.start_ - begin_);
-    currentValue().setOffsetLimit(token.end_ - begin_);
-  } break;
-  case tokenArraySeparator:
-  case tokenObjectEnd:
-  case tokenArrayEnd:
-    if (features_.allowDroppedNullPlaceholders_) {
-      // "Un-read" the current token and mark the current value as a null
-      // token.
-      current_--;
-      Value v;
-      currentValue().swapPayload(v);
-      currentValue().setOffsetStart(current_ - begin_ - 1);
-      currentValue().setOffsetLimit(current_ - begin_);
-      break;
-    } // Else, fall through...
-  default:
-    currentValue().setOffsetStart(token.start_ - begin_);
-    currentValue().setOffsetLimit(token.end_ - begin_);
-    return addError("Syntax error: value, object or array expected.", token);
-  }
-
-  if (collectComments_) {
-    lastValueEnd_ = current_;
-    lastValue_ = &currentValue();
-  }
-
-  return successful;
-}
-
-void Reader::skipCommentTokens(Token& token) {
-  if (features_.allowComments_) {
-    do {
-      readToken(token);
-    } while (token.type_ == tokenComment);
-  } else {
-    readToken(token);
-  }
-}
-
-bool Reader::readToken(Token& token) {
-  skipSpaces();
-  token.start_ = current_;
-  Char c = getNextChar();
-  bool ok = true;
-  switch (c) {
-  case '{':
-    token.type_ = tokenObjectBegin;
-    break;
-  case '}':
-    token.type_ = tokenObjectEnd;
-    break;
-  case '[':
-    token.type_ = tokenArrayBegin;
-    break;
-  case ']':
-    token.type_ = tokenArrayEnd;
-    break;
-  case '"':
-    token.type_ = tokenString;
-    ok = readString();
-    break;
-  case '/':
-    token.type_ = tokenComment;
-    ok = readComment();
-    break;
-  case '0':
-  case '1':
-  case '2':
-  case '3':
-  case '4':
-  case '5':
-  case '6':
-  case '7':
-  case '8':
-  case '9':
-  case '-':
-    token.type_ = tokenNumber;
-    readNumber();
-    break;
-  case 't':
-    token.type_ = tokenTrue;
-    ok = match("rue", 3);
-    break;
-  case 'f':
-    token.type_ = tokenFalse;
-    ok = match("alse", 4);
-    break;
-  case 'n':
-    token.type_ = tokenNull;
-    ok = match("ull", 3);
-    break;
-  case ',':
-    token.type_ = tokenArraySeparator;
-    break;
-  case ':':
-    token.type_ = tokenMemberSeparator;
-    break;
-  case 0:
-    token.type_ = tokenEndOfStream;
-    break;
-  default:
-    ok = false;
-    break;
-  }
-  if (!ok)
-    token.type_ = tokenError;
-  token.end_ = current_;
-  return true;
-}
-
-void Reader::skipSpaces() {
-  while (current_ != end_) {
-    Char c = *current_;
-    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
-      ++current_;
-    else
-      break;
-  }
-}
-
-bool Reader::match(Location pattern, int patternLength) {
-  if (end_ - current_ < patternLength)
-    return false;
-  int index = patternLength;
-  while (index--)
-    if (current_[index] != pattern[index])
-      return false;
-  current_ += patternLength;
-  return true;
-}
-
-bool Reader::readComment() {
-  Location commentBegin = current_ - 1;
-  Char c = getNextChar();
-  bool successful = false;
-  if (c == '*')
-    successful = readCStyleComment();
-  else if (c == '/')
-    successful = readCppStyleComment();
-  if (!successful)
-    return false;
-
-  if (collectComments_) {
-    CommentPlacement placement = commentBefore;
-    if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
-      if (c != '*' || !containsNewLine(commentBegin, current_))
-        placement = commentAfterOnSameLine;
-    }
-
-    addComment(commentBegin, current_, placement);
-  }
-  return true;
-}
-
-String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) {
-  String normalized;
-  normalized.reserve(static_cast<size_t>(end - begin));
-  Reader::Location current = begin;
-  while (current != end) {
-    char c = *current++;
-    if (c == '\r') {
-      if (current != end && *current == '\n')
-        // convert dos EOL
-        ++current;
-      // convert Mac EOL
-      normalized += '\n';
-    } else {
-      normalized += c;
-    }
-  }
-  return normalized;
-}
-
-void Reader::addComment(Location begin,
-                        Location end,
-                        CommentPlacement placement) {
-  assert(collectComments_);
-  const String& normalized = normalizeEOL(begin, end);
-  if (placement == commentAfterOnSameLine) {
-    assert(lastValue_ != nullptr);
-    lastValue_->setComment(normalized, placement);
-  } else {
-    commentsBefore_ += normalized;
-  }
-}
-
-bool Reader::readCStyleComment() {
-  while ((current_ + 1) < end_) {
-    Char c = getNextChar();
-    if (c == '*' && *current_ == '/')
-      break;
-  }
-  return getNextChar() == '/';
-}
-
-bool Reader::readCppStyleComment() {
-  while (current_ != end_) {
-    Char c = getNextChar();
-    if (c == '\n')
-      break;
-    if (c == '\r') {
-      // Consume DOS EOL. It will be normalized in addComment.
-      if (current_ != end_ && *current_ == '\n')
-        getNextChar();
-      // Break on Moc OS 9 EOL.
-      break;
-    }
-  }
-  return true;
-}
-
-void Reader::readNumber() {
-  const char* p = current_;
-  char c = '0'; // stopgap for already consumed character
-  // integral part
-  while (c >= '0' && c <= '9')
-    c = (current_ = p) < end_ ? *p++ : '\0';
-  // fractional part
-  if (c == '.') {
-    c = (current_ = p) < end_ ? *p++ : '\0';
-    while (c >= '0' && c <= '9')
-      c = (current_ = p) < end_ ? *p++ : '\0';
-  }
-  // exponential part
-  if (c == 'e' || c == 'E') {
-    c = (current_ = p) < end_ ? *p++ : '\0';
-    if (c == '+' || c == '-')
-      c = (current_ = p) < end_ ? *p++ : '\0';
-    while (c >= '0' && c <= '9')
-      c = (current_ = p) < end_ ? *p++ : '\0';
-  }
-}
-
-bool Reader::readString() {
-  Char c = '\0';
-  while (current_ != end_) {
-    c = getNextChar();
-    if (c == '\\')
-      getNextChar();
-    else if (c == '"')
-      break;
-  }
-  return c == '"';
-}
-
-bool Reader::readObject(Token& token) {
-  Token tokenName;
-  String name;
-  Value init(objectValue);
-  currentValue().swapPayload(init);
-  currentValue().setOffsetStart(token.start_ - begin_);
-  while (readToken(tokenName)) {
-    bool initialTokenOk = true;
-    while (tokenName.type_ == tokenComment && initialTokenOk)
-      initialTokenOk = readToken(tokenName);
-    if (!initialTokenOk)
-      break;
-    if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
-      return true;
-    name.clear();
-    if (tokenName.type_ == tokenString) {
-      if (!decodeString(tokenName, name))
-        return recoverFromError(tokenObjectEnd);
-    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
-      Value numberName;
-      if (!decodeNumber(tokenName, numberName))
-        return recoverFromError(tokenObjectEnd);
-      name = String(numberName.asCString());
-    } else {
-      break;
-    }
-
-    Token colon;
-    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
-      return addErrorAndRecover("Missing ':' after object member name", colon,
-                                tokenObjectEnd);
-    }
-    Value& value = currentValue()[name];
-    nodes_.push(&value);
-    bool ok = readValue();
-    nodes_.pop();
-    if (!ok) // error already set
-      return recoverFromError(tokenObjectEnd);
-
-    Token comma;
-    if (!readToken(comma) ||
-        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
-         comma.type_ != tokenComment)) {
-      return addErrorAndRecover("Missing ',' or '}' in object declaration",
-                                comma, tokenObjectEnd);
-    }
-    bool finalizeTokenOk = true;
-    while (comma.type_ == tokenComment && finalizeTokenOk)
-      finalizeTokenOk = readToken(comma);
-    if (comma.type_ == tokenObjectEnd)
-      return true;
-  }
-  return addErrorAndRecover("Missing '}' or object member name", tokenName,
-                            tokenObjectEnd);
-}
-
-bool Reader::readArray(Token& token) {
-  Value init(arrayValue);
-  currentValue().swapPayload(init);
-  currentValue().setOffsetStart(token.start_ - begin_);
-  skipSpaces();
-  if (current_ != end_ && *current_ == ']') // empty array
-  {
-    Token endArray;
-    readToken(endArray);
-    return true;
-  }
-  int index = 0;
-  for (;;) {
-    Value& value = currentValue()[index++];
-    nodes_.push(&value);
-    bool ok = readValue();
-    nodes_.pop();
-    if (!ok) // error already set
-      return recoverFromError(tokenArrayEnd);
-
-    Token currentToken;
-    // Accept Comment after last item in the array.
-    ok = readToken(currentToken);
-    while (currentToken.type_ == tokenComment && ok) {
-      ok = readToken(currentToken);
-    }
-    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
-                         currentToken.type_ != tokenArrayEnd);
-    if (!ok || badTokenType) {
-      return addErrorAndRecover("Missing ',' or ']' in array declaration",
-                                currentToken, tokenArrayEnd);
-    }
-    if (currentToken.type_ == tokenArrayEnd)
-      break;
-  }
-  return true;
-}
-
-bool Reader::decodeNumber(Token& token) {
-  Value decoded;
-  if (!decodeNumber(token, decoded))
-    return false;
-  currentValue().swapPayload(decoded);
-  currentValue().setOffsetStart(token.start_ - begin_);
-  currentValue().setOffsetLimit(token.end_ - begin_);
-  return true;
-}
-
-bool Reader::decodeNumber(Token& token, Value& decoded) {
-  // Attempts to parse the number as an integer. If the number is
-  // larger than the maximum supported value of an integer then
-  // we decode the number as a double.
-  Location current = token.start_;
-  bool isNegative = *current == '-';
-  if (isNegative)
-    ++current;
-  // TODO: Help the compiler do the div and mod at compile time or get rid of
-  // them.
-  Value::LargestUInt maxIntegerValue =
-      isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1
-                 : Value::maxLargestUInt;
-  Value::LargestUInt threshold = maxIntegerValue / 10;
-  Value::LargestUInt value = 0;
-  while (current < token.end_) {
-    Char c = *current++;
-    if (c < '0' || c > '9')
-      return decodeDouble(token, decoded);
-    auto digit(static_cast<Value::UInt>(c - '0'));
-    if (value >= threshold) {
-      // We've hit or exceeded the max value divided by 10 (rounded down). If
-      // a) we've only just touched the limit, b) this is the last digit, and
-      // c) it's small enough to fit in that rounding delta, we're okay.
-      // Otherwise treat this number as a double to avoid overflow.
-      if (value > threshold || current != token.end_ ||
-          digit > maxIntegerValue % 10) {
-        return decodeDouble(token, decoded);
-      }
-    }
-    value = value * 10 + digit;
-  }
-  if (isNegative && value == maxIntegerValue)
-    decoded = Value::minLargestInt;
-  else if (isNegative)
-    decoded = -Value::LargestInt(value);
-  else if (value <= Value::LargestUInt(Value::maxInt))
-    decoded = Value::LargestInt(value);
-  else
-    decoded = value;
-  return true;
-}
-
-bool Reader::decodeDouble(Token& token) {
-  Value decoded;
-  if (!decodeDouble(token, decoded))
-    return false;
-  currentValue().swapPayload(decoded);
-  currentValue().setOffsetStart(token.start_ - begin_);
-  currentValue().setOffsetLimit(token.end_ - begin_);
-  return true;
-}
-
-bool Reader::decodeDouble(Token& token, Value& decoded) {
-  double value = 0;
-  String buffer(token.start_, token.end_);
-  IStringStream is(buffer);
-  if (!(is >> value))
-    return addError(
-        "'" + String(token.start_, token.end_) + "' is not a number.", token);
-  decoded = value;
-  return true;
-}
-
-bool Reader::decodeString(Token& token) {
-  String decoded_string;
-  if (!decodeString(token, decoded_string))
-    return false;
-  Value decoded(decoded_string);
-  currentValue().swapPayload(decoded);
-  currentValue().setOffsetStart(token.start_ - begin_);
-  currentValue().setOffsetLimit(token.end_ - begin_);
-  return true;
-}
-
-bool Reader::decodeString(Token& token, String& decoded) {
-  decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
-  Location current = token.start_ + 1; // skip '"'
-  Location end = token.end_ - 1;       // do not include '"'
-  while (current != end) {
-    Char c = *current++;
-    if (c == '"')
-      break;
-    else if (c == '\\') {
-      if (current == end)
-        return addError("Empty escape sequence in string", token, current);
-      Char escape = *current++;
-      switch (escape) {
-      case '"':
-        decoded += '"';
-        break;
-      case '/':
-        decoded += '/';
-        break;
-      case '\\':
-        decoded += '\\';
-        break;
-      case 'b':
-        decoded += '\b';
-        break;
-      case 'f':
-        decoded += '\f';
-        break;
-      case 'n':
-        decoded += '\n';
-        break;
-      case 'r':
-        decoded += '\r';
-        break;
-      case 't':
-        decoded += '\t';
-        break;
-      case 'u': {
-        unsigned int unicode;
-        if (!decodeUnicodeCodePoint(token, current, end, unicode))
-          return false;
-        decoded += codePointToUTF8(unicode);
-      } break;
-      default:
-        return addError("Bad escape sequence in string", token, current);
-      }
-    } else {
-      decoded += c;
-    }
-  }
-  return true;
-}
-
-bool Reader::decodeUnicodeCodePoint(Token& token,
-                                    Location& current,
-                                    Location end,
-                                    unsigned int& unicode) {
-
-  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
-    return false;
-  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
-    // surrogate pairs
-    if (end - current < 6)
-      return addError(
-          "additional six characters expected to parse unicode surrogate pair.",
-          token, current);
-    if (*(current++) == '\\' && *(current++) == 'u') {
-      unsigned int surrogatePair;
-      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
-        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
-      } else
-        return false;
-    } else
-      return addError("expecting another \\u token to begin the second half of "
-                      "a unicode surrogate pair",
-                      token, current);
-  }
-  return true;
-}
-
-bool Reader::decodeUnicodeEscapeSequence(Token& token,
-                                         Location& current,
-                                         Location end,
-                                         unsigned int& ret_unicode) {
-  if (end - current < 4)
-    return addError(
-        "Bad unicode escape sequence in string: four digits expected.", token,
-        current);
-  int unicode = 0;
-  for (int index = 0; index < 4; ++index) {
-    Char c = *current++;
-    unicode *= 16;
-    if (c >= '0' && c <= '9')
-      unicode += c - '0';
-    else if (c >= 'a' && c <= 'f')
-      unicode += c - 'a' + 10;
-    else if (c >= 'A' && c <= 'F')
-      unicode += c - 'A' + 10;
-    else
-      return addError(
-          "Bad unicode escape sequence in string: hexadecimal digit expected.",
-          token, current);
-  }
-  ret_unicode = static_cast<unsigned int>(unicode);
-  return true;
-}
-
-bool Reader::addError(const String& message, Token& token, Location extra) {
-  ErrorInfo info;
-  info.token_ = token;
-  info.message_ = message;
-  info.extra_ = extra;
-  errors_.push_back(info);
-  return false;
-}
-
-bool Reader::recoverFromError(TokenType skipUntilToken) {
-  size_t const errorCount = errors_.size();
-  Token skip;
-  for (;;) {
-    if (!readToken(skip))
-      errors_.resize(errorCount); // discard errors caused by recovery
-    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
-      break;
-  }
-  errors_.resize(errorCount);
-  return false;
-}
-
-bool Reader::addErrorAndRecover(const String& message,
-                                Token& token,
-                                TokenType skipUntilToken) {
-  addError(message, token);
-  return recoverFromError(skipUntilToken);
-}
-
-Value& Reader::currentValue() { return *(nodes_.top()); }
-
-Reader::Char Reader::getNextChar() {
-  if (current_ == end_)
-    return 0;
-  return *current_++;
-}
-
-void Reader::getLocationLineAndColumn(Location location,
-                                      int& line,
-                                      int& column) const {
-  Location current = begin_;
-  Location lastLineStart = current;
-  line = 0;
-  while (current < location && current != end_) {
-    Char c = *current++;
-    if (c == '\r') {
-      if (*current == '\n')
-        ++current;
-      lastLineStart = current;
-      ++line;
-    } else if (c == '\n') {
-      lastLineStart = current;
-      ++line;
-    }
-  }
-  // column & line start at 1
-  column = int(location - lastLineStart) + 1;
-  ++line;
-}
-
-String Reader::getLocationLineAndColumn(Location location) const {
-  int line, column;
-  getLocationLineAndColumn(location, line, column);
-  char buffer[18 + 16 + 16 + 1];
-  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
-  return buffer;
-}
-
-// Deprecated. Preserved for backward compatibility
-String Reader::getFormatedErrorMessages() const {
-  return getFormattedErrorMessages();
-}
-
-String Reader::getFormattedErrorMessages() const {
-  String formattedMessage;
-  for (const auto& error : errors_) {
-    formattedMessage +=
-        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
-    formattedMessage += "  " + error.message_ + "\n";
-    if (error.extra_)
-      formattedMessage +=
-          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
-  }
-  return formattedMessage;
-}
-
-std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
-  std::vector<Reader::StructuredError> allErrors;
-  for (const auto& error : errors_) {
-    Reader::StructuredError structured;
-    structured.offset_start = error.token_.start_ - begin_;
-    structured.offset_limit = error.token_.end_ - begin_;
-    structured.message = error.message_;
-    allErrors.push_back(structured);
-  }
-  return allErrors;
-}
-
-bool Reader::pushError(const Value& value, const String& message) {
-  ptrdiff_t const length = end_ - begin_;
-  if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
-    return false;
-  Token token;
-  token.type_ = tokenError;
-  token.start_ = begin_ + value.getOffsetStart();
-  token.end_ = end_ + value.getOffsetLimit();
-  ErrorInfo info;
-  info.token_ = token;
-  info.message_ = message;
-  info.extra_ = nullptr;
-  errors_.push_back(info);
-  return true;
-}
-
-bool Reader::pushError(const Value& value,
-                       const String& message,
-                       const Value& extra) {
-  ptrdiff_t const length = end_ - begin_;
-  if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
-      extra.getOffsetLimit() > length)
-    return false;
-  Token token;
-  token.type_ = tokenError;
-  token.start_ = begin_ + value.getOffsetStart();
-  token.end_ = begin_ + value.getOffsetLimit();
-  ErrorInfo info;
-  info.token_ = token;
-  info.message_ = message;
-  info.extra_ = begin_ + extra.getOffsetStart();
-  errors_.push_back(info);
-  return true;
-}
-
-bool Reader::good() const { return errors_.empty(); }
-
-// exact copy of Features
-class OurFeatures {
-public:
-  static OurFeatures all();
-  bool allowComments_;
-  bool strictRoot_;
-  bool allowDroppedNullPlaceholders_;
-  bool allowNumericKeys_;
-  bool allowSingleQuotes_;
-  bool failIfExtra_;
-  bool rejectDupKeys_;
-  bool allowSpecialFloats_;
-  size_t stackLimit_;
-}; // OurFeatures
-
-// exact copy of Implementation of class Features
-// ////////////////////////////////
-
-OurFeatures OurFeatures::all() { return {}; }
-
-// Implementation of class Reader
-// ////////////////////////////////
-
-// exact copy of Reader, renamed to OurReader
-class OurReader {
-public:
-  typedef char Char;
-  typedef const Char* Location;
-  struct StructuredError {
-    ptrdiff_t offset_start;
-    ptrdiff_t offset_limit;
-    String message;
-  };
-
-  OurReader(OurFeatures const& features);
-  bool parse(const char* beginDoc,
-             const char* endDoc,
-             Value& root,
-             bool collectComments = true);
-  String getFormattedErrorMessages() const;
-  std::vector<StructuredError> getStructuredErrors() const;
-  bool pushError(const Value& value, const String& message);
-  bool pushError(const Value& value, const String& message, const Value& extra);
-  bool good() const;
-
-private:
-  OurReader(OurReader const&);      // no impl
-  void operator=(OurReader const&); // no impl
-
-  enum TokenType {
-    tokenEndOfStream = 0,
-    tokenObjectBegin,
-    tokenObjectEnd,
-    tokenArrayBegin,
-    tokenArrayEnd,
-    tokenString,
-    tokenNumber,
-    tokenTrue,
-    tokenFalse,
-    tokenNull,
-    tokenNaN,
-    tokenPosInf,
-    tokenNegInf,
-    tokenArraySeparator,
-    tokenMemberSeparator,
-    tokenComment,
-    tokenError
-  };
-
-  class Token {
-  public:
-    TokenType type_;
-    Location start_;
-    Location end_;
-  };
-
-  class ErrorInfo {
-  public:
-    Token token_;
-    String message_;
-    Location extra_;
-  };
-
-  typedef std::deque<ErrorInfo> Errors;
-
-  bool readToken(Token& token);
-  void skipSpaces();
-  bool match(Location pattern, int patternLength);
-  bool readComment();
-  bool readCStyleComment();
-  bool readCppStyleComment();
-  bool readString();
-  bool readStringSingleQuote();
-  bool readNumber(bool checkInf);
-  bool readValue();
-  bool readObject(Token& token);
-  bool readArray(Token& token);
-  bool decodeNumber(Token& token);
-  bool decodeNumber(Token& token, Value& decoded);
-  bool decodeString(Token& token);
-  bool decodeString(Token& token, String& decoded);
-  bool decodeDouble(Token& token);
-  bool decodeDouble(Token& token, Value& decoded);
-  bool decodeUnicodeCodePoint(Token& token,
-                              Location& current,
-                              Location end,
-                              unsigned int& unicode);
-  bool decodeUnicodeEscapeSequence(Token& token,
-                                   Location& current,
-                                   Location end,
-                                   unsigned int& unicode);
-  bool addError(const String& message, Token& token, Location extra = nullptr);
-  bool recoverFromError(TokenType skipUntilToken);
-  bool addErrorAndRecover(const String& message,
-                          Token& token,
-                          TokenType skipUntilToken);
-  void skipUntilSpace();
-  Value& currentValue();
-  Char getNextChar();
-  void
-  getLocationLineAndColumn(Location location, int& line, int& column) const;
-  String getLocationLineAndColumn(Location location) const;
-  void addComment(Location begin, Location end, CommentPlacement placement);
-  void skipCommentTokens(Token& token);
-
-  static String normalizeEOL(Location begin, Location end);
-  static bool containsNewLine(Location begin, Location end);
-
-  typedef std::stack<Value*> Nodes;
-  Nodes nodes_;
-  Errors errors_;
-  String document_;
-  Location begin_;
-  Location end_;
-  Location current_;
-  Location lastValueEnd_;
-  Value* lastValue_;
-  String commentsBefore_;
-
-  OurFeatures const features_;
-  bool collectComments_;
-}; // OurReader
-
-// complete copy of Read impl, for OurReader
-
-bool OurReader::containsNewLine(OurReader::Location begin,
-                                OurReader::Location end) {
-  for (; begin < end; ++begin)
-    if (*begin == '\n' || *begin == '\r')
-      return true;
-  return false;
-}
-
-OurReader::OurReader(OurFeatures const& features)
-    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
-      lastValue_(), commentsBefore_(), features_(features), collectComments_() {
-}
-
-bool OurReader::parse(const char* beginDoc,
-                      const char* endDoc,
-                      Value& root,
-                      bool collectComments) {
-  if (!features_.allowComments_) {
-    collectComments = false;
-  }
-
-  begin_ = beginDoc;
-  end_ = endDoc;
-  collectComments_ = collectComments;
-  current_ = begin_;
-  lastValueEnd_ = nullptr;
-  lastValue_ = nullptr;
-  commentsBefore_.clear();
-  errors_.clear();
-  while (!nodes_.empty())
-    nodes_.pop();
-  nodes_.push(&root);
-
-  bool successful = readValue();
-  Token token;
-  skipCommentTokens(token);
-  if (features_.failIfExtra_) {
-    if ((features_.strictRoot_ || token.type_ != tokenError) &&
-        token.type_ != tokenEndOfStream) {
-      addError("Extra non-whitespace after JSON value.", token);
-      return false;
-    }
-  }
-  if (collectComments_ && !commentsBefore_.empty())
-    root.setComment(commentsBefore_, commentAfter);
-  if (features_.strictRoot_) {
-    if (!root.isArray() && !root.isObject()) {
-      // Set error location to start of doc, ideally should be first token found
-      // in doc
-      token.type_ = tokenError;
-      token.start_ = beginDoc;
-      token.end_ = endDoc;
-      addError(
-          "A valid JSON document must be either an array or an object value.",
-          token);
-      return false;
-    }
-  }
-  return successful;
-}
-
-bool OurReader::readValue() {
-  //  To preserve the old behaviour we cast size_t to int.
-  if (nodes_.size() > features_.stackLimit_)
-    throwRuntimeError("Exceeded stackLimit in readValue().");
-  Token token;
-  skipCommentTokens(token);
-  bool successful = true;
-
-  if (collectComments_ && !commentsBefore_.empty()) {
-    currentValue().setComment(commentsBefore_, commentBefore);
-    commentsBefore_.clear();
-  }
-
-  switch (token.type_) {
-  case tokenObjectBegin:
-    successful = readObject(token);
-    currentValue().setOffsetLimit(current_ - begin_);
-    break;
-  case tokenArrayBegin:
-    successful = readArray(token);
-    currentValue().setOffsetLimit(current_ - begin_);
-    break;
-  case tokenNumber:
-    successful = decodeNumber(token);
-    break;
-  case tokenString:
-    successful = decodeString(token);
-    break;
-  case tokenTrue: {
-    Value v(true);
-    currentValue().swapPayload(v);
-    currentValue().setOffsetStart(token.start_ - begin_);
-    currentValue().setOffsetLimit(token.end_ - begin_);
-  } break;
-  case tokenFalse: {
-    Value v(false);
-    currentValue().swapPayload(v);
-    currentValue().setOffsetStart(token.start_ - begin_);
-    currentValue().setOffsetLimit(token.end_ - begin_);
-  } break;
-  case tokenNull: {
-    Value v;
-    currentValue().swapPayload(v);
-    currentValue().setOffsetStart(token.start_ - begin_);
-    currentValue().setOffsetLimit(token.end_ - begin_);
-  } break;
-  case tokenNaN: {
-    Value v(std::numeric_limits<double>::quiet_NaN());
-    currentValue().swapPayload(v);
-    currentValue().setOffsetStart(token.start_ - begin_);
-    currentValue().setOffsetLimit(token.end_ - begin_);
-  } break;
-  case tokenPosInf: {
-    Value v(std::numeric_limits<double>::infinity());
-    currentValue().swapPayload(v);
-    currentValue().setOffsetStart(token.start_ - begin_);
-    currentValue().setOffsetLimit(token.end_ - begin_);
-  } break;
-  case tokenNegInf: {
-    Value v(-std::numeric_limits<double>::infinity());
-    currentValue().swapPayload(v);
-    currentValue().setOffsetStart(token.start_ - begin_);
-    currentValue().setOffsetLimit(token.end_ - begin_);
-  } break;
-  case tokenArraySeparator:
-  case tokenObjectEnd:
-  case tokenArrayEnd:
-    if (features_.allowDroppedNullPlaceholders_) {
-      // "Un-read" the current token and mark the current value as a null
-      // token.
-      current_--;
-      Value v;
-      currentValue().swapPayload(v);
-      currentValue().setOffsetStart(current_ - begin_ - 1);
-      currentValue().setOffsetLimit(current_ - begin_);
-      break;
-    } // else, fall through ...
-  default:
-    currentValue().setOffsetStart(token.start_ - begin_);
-    currentValue().setOffsetLimit(token.end_ - begin_);
-    return addError("Syntax error: value, object or array expected.", token);
-  }
-
-  if (collectComments_) {
-    lastValueEnd_ = current_;
-    lastValue_ = &currentValue();
-  }
-
-  return successful;
-}
-
-void OurReader::skipCommentTokens(Token& token) {
-  if (features_.allowComments_) {
-    do {
-      readToken(token);
-    } while (token.type_ == tokenComment);
-  } else {
-    readToken(token);
-  }
-}
-
-bool OurReader::readToken(Token& token) {
-  skipSpaces();
-  token.start_ = current_;
-  Char c = getNextChar();
-  bool ok = true;
-  switch (c) {
-  case '{':
-    token.type_ = tokenObjectBegin;
-    break;
-  case '}':
-    token.type_ = tokenObjectEnd;
-    break;
-  case '[':
-    token.type_ = tokenArrayBegin;
-    break;
-  case ']':
-    token.type_ = tokenArrayEnd;
-    break;
-  case '"':
-    token.type_ = tokenString;
-    ok = readString();
-    break;
-  case '\'':
-    if (features_.allowSingleQuotes_) {
-      token.type_ = tokenString;
-      ok = readStringSingleQuote();
-      break;
-    } // else fall through
-  case '/':
-    token.type_ = tokenComment;
-    ok = readComment();
-    break;
-  case '0':
-  case '1':
-  case '2':
-  case '3':
-  case '4':
-  case '5':
-  case '6':
-  case '7':
-  case '8':
-  case '9':
-    token.type_ = tokenNumber;
-    readNumber(false);
-    break;
-  case '-':
-    if (readNumber(true)) {
-      token.type_ = tokenNumber;
-    } else {
-      token.type_ = tokenNegInf;
-      ok = features_.allowSpecialFloats_ && match("nfinity", 7);
-    }
-    break;
-  case 't':
-    token.type_ = tokenTrue;
-    ok = match("rue", 3);
-    break;
-  case 'f':
-    token.type_ = tokenFalse;
-    ok = match("alse", 4);
-    break;
-  case 'n':
-    token.type_ = tokenNull;
-    ok = match("ull", 3);
-    break;
-  case 'N':
-    if (features_.allowSpecialFloats_) {
-      token.type_ = tokenNaN;
-      ok = match("aN", 2);
-    } else {
-      ok = false;
-    }
-    break;
-  case 'I':
-    if (features_.allowSpecialFloats_) {
-      token.type_ = tokenPosInf;
-      ok = match("nfinity", 7);
-    } else {
-      ok = false;
-    }
-    break;
-  case ',':
-    token.type_ = tokenArraySeparator;
-    break;
-  case ':':
-    token.type_ = tokenMemberSeparator;
-    break;
-  case 0:
-    token.type_ = tokenEndOfStream;
-    break;
-  default:
-    ok = false;
-    break;
-  }
-  if (!ok)
-    token.type_ = tokenError;
-  token.end_ = current_;
-  return true;
-}
-
-void OurReader::skipSpaces() {
-  while (current_ != end_) {
-    Char c = *current_;
-    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
-      ++current_;
-    else
-      break;
-  }
-}
-
-bool OurReader::match(Location pattern, int patternLength) {
-  if (end_ - current_ < patternLength)
-    return false;
-  int index = patternLength;
-  while (index--)
-    if (current_[index] != pattern[index])
-      return false;
-  current_ += patternLength;
-  return true;
-}
-
-bool OurReader::readComment() {
-  Location commentBegin = current_ - 1;
-  Char c = getNextChar();
-  bool successful = false;
-  if (c == '*')
-    successful = readCStyleComment();
-  else if (c == '/')
-    successful = readCppStyleComment();
-  if (!successful)
-    return false;
-
-  if (collectComments_) {
-    CommentPlacement placement = commentBefore;
-    if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
-      if (c != '*' || !containsNewLine(commentBegin, current_))
-        placement = commentAfterOnSameLine;
-    }
-
-    addComment(commentBegin, current_, placement);
-  }
-  return true;
-}
-
-String OurReader::normalizeEOL(OurReader::Location begin,
-                               OurReader::Location end) {
-  String normalized;
-  normalized.reserve(static_cast<size_t>(end - begin));
-  OurReader::Location current = begin;
-  while (current != end) {
-    char c = *current++;
-    if (c == '\r') {
-      if (current != end && *current == '\n')
-        // convert dos EOL
-        ++current;
-      // convert Mac EOL
-      normalized += '\n';
-    } else {
-      normalized += c;
-    }
-  }
-  return normalized;
-}
-
-void OurReader::addComment(Location begin,
-                           Location end,
-                           CommentPlacement placement) {
-  assert(collectComments_);
-  const String& normalized = normalizeEOL(begin, end);
-  if (placement == commentAfterOnSameLine) {
-    assert(lastValue_ != nullptr);
-    lastValue_->setComment(normalized, placement);
-  } else {
-    commentsBefore_ += normalized;
-  }
-}
-
-bool OurReader::readCStyleComment() {
-  while ((current_ + 1) < end_) {
-    Char c = getNextChar();
-    if (c == '*' && *current_ == '/')
-      break;
-  }
-  return getNextChar() == '/';
-}
-
-bool OurReader::readCppStyleComment() {
-  while (current_ != end_) {
-    Char c = getNextChar();
-    if (c == '\n')
-      break;
-    if (c == '\r') {
-      // Consume DOS EOL. It will be normalized in addComment.
-      if (current_ != end_ && *current_ == '\n')
-        getNextChar();
-      // Break on Moc OS 9 EOL.
-      break;
-    }
-  }
-  return true;
-}
-
-bool OurReader::readNumber(bool checkInf) {
-  const char* p = current_;
-  if (checkInf && p != end_ && *p == 'I') {
-    current_ = ++p;
-    return false;
-  }
-  char c = '0'; // stopgap for already consumed character
-  // integral part
-  while (c >= '0' && c <= '9')
-    c = (current_ = p) < end_ ? *p++ : '\0';
-  // fractional part
-  if (c == '.') {
-    c = (current_ = p) < end_ ? *p++ : '\0';
-    while (c >= '0' && c <= '9')
-      c = (current_ = p) < end_ ? *p++ : '\0';
-  }
-  // exponential part
-  if (c == 'e' || c == 'E') {
-    c = (current_ = p) < end_ ? *p++ : '\0';
-    if (c == '+' || c == '-')
-      c = (current_ = p) < end_ ? *p++ : '\0';
-    while (c >= '0' && c <= '9')
-      c = (current_ = p) < end_ ? *p++ : '\0';
-  }
-  return true;
-}
-bool OurReader::readString() {
-  Char c = 0;
-  while (current_ != end_) {
-    c = getNextChar();
-    if (c == '\\')
-      getNextChar();
-    else if (c == '"')
-      break;
-  }
-  return c == '"';
-}
-
-bool OurReader::readStringSingleQuote() {
-  Char c = 0;
-  while (current_ != end_) {
-    c = getNextChar();
-    if (c == '\\')
-      getNextChar();
-    else if (c == '\'')
-      break;
-  }
-  return c == '\'';
-}
-
-bool OurReader::readObject(Token& token) {
-  Token tokenName;
-  String name;
-  Value init(objectValue);
-  currentValue().swapPayload(init);
-  currentValue().setOffsetStart(token.start_ - begin_);
-  while (readToken(tokenName)) {
-    bool initialTokenOk = true;
-    while (tokenName.type_ == tokenComment && initialTokenOk)
-      initialTokenOk = readToken(tokenName);
-    if (!initialTokenOk)
-      break;
-    if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
-      return true;
-    name.clear();
-    if (tokenName.type_ == tokenString) {
-      if (!decodeString(tokenName, name))
-        return recoverFromError(tokenObjectEnd);
-    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
-      Value numberName;
-      if (!decodeNumber(tokenName, numberName))
-        return recoverFromError(tokenObjectEnd);
-      name = numberName.asString();
-    } else {
-      break;
-    }
-
-    Token colon;
-    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
-      return addErrorAndRecover("Missing ':' after object member name", colon,
-                                tokenObjectEnd);
-    }
-    if (name.length() >= (1U << 30))
-      throwRuntimeError("keylength >= 2^30");
-    if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
-      String msg = "Duplicate key: '" + name + "'";
-      return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
-    }
-    Value& value = currentValue()[name];
-    nodes_.push(&value);
-    bool ok = readValue();
-    nodes_.pop();
-    if (!ok) // error already set
-      return recoverFromError(tokenObjectEnd);
-
-    Token comma;
-    if (!readToken(comma) ||
-        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
-         comma.type_ != tokenComment)) {
-      return addErrorAndRecover("Missing ',' or '}' in object declaration",
-                                comma, tokenObjectEnd);
-    }
-    bool finalizeTokenOk = true;
-    while (comma.type_ == tokenComment && finalizeTokenOk)
-      finalizeTokenOk = readToken(comma);
-    if (comma.type_ == tokenObjectEnd)
-      return true;
-  }
-  return addErrorAndRecover("Missing '}' or object member name", tokenName,
-                            tokenObjectEnd);
-}
-
-bool OurReader::readArray(Token& token) {
-  Value init(arrayValue);
-  currentValue().swapPayload(init);
-  currentValue().setOffsetStart(token.start_ - begin_);
-  skipSpaces();
-  if (current_ != end_ && *current_ == ']') // empty array
-  {
-    Token endArray;
-    readToken(endArray);
-    return true;
-  }
-  int index = 0;
-  for (;;) {
-    Value& value = currentValue()[index++];
-    nodes_.push(&value);
-    bool ok = readValue();
-    nodes_.pop();
-    if (!ok) // error already set
-      return recoverFromError(tokenArrayEnd);
-
-    Token currentToken;
-    // Accept Comment after last item in the array.
-    ok = readToken(currentToken);
-    while (currentToken.type_ == tokenComment && ok) {
-      ok = readToken(currentToken);
-    }
-    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
-                         currentToken.type_ != tokenArrayEnd);
-    if (!ok || badTokenType) {
-      return addErrorAndRecover("Missing ',' or ']' in array declaration",
-                                currentToken, tokenArrayEnd);
-    }
-    if (currentToken.type_ == tokenArrayEnd)
-      break;
-  }
-  return true;
-}
-
-bool OurReader::decodeNumber(Token& token) {
-  Value decoded;
-  if (!decodeNumber(token, decoded))
-    return false;
-  currentValue().swapPayload(decoded);
-  currentValue().setOffsetStart(token.start_ - begin_);
-  currentValue().setOffsetLimit(token.end_ - begin_);
-  return true;
-}
-
-bool OurReader::decodeNumber(Token& token, Value& decoded) {
-  // Attempts to parse the number as an integer. If the number is
-  // larger than the maximum supported value of an integer then
-  // we decode the number as a double.
-  Location current = token.start_;
-  bool isNegative = *current == '-';
-  if (isNegative)
-    ++current;
-  // TODO: Help the compiler do the div and mod at compile time or get rid of
-  // them.
-  Value::LargestUInt maxIntegerValue =
-      isNegative ? Value::LargestUInt(Value::minLargestInt)
-                 : Value::maxLargestUInt;
-  Value::LargestUInt threshold = maxIntegerValue / 10;
-  Value::LargestUInt value = 0;
-  while (current < token.end_) {
-    Char c = *current++;
-    if (c < '0' || c > '9')
-      return decodeDouble(token, decoded);
-    auto digit(static_cast<Value::UInt>(c - '0'));
-    if (value >= threshold) {
-      // We've hit or exceeded the max value divided by 10 (rounded down). If
-      // a) we've only just touched the limit, b) this is the last digit, and
-      // c) it's small enough to fit in that rounding delta, we're okay.
-      // Otherwise treat this number as a double to avoid overflow.
-      if (value > threshold || current != token.end_ ||
-          digit > maxIntegerValue % 10) {
-        return decodeDouble(token, decoded);
-      }
-    }
-    value = value * 10 + digit;
-  }
-  if (isNegative)
-    decoded = -Value::LargestInt(value);
-  else if (value <= Value::LargestUInt(Value::maxInt))
-    decoded = Value::LargestInt(value);
-  else
-    decoded = value;
-  return true;
-}
-
-bool OurReader::decodeDouble(Token& token) {
-  Value decoded;
-  if (!decodeDouble(token, decoded))
-    return false;
-  currentValue().swapPayload(decoded);
-  currentValue().setOffsetStart(token.start_ - begin_);
-  currentValue().setOffsetLimit(token.end_ - begin_);
-  return true;
-}
-
-bool OurReader::decodeDouble(Token& token, Value& decoded) {
-  double value = 0;
-  const int bufferSize = 32;
-  int count;
-  ptrdiff_t const length = token.end_ - token.start_;
-
-  // Sanity check to avoid buffer overflow exploits.
-  if (length < 0) {
-    return addError("Unable to parse token length", token);
-  }
-  auto const ulength = static_cast<size_t>(length);
-
-  // Avoid using a string constant for the format control string given to
-  // sscanf, as this can cause hard to debug crashes on OS X. See here for more
-  // info:
-  //
-  //     http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
-  char format[] = "%lf";
-
-  if (length <= bufferSize) {
-    Char buffer[bufferSize + 1];
-    memcpy(buffer, token.start_, ulength);
-    buffer[length] = 0;
-    fixNumericLocaleInput(buffer, buffer + length);
-    count = sscanf(buffer, format, &value);
-  } else {
-    String buffer(token.start_, token.end_);
-    count = sscanf(buffer.c_str(), format, &value);
-  }
-
-  if (count != 1)
-    return addError(
-        "'" + String(token.start_, token.end_) + "' is not a number.", token);
-  decoded = value;
-  return true;
-}
-
-bool OurReader::decodeString(Token& token) {
-  String decoded_string;
-  if (!decodeString(token, decoded_string))
-    return false;
-  Value decoded(decoded_string);
-  currentValue().swapPayload(decoded);
-  currentValue().setOffsetStart(token.start_ - begin_);
-  currentValue().setOffsetLimit(token.end_ - begin_);
-  return true;
-}
-
-bool OurReader::decodeString(Token& token, String& decoded) {
-  decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
-  Location current = token.start_ + 1; // skip '"'
-  Location end = token.end_ - 1;       // do not include '"'
-  while (current != end) {
-    Char c = *current++;
-    if (c == '"')
-      break;
-    else if (c == '\\') {
-      if (current == end)
-        return addError("Empty escape sequence in string", token, current);
-      Char escape = *current++;
-      switch (escape) {
-      case '"':
-        decoded += '"';
-        break;
-      case '/':
-        decoded += '/';
-        break;
-      case '\\':
-        decoded += '\\';
-        break;
-      case 'b':
-        decoded += '\b';
-        break;
-      case 'f':
-        decoded += '\f';
-        break;
-      case 'n':
-        decoded += '\n';
-        break;
-      case 'r':
-        decoded += '\r';
-        break;
-      case 't':
-        decoded += '\t';
-        break;
-      case 'u': {
-        unsigned int unicode;
-        if (!decodeUnicodeCodePoint(token, current, end, unicode))
-          return false;
-        decoded += codePointToUTF8(unicode);
-      } break;
-      default:
-        return addError("Bad escape sequence in string", token, current);
-      }
-    } else {
-      decoded += c;
-    }
-  }
-  return true;
-}
-
-bool OurReader::decodeUnicodeCodePoint(Token& token,
-                                       Location& current,
-                                       Location end,
-                                       unsigned int& unicode) {
-
-  if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
-    return false;
-  if (unicode >= 0xD800 && unicode <= 0xDBFF) {
-    // surrogate pairs
-    if (end - current < 6)
-      return addError(
-          "additional six characters expected to parse unicode surrogate pair.",
-          token, current);
-    if (*(current++) == '\\' && *(current++) == 'u') {
-      unsigned int surrogatePair;
-      if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
-        unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
-      } else
-        return false;
-    } else
-      return addError("expecting another \\u token to begin the second half of "
-                      "a unicode surrogate pair",
-                      token, current);
-  }
-  return true;
-}
-
-bool OurReader::decodeUnicodeEscapeSequence(Token& token,
-                                            Location& current,
-                                            Location end,
-                                            unsigned int& ret_unicode) {
-  if (end - current < 4)
-    return addError(
-        "Bad unicode escape sequence in string: four digits expected.", token,
-        current);
-  int unicode = 0;
-  for (int index = 0; index < 4; ++index) {
-    Char c = *current++;
-    unicode *= 16;
-    if (c >= '0' && c <= '9')
-      unicode += c - '0';
-    else if (c >= 'a' && c <= 'f')
-      unicode += c - 'a' + 10;
-    else if (c >= 'A' && c <= 'F')
-      unicode += c - 'A' + 10;
-    else
-      return addError(
-          "Bad unicode escape sequence in string: hexadecimal digit expected.",
-          token, current);
-  }
-  ret_unicode = static_cast<unsigned int>(unicode);
-  return true;
-}
-
-bool OurReader::addError(const String& message, Token& token, Location extra) {
-  ErrorInfo info;
-  info.token_ = token;
-  info.message_ = message;
-  info.extra_ = extra;
-  errors_.push_back(info);
-  return false;
-}
-
-bool OurReader::recoverFromError(TokenType skipUntilToken) {
-  size_t errorCount = errors_.size();
-  Token skip;
-  for (;;) {
-    if (!readToken(skip))
-      errors_.resize(errorCount); // discard errors caused by recovery
-    if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
-      break;
-  }
-  errors_.resize(errorCount);
-  return false;
-}
-
-bool OurReader::addErrorAndRecover(const String& message,
-                                   Token& token,
-                                   TokenType skipUntilToken) {
-  addError(message, token);
-  return recoverFromError(skipUntilToken);
-}
-
-Value& OurReader::currentValue() { return *(nodes_.top()); }
-
-OurReader::Char OurReader::getNextChar() {
-  if (current_ == end_)
-    return 0;
-  return *current_++;
-}
-
-void OurReader::getLocationLineAndColumn(Location location,
-                                         int& line,
-                                         int& column) const {
-  Location current = begin_;
-  Location lastLineStart = current;
-  line = 0;
-  while (current < location && current != end_) {
-    Char c = *current++;
-    if (c == '\r') {
-      if (*current == '\n')
-        ++current;
-      lastLineStart = current;
-      ++line;
-    } else if (c == '\n') {
-      lastLineStart = current;
-      ++line;
-    }
-  }
-  // column & line start at 1
-  column = int(location - lastLineStart) + 1;
-  ++line;
-}
-
-String OurReader::getLocationLineAndColumn(Location location) const {
-  int line, column;
-  getLocationLineAndColumn(location, line, column);
-  char buffer[18 + 16 + 16 + 1];
-  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
-  return buffer;
-}
-
-String OurReader::getFormattedErrorMessages() const {
-  String formattedMessage;
-  for (const auto& error : errors_) {
-    formattedMessage +=
-        "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
-    formattedMessage += "  " + error.message_ + "\n";
-    if (error.extra_)
-      formattedMessage +=
-          "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n";
-  }
-  return formattedMessage;
-}
-
-std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
-  std::vector<OurReader::StructuredError> allErrors;
-  for (const auto& error : errors_) {
-    OurReader::StructuredError structured;
-    structured.offset_start = error.token_.start_ - begin_;
-    structured.offset_limit = error.token_.end_ - begin_;
-    structured.message = error.message_;
-    allErrors.push_back(structured);
-  }
-  return allErrors;
-}
-
-bool OurReader::pushError(const Value& value, const String& message) {
-  ptrdiff_t length = end_ - begin_;
-  if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
-    return false;
-  Token token;
-  token.type_ = tokenError;
-  token.start_ = begin_ + value.getOffsetStart();
-  token.end_ = end_ + value.getOffsetLimit();
-  ErrorInfo info;
-  info.token_ = token;
-  info.message_ = message;
-  info.extra_ = nullptr;
-  errors_.push_back(info);
-  return true;
-}
-
-bool OurReader::pushError(const Value& value,
-                          const String& message,
-                          const Value& extra) {
-  ptrdiff_t length = end_ - begin_;
-  if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
-      extra.getOffsetLimit() > length)
-    return false;
-  Token token;
-  token.type_ = tokenError;
-  token.start_ = begin_ + value.getOffsetStart();
-  token.end_ = begin_ + value.getOffsetLimit();
-  ErrorInfo info;
-  info.token_ = token;
-  info.message_ = message;
-  info.extra_ = begin_ + extra.getOffsetStart();
-  errors_.push_back(info);
-  return true;
-}
-
-bool OurReader::good() const { return errors_.empty(); }
-
-class OurCharReader : public CharReader {
-  bool const collectComments_;
-  OurReader reader_;
-
-public:
-  OurCharReader(bool collectComments, OurFeatures const& features)
-      : collectComments_(collectComments), reader_(features) {}
-  bool parse(char const* beginDoc,
-             char const* endDoc,
-             Value* root,
-             String* errs) override {
-    bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
-    if (errs) {
-      *errs = reader_.getFormattedErrorMessages();
-    }
-    return ok;
-  }
-};
-
-CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
-CharReaderBuilder::~CharReaderBuilder() = default;
-CharReader* CharReaderBuilder::newCharReader() const {
-  bool collectComments = settings_["collectComments"].asBool();
-  OurFeatures features = OurFeatures::all();
-  features.allowComments_ = settings_["allowComments"].asBool();
-  features.strictRoot_ = settings_["strictRoot"].asBool();
-  features.allowDroppedNullPlaceholders_ =
-      settings_["allowDroppedNullPlaceholders"].asBool();
-  features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
-  features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
-#if defined(JSON_HAS_INT64)
-  features.stackLimit_ = settings_["stackLimit"].asUInt64();
-#else
-  features.stackLimit_ = settings_["stackLimit"].asUInt();
-#endif
-  features.failIfExtra_ = settings_["failIfExtra"].asBool();
-  features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
-  features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
-  return new OurCharReader(collectComments, features);
-}
-static void getValidReaderKeys(std::set<String>* valid_keys) {
-  valid_keys->clear();
-  valid_keys->insert("collectComments");
-  valid_keys->insert("allowComments");
-  valid_keys->insert("strictRoot");
-  valid_keys->insert("allowDroppedNullPlaceholders");
-  valid_keys->insert("allowNumericKeys");
-  valid_keys->insert("allowSingleQuotes");
-  valid_keys->insert("stackLimit");
-  valid_keys->insert("failIfExtra");
-  valid_keys->insert("rejectDupKeys");
-  valid_keys->insert("allowSpecialFloats");
-}
-bool CharReaderBuilder::validate(Json::Value* invalid) const {
-  Json::Value my_invalid;
-  if (!invalid)
-    invalid = &my_invalid; // so we do not need to test for NULL
-  Json::Value& inv = *invalid;
-  std::set<String> valid_keys;
-  getValidReaderKeys(&valid_keys);
-  Value::Members keys = settings_.getMemberNames();
-  size_t n = keys.size();
-  for (size_t i = 0; i < n; ++i) {
-    String const& key = keys[i];
-    if (valid_keys.find(key) == valid_keys.end()) {
-      inv[key] = settings_[key];
-    }
-  }
-  return inv.empty();
-}
-Value& CharReaderBuilder::operator[](const String& key) {
-  return settings_[key];
-}
-// static
-void CharReaderBuilder::strictMode(Json::Value* settings) {
-  //! [CharReaderBuilderStrictMode]
-  (*settings)["allowComments"] = false;
-  (*settings)["strictRoot"] = true;
-  (*settings)["allowDroppedNullPlaceholders"] = false;
-  (*settings)["allowNumericKeys"] = false;
-  (*settings)["allowSingleQuotes"] = false;
-  (*settings)["stackLimit"] = 1000;
-  (*settings)["failIfExtra"] = true;
-  (*settings)["rejectDupKeys"] = true;
-  (*settings)["allowSpecialFloats"] = false;
-  //! [CharReaderBuilderStrictMode]
-}
-// static
-void CharReaderBuilder::setDefaults(Json::Value* settings) {
-  //! [CharReaderBuilderDefaults]
-  (*settings)["collectComments"] = true;
-  (*settings)["allowComments"] = true;
-  (*settings)["strictRoot"] = false;
-  (*settings)["allowDroppedNullPlaceholders"] = false;
-  (*settings)["allowNumericKeys"] = false;
-  (*settings)["allowSingleQuotes"] = false;
-  (*settings)["stackLimit"] = 1000;
-  (*settings)["failIfExtra"] = false;
-  (*settings)["rejectDupKeys"] = false;
-  (*settings)["allowSpecialFloats"] = false;
-  //! [CharReaderBuilderDefaults]
-}
-
-//////////////////////////////////
-// global functions
-
-bool parseFromStream(CharReader::Factory const& fact,
-                     IStream& sin,
-                     Value* root,
-                     String* errs) {
-  OStringStream ssin;
-  ssin << sin.rdbuf();
-  String doc = ssin.str();
-  char const* begin = doc.data();
-  char const* end = begin + doc.size();
-  // Note that we do not actually need a null-terminator.
-  CharReaderPtr const reader(fact.newCharReader());
-  return reader->parse(begin, end, root, errs);
-}
-
-IStream& operator>>(IStream& sin, Value& root) {
-  CharReaderBuilder b;
-  String errs;
-  bool ok = parseFromStream(b, sin, &root, &errs);
-  if (!ok) {
-    throwRuntimeError(errs);
-  }
-  return sin;
-}
-
-} // namespace Json
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: src/lib_json/json_reader.cpp
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: src/lib_json/json_valueiterator.inl
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-// included by json_value.cpp
-
-namespace Json {
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class ValueIteratorBase
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-ValueIteratorBase::ValueIteratorBase() : current_() {}
-
-ValueIteratorBase::ValueIteratorBase(
-    const Value::ObjectValues::iterator& current)
-    : current_(current), isNull_(false) {}
-
-Value& ValueIteratorBase::deref() const { return current_->second; }
-
-void ValueIteratorBase::increment() { ++current_; }
-
-void ValueIteratorBase::decrement() { --current_; }
-
-ValueIteratorBase::difference_type
-ValueIteratorBase::computeDistance(const SelfType& other) const {
-#ifdef JSON_USE_CPPTL_SMALLMAP
-  return other.current_ - current_;
-#else
-  // Iterator for null value are initialized using the default
-  // constructor, which initialize current_ to the default
-  // std::map::iterator. As begin() and end() are two instance
-  // of the default std::map::iterator, they can not be compared.
-  // To allow this, we handle this comparison specifically.
-  if (isNull_ && other.isNull_) {
-    return 0;
-  }
-
-  // Usage of std::distance is not portable (does not compile with Sun Studio 12
-  // RogueWave STL,
-  // which is the one used by default).
-  // Using a portable hand-made version for non random iterator instead:
-  //   return difference_type( std::distance( current_, other.current_ ) );
-  difference_type myDistance = 0;
-  for (Value::ObjectValues::iterator it = current_; it != other.current_;
-       ++it) {
-    ++myDistance;
-  }
-  return myDistance;
-#endif
-}
-
-bool ValueIteratorBase::isEqual(const SelfType& other) const {
-  if (isNull_) {
-    return other.isNull_;
-  }
-  return current_ == other.current_;
-}
-
-void ValueIteratorBase::copy(const SelfType& other) {
-  current_ = other.current_;
-  isNull_ = other.isNull_;
-}
-
-Value ValueIteratorBase::key() const {
-  const Value::CZString czstring = (*current_).first;
-  if (czstring.data()) {
-    if (czstring.isStaticString())
-      return Value(StaticString(czstring.data()));
-    return Value(czstring.data(), czstring.data() + czstring.length());
-  }
-  return Value(czstring.index());
-}
-
-UInt ValueIteratorBase::index() const {
-  const Value::CZString czstring = (*current_).first;
-  if (!czstring.data())
-    return czstring.index();
-  return Value::UInt(-1);
-}
-
-String ValueIteratorBase::name() const {
-  char const* keey;
-  char const* end;
-  keey = memberName(&end);
-  if (!keey)
-    return String();
-  return String(keey, end);
-}
-
-char const* ValueIteratorBase::memberName() const {
-  const char* cname = (*current_).first.data();
-  return cname ? cname : "";
-}
-
-char const* ValueIteratorBase::memberName(char const** end) const {
-  const char* cname = (*current_).first.data();
-  if (!cname) {
-    *end = nullptr;
-    return nullptr;
-  }
-  *end = cname + (*current_).first.length();
-  return cname;
-}
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class ValueConstIterator
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-ValueConstIterator::ValueConstIterator() = default;
-
-ValueConstIterator::ValueConstIterator(
-    const Value::ObjectValues::iterator& current)
-    : ValueIteratorBase(current) {}
-
-ValueConstIterator::ValueConstIterator(ValueIterator const& other)
-    : ValueIteratorBase(other) {}
-
-ValueConstIterator& ValueConstIterator::
-operator=(const ValueIteratorBase& other) {
-  copy(other);
-  return *this;
-}
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class ValueIterator
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-ValueIterator::ValueIterator() = default;
-
-ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current)
-    : ValueIteratorBase(current) {}
-
-ValueIterator::ValueIterator(const ValueConstIterator& other)
-    : ValueIteratorBase(other) {
-  throwRuntimeError("ConstIterator to Iterator should never be allowed.");
-}
-
-ValueIterator::ValueIterator(const ValueIterator& other) = default;
-
-ValueIterator& ValueIterator::operator=(const SelfType& other) {
-  copy(other);
-  return *this;
-}
-
-} // namespace Json
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: src/lib_json/json_valueiterator.inl
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: src/lib_json/json_value.cpp
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#if !defined(JSON_IS_AMALGAMATION)
-#include <json/assertions.h>
-#include <json/value.h>
-#include <json/writer.h>
-#endif // if !defined(JSON_IS_AMALGAMATION)
-#include <cassert>
-#include <cmath>
-#include <cstring>
-#include <sstream>
-#include <utility>
-#ifdef JSON_USE_CPPTL
-#include <cpptl/conststring.h>
-#endif
-#include <algorithm> // min()
-#include <cstddef>   // size_t
-
-// Provide implementation equivalent of std::snprintf for older _MSC compilers
-#if defined(_MSC_VER) && _MSC_VER < 1900
-#include <stdarg.h>
-static int msvc_pre1900_c99_vsnprintf(char* outBuf,
-                                      size_t size,
-                                      const char* format,
-                                      va_list ap) {
-  int count = -1;
-  if (size != 0)
-    count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
-  if (count == -1)
-    count = _vscprintf(format, ap);
-  return count;
-}
-
-int JSON_API msvc_pre1900_c99_snprintf(char* outBuf,
-                                       size_t size,
-                                       const char* format,
-                                       ...) {
-  va_list ap;
-  va_start(ap, format);
-  const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap);
-  va_end(ap);
-  return count;
-}
-#endif
-
-// Disable warning C4702 : unreachable code
-#if defined(_MSC_VER)
-#pragma warning(disable : 4702)
-#endif
-
-#define JSON_ASSERT_UNREACHABLE assert(false)
-
-namespace Json {
-
-// This is a walkaround to avoid the static initialization of Value::null.
-// kNull must be word-aligned to avoid crashing on ARM.  We use an alignment of
-// 8 (instead of 4) as a bit of future-proofing.
-#if defined(__ARMEL__)
-#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
-#else
-#define ALIGNAS(byte_alignment)
-#endif
-// static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };
-// const unsigned char& kNullRef = kNull[0];
-// const Value& Value::null = reinterpret_cast<const Value&>(kNullRef);
-// const Value& Value::nullRef = null;
-
-// static
-Value const& Value::nullSingleton() {
-  static Value const nullStatic;
-  return nullStatic;
-}
-
-// for backwards compatibility, we'll leave these global references around, but
-// DO NOT use them in JSONCPP library code any more!
-Value const& Value::null = Value::nullSingleton();
-Value const& Value::nullRef = Value::nullSingleton();
-
-const Int Value::minInt = Int(~(UInt(-1) / 2));
-const Int Value::maxInt = Int(UInt(-1) / 2);
-const UInt Value::maxUInt = UInt(-1);
-#if defined(JSON_HAS_INT64)
-const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2));
-const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2);
-const UInt64 Value::maxUInt64 = UInt64(-1);
-// The constant is hard-coded because some compiler have trouble
-// converting Value::maxUInt64 to a double correctly (AIX/xlC).
-// Assumes that UInt64 is a 64 bits integer.
-static const double maxUInt64AsDouble = 18446744073709551615.0;
-#endif // defined(JSON_HAS_INT64)
-const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2));
-const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);
-const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
-
-const UInt Value::defaultRealPrecision = 17;
-
-#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-template <typename T, typename U>
-static inline bool InRange(double d, T min, U max) {
-  // The casts can lose precision, but we are looking only for
-  // an approximate range. Might fail on edge cases though. ~cdunn
-  // return d >= static_cast<double>(min) && d <= static_cast<double>(max);
-  return d >= min && d <= max;
-}
-#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-static inline double integerToDouble(Json::UInt64 value) {
-  return static_cast<double>(Int64(value / 2)) * 2.0 +
-         static_cast<double>(Int64(value & 1));
-}
-
-template <typename T> static inline double integerToDouble(T value) {
-  return static_cast<double>(value);
-}
-
-template <typename T, typename U>
-static inline bool InRange(double d, T min, U max) {
-  return d >= integerToDouble(min) && d <= integerToDouble(max);
-}
-#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-
-/** Duplicates the specified string value.
- * @param value Pointer to the string to duplicate. Must be zero-terminated if
- *              length is "unknown".
- * @param length Length of the value. if equals to unknown, then it will be
- *               computed using strlen(value).
- * @return Pointer on the duplicate instance of string.
- */
-static inline char* duplicateStringValue(const char* value, size_t length) {
-  // Avoid an integer overflow in the call to malloc below by limiting length
-  // to a sane value.
-  if (length >= static_cast<size_t>(Value::maxInt))
-    length = Value::maxInt - 1;
-
-  char* newString = static_cast<char*>(malloc(length + 1));
-  if (newString == nullptr) {
-    throwRuntimeError("in Json::Value::duplicateStringValue(): "
-                      "Failed to allocate string value buffer");
-  }
-  memcpy(newString, value, length);
-  newString[length] = 0;
-  return newString;
-}
-
-/* Record the length as a prefix.
- */
-static inline char* duplicateAndPrefixStringValue(const char* value,
-                                                  unsigned int length) {
-  // Avoid an integer overflow in the call to malloc below by limiting length
-  // to a sane value.
-  JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) -
-                                    sizeof(unsigned) - 1U,
-                      "in Json::Value::duplicateAndPrefixStringValue(): "
-                      "length too big for prefixing");
-  unsigned actualLength = length + static_cast<unsigned>(sizeof(unsigned)) + 1U;
-  char* newString = static_cast<char*>(malloc(actualLength));
-  if (newString == nullptr) {
-    throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): "
-                      "Failed to allocate string value buffer");
-  }
-  *reinterpret_cast<unsigned*>(newString) = length;
-  memcpy(newString + sizeof(unsigned), value, length);
-  newString[actualLength - 1U] =
-      0; // to avoid buffer over-run accidents by users later
-  return newString;
-}
-inline static void decodePrefixedString(bool isPrefixed,
-                                        char const* prefixed,
-                                        unsigned* length,
-                                        char const** value) {
-  if (!isPrefixed) {
-    *length = static_cast<unsigned>(strlen(prefixed));
-    *value = prefixed;
-  } else {
-    *length = *reinterpret_cast<unsigned const*>(prefixed);
-    *value = prefixed + sizeof(unsigned);
-  }
-}
-/** Free the string duplicated by
- * duplicateStringValue()/duplicateAndPrefixStringValue().
- */
-#if JSONCPP_USING_SECURE_MEMORY
-static inline void releasePrefixedStringValue(char* value) {
-  unsigned length = 0;
-  char const* valueDecoded;
-  decodePrefixedString(true, value, &length, &valueDecoded);
-  size_t const size = sizeof(unsigned) + length + 1U;
-  memset(value, 0, size);
-  free(value);
-}
-static inline void releaseStringValue(char* value, unsigned length) {
-  // length==0 => we allocated the strings memory
-  size_t size = (length == 0) ? strlen(value) : length;
-  memset(value, 0, size);
-  free(value);
-}
-#else  // !JSONCPP_USING_SECURE_MEMORY
-static inline void releasePrefixedStringValue(char* value) { free(value); }
-static inline void releaseStringValue(char* value, unsigned) { free(value); }
-#endif // JSONCPP_USING_SECURE_MEMORY
-
-} // namespace Json
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// ValueInternals...
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-#if !defined(JSON_IS_AMALGAMATION)
-
-#include "json_valueiterator.inl"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-
-namespace Json {
-
-Exception::Exception(String msg) : msg_(std::move(msg)) {}
-Exception::~Exception() JSONCPP_NOEXCEPT {}
-char const* Exception::what() const JSONCPP_NOEXCEPT { return msg_.c_str(); }
-RuntimeError::RuntimeError(String const& msg) : Exception(msg) {}
-LogicError::LogicError(String const& msg) : Exception(msg) {}
-JSONCPP_NORETURN void throwRuntimeError(String const& msg) {
-  throw RuntimeError(msg);
-}
-JSONCPP_NORETURN void throwLogicError(String const& msg) {
-  throw LogicError(msg);
-}
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class Value::CommentInfo
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-Value::CommentInfo::CommentInfo() = default;
-
-Value::CommentInfo::~CommentInfo() {
-  if (comment_)
-    releaseStringValue(comment_, 0u);
-}
-
-void Value::CommentInfo::setComment(const char* text, size_t len) {
-  if (comment_) {
-    releaseStringValue(comment_, 0u);
-    comment_ = nullptr;
-  }
-  JSON_ASSERT(text != nullptr);
-  JSON_ASSERT_MESSAGE(
-      text[0] == '\0' || text[0] == '/',
-      "in Json::Value::setComment(): Comments must start with /");
-  // It seems that /**/ style comments are acceptable as well.
-  comment_ = duplicateStringValue(text, len);
-}
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class Value::CZString
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-// Notes: policy_ indicates if the string was allocated when
-// a string is stored.
-
-Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {}
-
-Value::CZString::CZString(char const* str,
-                          unsigned length,
-                          DuplicationPolicy allocate)
-    : cstr_(str) {
-  // allocate != duplicate
-  storage_.policy_ = allocate & 0x3;
-  storage_.length_ = length & 0x3FFFFFFF;
-}
-
-Value::CZString::CZString(const CZString& other) {
-  cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != nullptr
-               ? duplicateStringValue(other.cstr_, other.storage_.length_)
-               : other.cstr_);
-  storage_.policy_ =
-      static_cast<unsigned>(
-          other.cstr_
-              ? (static_cast<DuplicationPolicy>(other.storage_.policy_) ==
-                         noDuplication
-                     ? noDuplication
-                     : duplicate)
-              : static_cast<DuplicationPolicy>(other.storage_.policy_)) &
-      3U;
-  storage_.length_ = other.storage_.length_;
-}
-
-#if JSON_HAS_RVALUE_REFERENCES
-Value::CZString::CZString(CZString&& other)
-    : cstr_(other.cstr_), index_(other.index_) {
-  other.cstr_ = nullptr;
-}
-#endif
-
-Value::CZString::~CZString() {
-  if (cstr_ && storage_.policy_ == duplicate) {
-    releaseStringValue(const_cast<char*>(cstr_),
-                       storage_.length_ + 1u); // +1 for null terminating
-                                               // character for sake of
-                                               // completeness but not actually
-                                               // necessary
-  }
-}
-
-void Value::CZString::swap(CZString& other) {
-  std::swap(cstr_, other.cstr_);
-  std::swap(index_, other.index_);
-}
-
-Value::CZString& Value::CZString::operator=(const CZString& other) {
-  cstr_ = other.cstr_;
-  index_ = other.index_;
-  return *this;
-}
-
-#if JSON_HAS_RVALUE_REFERENCES
-Value::CZString& Value::CZString::operator=(CZString&& other) {
-  cstr_ = other.cstr_;
-  index_ = other.index_;
-  other.cstr_ = nullptr;
-  return *this;
-}
-#endif
-
-bool Value::CZString::operator<(const CZString& other) const {
-  if (!cstr_)
-    return index_ < other.index_;
-  // return strcmp(cstr_, other.cstr_) < 0;
-  // Assume both are strings.
-  unsigned this_len = this->storage_.length_;
-  unsigned other_len = other.storage_.length_;
-  unsigned min_len = std::min<unsigned>(this_len, other_len);
-  JSON_ASSERT(this->cstr_ && other.cstr_);
-  int comp = memcmp(this->cstr_, other.cstr_, min_len);
-  if (comp < 0)
-    return true;
-  if (comp > 0)
-    return false;
-  return (this_len < other_len);
-}
-
-bool Value::CZString::operator==(const CZString& other) const {
-  if (!cstr_)
-    return index_ == other.index_;
-  // return strcmp(cstr_, other.cstr_) == 0;
-  // Assume both are strings.
-  unsigned this_len = this->storage_.length_;
-  unsigned other_len = other.storage_.length_;
-  if (this_len != other_len)
-    return false;
-  JSON_ASSERT(this->cstr_ && other.cstr_);
-  int comp = memcmp(this->cstr_, other.cstr_, this_len);
-  return comp == 0;
-}
-
-ArrayIndex Value::CZString::index() const { return index_; }
-
-// const char* Value::CZString::c_str() const { return cstr_; }
-const char* Value::CZString::data() const { return cstr_; }
-unsigned Value::CZString::length() const { return storage_.length_; }
-bool Value::CZString::isStaticString() const {
-  return storage_.policy_ == noDuplication;
-}
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class Value::Value
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-/*! \internal Default constructor initialization must be equivalent to:
- * memset( this, 0, sizeof(Value) )
- * This optimization is used in ValueInternalMap fast allocator.
- */
-Value::Value(ValueType type) {
-  static char const emptyString[] = "";
-  initBasic(type);
-  switch (type) {
-  case nullValue:
-    break;
-  case intValue:
-  case uintValue:
-    value_.int_ = 0;
-    break;
-  case realValue:
-    value_.real_ = 0.0;
-    break;
-  case stringValue:
-    // allocated_ == false, so this is safe.
-    value_.string_ = const_cast<char*>(static_cast<char const*>(emptyString));
-    break;
-  case arrayValue:
-  case objectValue:
-    value_.map_ = new ObjectValues();
-    break;
-  case booleanValue:
-    value_.bool_ = false;
-    break;
-  default:
-    JSON_ASSERT_UNREACHABLE;
-  }
-}
-
-Value::Value(Int value) {
-  initBasic(intValue);
-  value_.int_ = value;
-}
-
-Value::Value(UInt value) {
-  initBasic(uintValue);
-  value_.uint_ = value;
-}
-#if defined(JSON_HAS_INT64)
-Value::Value(Int64 value) {
-  initBasic(intValue);
-  value_.int_ = value;
-}
-Value::Value(UInt64 value) {
-  initBasic(uintValue);
-  value_.uint_ = value;
-}
-#endif // defined(JSON_HAS_INT64)
-
-Value::Value(double value) {
-  initBasic(realValue);
-  value_.real_ = value;
-}
-
-Value::Value(const char* value) {
-  initBasic(stringValue, true);
-  JSON_ASSERT_MESSAGE(value != nullptr,
-                      "Null Value Passed to Value Constructor");
-  value_.string_ = duplicateAndPrefixStringValue(
-      value, static_cast<unsigned>(strlen(value)));
-}
-
-Value::Value(const char* begin, const char* end) {
-  initBasic(stringValue, true);
-  value_.string_ =
-      duplicateAndPrefixStringValue(begin, static_cast<unsigned>(end - begin));
-}
-
-Value::Value(const String& value) {
-  initBasic(stringValue, true);
-  value_.string_ = duplicateAndPrefixStringValue(
-      value.data(), static_cast<unsigned>(value.length()));
-}
-
-Value::Value(const StaticString& value) {
-  initBasic(stringValue);
-  value_.string_ = const_cast<char*>(value.c_str());
-}
-
-#ifdef JSON_USE_CPPTL
-Value::Value(const CppTL::ConstString& value) {
-  initBasic(stringValue, true);
-  value_.string_ = duplicateAndPrefixStringValue(
-      value, static_cast<unsigned>(value.length()));
-}
-#endif
-
-Value::Value(bool value) {
-  initBasic(booleanValue);
-  value_.bool_ = value;
-}
-
-Value::Value(const Value& other) {
-  dupPayload(other);
-  dupMeta(other);
-}
-
-Value::Value(Value&& other) {
-  initBasic(nullValue);
-  swap(other);
-}
-
-Value::~Value() {
-  releasePayload();
-  delete[] comments_;
-  value_.uint_ = 0;
-}
-
-Value& Value::operator=(const Value& other) {
-  Value(other).swap(*this);
-  return *this;
-}
-
-Value& Value::operator=(Value&& other) {
-  other.swap(*this);
-  return *this;
-}
-
-void Value::swapPayload(Value& other) {
-  std::swap(bits_, other.bits_);
-  std::swap(value_, other.value_);
-}
-
-void Value::copyPayload(const Value& other) {
-  releasePayload();
-  dupPayload(other);
-}
-
-void Value::swap(Value& other) {
-  swapPayload(other);
-  std::swap(comments_, other.comments_);
-  std::swap(start_, other.start_);
-  std::swap(limit_, other.limit_);
-}
-
-void Value::copy(const Value& other) {
-  copyPayload(other);
-  delete[] comments_;
-  dupMeta(other);
-}
-
-ValueType Value::type() const {
-  return static_cast<ValueType>(bits_.value_type_);
-}
-
-int Value::compare(const Value& other) const {
-  if (*this < other)
-    return -1;
-  if (*this > other)
-    return 1;
-  return 0;
-}
-
-bool Value::operator<(const Value& other) const {
-  int typeDelta = type() - other.type();
-  if (typeDelta)
-    return typeDelta < 0 ? true : false;
-  switch (type()) {
-  case nullValue:
-    return false;
-  case intValue:
-    return value_.int_ < other.value_.int_;
-  case uintValue:
-    return value_.uint_ < other.value_.uint_;
-  case realValue:
-    return value_.real_ < other.value_.real_;
-  case booleanValue:
-    return value_.bool_ < other.value_.bool_;
-  case stringValue: {
-    if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
-      if (other.value_.string_)
-        return true;
-      else
-        return false;
-    }
-    unsigned this_len;
-    unsigned other_len;
-    char const* this_str;
-    char const* other_str;
-    decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
-                         &this_str);
-    decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
-                         &other_str);
-    unsigned min_len = std::min<unsigned>(this_len, other_len);
-    JSON_ASSERT(this_str && other_str);
-    int comp = memcmp(this_str, other_str, min_len);
-    if (comp < 0)
-      return true;
-    if (comp > 0)
-      return false;
-    return (this_len < other_len);
-  }
-  case arrayValue:
-  case objectValue: {
-    int delta = int(value_.map_->size() - other.value_.map_->size());
-    if (delta)
-      return delta < 0;
-    return (*value_.map_) < (*other.value_.map_);
-  }
-  default:
-    JSON_ASSERT_UNREACHABLE;
-  }
-  return false; // unreachable
-}
-
-bool Value::operator<=(const Value& other) const { return !(other < *this); }
-
-bool Value::operator>=(const Value& other) const { return !(*this < other); }
-
-bool Value::operator>(const Value& other) const { return other < *this; }
-
-bool Value::operator==(const Value& other) const {
-  if (type() != other.type())
-    return false;
-  switch (type()) {
-  case nullValue:
-    return true;
-  case intValue:
-    return value_.int_ == other.value_.int_;
-  case uintValue:
-    return value_.uint_ == other.value_.uint_;
-  case realValue:
-    return value_.real_ == other.value_.real_;
-  case booleanValue:
-    return value_.bool_ == other.value_.bool_;
-  case stringValue: {
-    if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
-      return (value_.string_ == other.value_.string_);
-    }
-    unsigned this_len;
-    unsigned other_len;
-    char const* this_str;
-    char const* other_str;
-    decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
-                         &this_str);
-    decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
-                         &other_str);
-    if (this_len != other_len)
-      return false;
-    JSON_ASSERT(this_str && other_str);
-    int comp = memcmp(this_str, other_str, this_len);
-    return comp == 0;
-  }
-  case arrayValue:
-  case objectValue:
-    return value_.map_->size() == other.value_.map_->size() &&
-           (*value_.map_) == (*other.value_.map_);
-  default:
-    JSON_ASSERT_UNREACHABLE;
-  }
-  return false; // unreachable
-}
-
-bool Value::operator!=(const Value& other) const { return !(*this == other); }
-
-const char* Value::asCString() const {
-  JSON_ASSERT_MESSAGE(type() == stringValue,
-                      "in Json::Value::asCString(): requires stringValue");
-  if (value_.string_ == nullptr)
-    return nullptr;
-  unsigned this_len;
-  char const* this_str;
-  decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
-                       &this_str);
-  return this_str;
-}
-
-#if JSONCPP_USING_SECURE_MEMORY
-unsigned Value::getCStringLength() const {
-  JSON_ASSERT_MESSAGE(type() == stringValue,
-                      "in Json::Value::asCString(): requires stringValue");
-  if (value_.string_ == 0)
-    return 0;
-  unsigned this_len;
-  char const* this_str;
-  decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
-                       &this_str);
-  return this_len;
-}
-#endif
-
-bool Value::getString(char const** begin, char const** end) const {
-  if (type() != stringValue)
-    return false;
-  if (value_.string_ == nullptr)
-    return false;
-  unsigned length;
-  decodePrefixedString(this->isAllocated(), this->value_.string_, &length,
-                       begin);
-  *end = *begin + length;
-  return true;
-}
-
-String Value::asString() const {
-  switch (type()) {
-  case nullValue:
-    return "";
-  case stringValue: {
-    if (value_.string_ == nullptr)
-      return "";
-    unsigned this_len;
-    char const* this_str;
-    decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
-                         &this_str);
-    return String(this_str, this_len);
-  }
-  case booleanValue:
-    return value_.bool_ ? "true" : "false";
-  case intValue:
-    return valueToString(value_.int_);
-  case uintValue:
-    return valueToString(value_.uint_);
-  case realValue:
-    return valueToString(value_.real_);
-  default:
-    JSON_FAIL_MESSAGE("Type is not convertible to string");
-  }
-}
-
-#ifdef JSON_USE_CPPTL
-CppTL::ConstString Value::asConstString() const {
-  unsigned len;
-  char const* str;
-  decodePrefixedString(isAllocated(), value_.string_, &len, &str);
-  return CppTL::ConstString(str, len);
-}
-#endif
-
-Value::Int Value::asInt() const {
-  switch (type()) {
-  case intValue:
-    JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range");
-    return Int(value_.int_);
-  case uintValue:
-    JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range");
-    return Int(value_.uint_);
-  case realValue:
-    JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt),
-                        "double out of Int range");
-    return Int(value_.real_);
-  case nullValue:
-    return 0;
-  case booleanValue:
-    return value_.bool_ ? 1 : 0;
-  default:
-    break;
-  }
-  JSON_FAIL_MESSAGE("Value is not convertible to Int.");
-}
-
-Value::UInt Value::asUInt() const {
-  switch (type()) {
-  case intValue:
-    JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range");
-    return UInt(value_.int_);
-  case uintValue:
-    JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range");
-    return UInt(value_.uint_);
-  case realValue:
-    JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt),
-                        "double out of UInt range");
-    return UInt(value_.real_);
-  case nullValue:
-    return 0;
-  case booleanValue:
-    return value_.bool_ ? 1 : 0;
-  default:
-    break;
-  }
-  JSON_FAIL_MESSAGE("Value is not convertible to UInt.");
-}
-
-#if defined(JSON_HAS_INT64)
-
-Value::Int64 Value::asInt64() const {
-  switch (type()) {
-  case intValue:
-    return Int64(value_.int_);
-  case uintValue:
-    JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range");
-    return Int64(value_.uint_);
-  case realValue:
-    JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64),
-                        "double out of Int64 range");
-    return Int64(value_.real_);
-  case nullValue:
-    return 0;
-  case booleanValue:
-    return value_.bool_ ? 1 : 0;
-  default:
-    break;
-  }
-  JSON_FAIL_MESSAGE("Value is not convertible to Int64.");
-}
-
-Value::UInt64 Value::asUInt64() const {
-  switch (type()) {
-  case intValue:
-    JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range");
-    return UInt64(value_.int_);
-  case uintValue:
-    return UInt64(value_.uint_);
-  case realValue:
-    JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64),
-                        "double out of UInt64 range");
-    return UInt64(value_.real_);
-  case nullValue:
-    return 0;
-  case booleanValue:
-    return value_.bool_ ? 1 : 0;
-  default:
-    break;
-  }
-  JSON_FAIL_MESSAGE("Value is not convertible to UInt64.");
-}
-#endif // if defined(JSON_HAS_INT64)
-
-LargestInt Value::asLargestInt() const {
-#if defined(JSON_NO_INT64)
-  return asInt();
-#else
-  return asInt64();
-#endif
-}
-
-LargestUInt Value::asLargestUInt() const {
-#if defined(JSON_NO_INT64)
-  return asUInt();
-#else
-  return asUInt64();
-#endif
-}
-
-double Value::asDouble() const {
-  switch (type()) {
-  case intValue:
-    return static_cast<double>(value_.int_);
-  case uintValue:
-#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-    return static_cast<double>(value_.uint_);
-#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-    return integerToDouble(value_.uint_);
-#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-  case realValue:
-    return value_.real_;
-  case nullValue:
-    return 0.0;
-  case booleanValue:
-    return value_.bool_ ? 1.0 : 0.0;
-  default:
-    break;
-  }
-  JSON_FAIL_MESSAGE("Value is not convertible to double.");
-}
-
-float Value::asFloat() const {
-  switch (type()) {
-  case intValue:
-    return static_cast<float>(value_.int_);
-  case uintValue:
-#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-    return static_cast<float>(value_.uint_);
-#else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-    // This can fail (silently?) if the value is bigger than MAX_FLOAT.
-    return static_cast<float>(integerToDouble(value_.uint_));
-#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-  case realValue:
-    return static_cast<float>(value_.real_);
-  case nullValue:
-    return 0.0;
-  case booleanValue:
-    return value_.bool_ ? 1.0f : 0.0f;
-  default:
-    break;
-  }
-  JSON_FAIL_MESSAGE("Value is not convertible to float.");
-}
-
-bool Value::asBool() const {
-  switch (type()) {
-  case booleanValue:
-    return value_.bool_;
-  case nullValue:
-    return false;
-  case intValue:
-    return value_.int_ ? true : false;
-  case uintValue:
-    return value_.uint_ ? true : false;
-  case realValue:
-    // This is kind of strange. Not recommended.
-    return (value_.real_ != 0.0) ? true : false;
-  default:
-    break;
-  }
-  JSON_FAIL_MESSAGE("Value is not convertible to bool.");
-}
-
-bool Value::isConvertibleTo(ValueType other) const {
-  switch (other) {
-  case nullValue:
-    return (isNumeric() && asDouble() == 0.0) ||
-           (type() == booleanValue && value_.bool_ == false) ||
-           (type() == stringValue && asString().empty()) ||
-           (type() == arrayValue && value_.map_->empty()) ||
-           (type() == objectValue && value_.map_->empty()) ||
-           type() == nullValue;
-  case intValue:
-    return isInt() ||
-           (type() == realValue && InRange(value_.real_, minInt, maxInt)) ||
-           type() == booleanValue || type() == nullValue;
-  case uintValue:
-    return isUInt() ||
-           (type() == realValue && InRange(value_.real_, 0, maxUInt)) ||
-           type() == booleanValue || type() == nullValue;
-  case realValue:
-    return isNumeric() || type() == booleanValue || type() == nullValue;
-  case booleanValue:
-    return isNumeric() || type() == booleanValue || type() == nullValue;
-  case stringValue:
-    return isNumeric() || type() == booleanValue || type() == stringValue ||
-           type() == nullValue;
-  case arrayValue:
-    return type() == arrayValue || type() == nullValue;
-  case objectValue:
-    return type() == objectValue || type() == nullValue;
-  }
-  JSON_ASSERT_UNREACHABLE;
-  return false;
-}
-
-/// Number of values in array or object
-ArrayIndex Value::size() const {
-  switch (type()) {
-  case nullValue:
-  case intValue:
-  case uintValue:
-  case realValue:
-  case booleanValue:
-  case stringValue:
-    return 0;
-  case arrayValue: // size of the array is highest index + 1
-    if (!value_.map_->empty()) {
-      ObjectValues::const_iterator itLast = value_.map_->end();
-      --itLast;
-      return (*itLast).first.index() + 1;
-    }
-    return 0;
-  case objectValue:
-    return ArrayIndex(value_.map_->size());
-  }
-  JSON_ASSERT_UNREACHABLE;
-  return 0; // unreachable;
-}
-
-bool Value::empty() const {
-  if (isNull() || isArray() || isObject())
-    return size() == 0u;
-  else
-    return false;
-}
-
-Value::operator bool() const { return !isNull(); }
-
-void Value::clear() {
-  JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue ||
-                          type() == objectValue,
-                      "in Json::Value::clear(): requires complex value");
-  start_ = 0;
-  limit_ = 0;
-  switch (type()) {
-  case arrayValue:
-  case objectValue:
-    value_.map_->clear();
-    break;
-  default:
-    break;
-  }
-}
-
-void Value::resize(ArrayIndex newSize) {
-  JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
-                      "in Json::Value::resize(): requires arrayValue");
-  if (type() == nullValue)
-    *this = Value(arrayValue);
-  ArrayIndex oldSize = size();
-  if (newSize == 0)
-    clear();
-  else if (newSize > oldSize)
-    this->operator[](newSize - 1);
-  else {
-    for (ArrayIndex index = newSize; index < oldSize; ++index) {
-      value_.map_->erase(index);
-    }
-    JSON_ASSERT(size() == newSize);
-  }
-}
-
-Value& Value::operator[](ArrayIndex index) {
-  JSON_ASSERT_MESSAGE(
-      type() == nullValue || type() == arrayValue,
-      "in Json::Value::operator[](ArrayIndex): requires arrayValue");
-  if (type() == nullValue)
-    *this = Value(arrayValue);
-  CZString key(index);
-  auto it = value_.map_->lower_bound(key);
-  if (it != value_.map_->end() && (*it).first == key)
-    return (*it).second;
-
-  ObjectValues::value_type defaultValue(key, nullSingleton());
-  it = value_.map_->insert(it, defaultValue);
-  return (*it).second;
-}
-
-Value& Value::operator[](int index) {
-  JSON_ASSERT_MESSAGE(
-      index >= 0,
-      "in Json::Value::operator[](int index): index cannot be negative");
-  return (*this)[ArrayIndex(index)];
-}
-
-const Value& Value::operator[](ArrayIndex index) const {
-  JSON_ASSERT_MESSAGE(
-      type() == nullValue || type() == arrayValue,
-      "in Json::Value::operator[](ArrayIndex)const: requires arrayValue");
-  if (type() == nullValue)
-    return nullSingleton();
-  CZString key(index);
-  ObjectValues::const_iterator it = value_.map_->find(key);
-  if (it == value_.map_->end())
-    return nullSingleton();
-  return (*it).second;
-}
-
-const Value& Value::operator[](int index) const {
-  JSON_ASSERT_MESSAGE(
-      index >= 0,
-      "in Json::Value::operator[](int index) const: index cannot be negative");
-  return (*this)[ArrayIndex(index)];
-}
-
-void Value::initBasic(ValueType type, bool allocated) {
-  setType(type);
-  setIsAllocated(allocated);
-  comments_ = nullptr;
-  start_ = 0;
-  limit_ = 0;
-}
-
-void Value::dupPayload(const Value& other) {
-  setType(other.type());
-  setIsAllocated(false);
-  switch (type()) {
-  case nullValue:
-  case intValue:
-  case uintValue:
-  case realValue:
-  case booleanValue:
-    value_ = other.value_;
-    break;
-  case stringValue:
-    if (other.value_.string_ && other.isAllocated()) {
-      unsigned len;
-      char const* str;
-      decodePrefixedString(other.isAllocated(), other.value_.string_, &len,
-                           &str);
-      value_.string_ = duplicateAndPrefixStringValue(str, len);
-      setIsAllocated(true);
-    } else {
-      value_.string_ = other.value_.string_;
-    }
-    break;
-  case arrayValue:
-  case objectValue:
-    value_.map_ = new ObjectValues(*other.value_.map_);
-    break;
-  default:
-    JSON_ASSERT_UNREACHABLE;
-  }
-}
-
-void Value::releasePayload() {
-  switch (type()) {
-  case nullValue:
-  case intValue:
-  case uintValue:
-  case realValue:
-  case booleanValue:
-    break;
-  case stringValue:
-    if (isAllocated())
-      releasePrefixedStringValue(value_.string_);
-    break;
-  case arrayValue:
-  case objectValue:
-    delete value_.map_;
-    break;
-  default:
-    JSON_ASSERT_UNREACHABLE;
-  }
-}
-
-void Value::dupMeta(const Value& other) {
-  if (other.comments_) {
-    comments_ = new CommentInfo[numberOfCommentPlacement];
-    for (int comment = 0; comment < numberOfCommentPlacement; ++comment) {
-      const CommentInfo& otherComment = other.comments_[comment];
-      if (otherComment.comment_)
-        comments_[comment].setComment(otherComment.comment_,
-                                      strlen(otherComment.comment_));
-    }
-  } else {
-    comments_ = nullptr;
-  }
-  start_ = other.start_;
-  limit_ = other.limit_;
-}
-
-// Access an object value by name, create a null member if it does not exist.
-// @pre Type of '*this' is object or null.
-// @param key is null-terminated.
-Value& Value::resolveReference(const char* key) {
-  JSON_ASSERT_MESSAGE(
-      type() == nullValue || type() == objectValue,
-      "in Json::Value::resolveReference(): requires objectValue");
-  if (type() == nullValue)
-    *this = Value(objectValue);
-  CZString actualKey(key, static_cast<unsigned>(strlen(key)),
-                     CZString::noDuplication); // NOTE!
-  auto it = value_.map_->lower_bound(actualKey);
-  if (it != value_.map_->end() && (*it).first == actualKey)
-    return (*it).second;
-
-  ObjectValues::value_type defaultValue(actualKey, nullSingleton());
-  it = value_.map_->insert(it, defaultValue);
-  Value& value = (*it).second;
-  return value;
-}
-
-// @param key is not null-terminated.
-Value& Value::resolveReference(char const* key, char const* end) {
-  JSON_ASSERT_MESSAGE(
-      type() == nullValue || type() == objectValue,
-      "in Json::Value::resolveReference(key, end): requires objectValue");
-  if (type() == nullValue)
-    *this = Value(objectValue);
-  CZString actualKey(key, static_cast<unsigned>(end - key),
-                     CZString::duplicateOnCopy);
-  auto it = value_.map_->lower_bound(actualKey);
-  if (it != value_.map_->end() && (*it).first == actualKey)
-    return (*it).second;
-
-  ObjectValues::value_type defaultValue(actualKey, nullSingleton());
-  it = value_.map_->insert(it, defaultValue);
-  Value& value = (*it).second;
-  return value;
-}
-
-Value Value::get(ArrayIndex index, const Value& defaultValue) const {
-  const Value* value = &((*this)[index]);
-  return value == &nullSingleton() ? defaultValue : *value;
-}
-
-bool Value::isValidIndex(ArrayIndex index) const { return index < size(); }
-
-Value const* Value::find(char const* begin, char const* end) const {
-  JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
-                      "in Json::Value::find(key, end, found): requires "
-                      "objectValue or nullValue");
-  if (type() == nullValue)
-    return nullptr;
-  CZString actualKey(begin, static_cast<unsigned>(end - begin),
-                     CZString::noDuplication);
-  ObjectValues::const_iterator it = value_.map_->find(actualKey);
-  if (it == value_.map_->end())
-    return nullptr;
-  return &(*it).second;
-}
-const Value& Value::operator[](const char* key) const {
-  Value const* found = find(key, key + strlen(key));
-  if (!found)
-    return nullSingleton();
-  return *found;
-}
-Value const& Value::operator[](const String& key) const {
-  Value const* found = find(key.data(), key.data() + key.length());
-  if (!found)
-    return nullSingleton();
-  return *found;
-}
-
-Value& Value::operator[](const char* key) {
-  return resolveReference(key, key + strlen(key));
-}
-
-Value& Value::operator[](const String& key) {
-  return resolveReference(key.data(), key.data() + key.length());
-}
-
-Value& Value::operator[](const StaticString& key) {
-  return resolveReference(key.c_str());
-}
-
-#ifdef JSON_USE_CPPTL
-Value& Value::operator[](const CppTL::ConstString& key) {
-  return resolveReference(key.c_str(), key.end_c_str());
-}
-Value const& Value::operator[](CppTL::ConstString const& key) const {
-  Value const* found = find(key.c_str(), key.end_c_str());
-  if (!found)
-    return nullSingleton();
-  return *found;
-}
-#endif
-
-Value& Value::append(const Value& value) { return (*this)[size()] = value; }
-
-#if JSON_HAS_RVALUE_REFERENCES
-Value& Value::append(Value&& value) {
-  return (*this)[size()] = std::move(value);
-}
-#endif
-
-Value Value::get(char const* begin,
-                 char const* end,
-                 Value const& defaultValue) const {
-  Value const* found = find(begin, end);
-  return !found ? defaultValue : *found;
-}
-Value Value::get(char const* key, Value const& defaultValue) const {
-  return get(key, key + strlen(key), defaultValue);
-}
-Value Value::get(String const& key, Value const& defaultValue) const {
-  return get(key.data(), key.data() + key.length(), defaultValue);
-}
-
-bool Value::removeMember(const char* begin, const char* end, Value* removed) {
-  if (type() != objectValue) {
-    return false;
-  }
-  CZString actualKey(begin, static_cast<unsigned>(end - begin),
-                     CZString::noDuplication);
-  auto it = value_.map_->find(actualKey);
-  if (it == value_.map_->end())
-    return false;
-  if (removed)
-#if JSON_HAS_RVALUE_REFERENCES
-    *removed = std::move(it->second);
-#else
-    *removed = it->second;
-#endif
-  value_.map_->erase(it);
-  return true;
-}
-bool Value::removeMember(const char* key, Value* removed) {
-  return removeMember(key, key + strlen(key), removed);
-}
-bool Value::removeMember(String const& key, Value* removed) {
-  return removeMember(key.data(), key.data() + key.length(), removed);
-}
-void Value::removeMember(const char* key) {
-  JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
-                      "in Json::Value::removeMember(): requires objectValue");
-  if (type() == nullValue)
-    return;
-
-  CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication);
-  value_.map_->erase(actualKey);
-}
-void Value::removeMember(const String& key) { removeMember(key.c_str()); }
-
-bool Value::removeIndex(ArrayIndex index, Value* removed) {
-  if (type() != arrayValue) {
-    return false;
-  }
-  CZString key(index);
-  auto it = value_.map_->find(key);
-  if (it == value_.map_->end()) {
-    return false;
-  }
-  if (removed)
-    *removed = it->second;
-  ArrayIndex oldSize = size();
-  // shift left all items left, into the place of the "removed"
-  for (ArrayIndex i = index; i < (oldSize - 1); ++i) {
-    CZString keey(i);
-    (*value_.map_)[keey] = (*this)[i + 1];
-  }
-  // erase the last one ("leftover")
-  CZString keyLast(oldSize - 1);
-  auto itLast = value_.map_->find(keyLast);
-  value_.map_->erase(itLast);
-  return true;
-}
-
-#ifdef JSON_USE_CPPTL
-Value Value::get(const CppTL::ConstString& key,
-                 const Value& defaultValue) const {
-  return get(key.c_str(), key.end_c_str(), defaultValue);
-}
-#endif
-
-bool Value::isMember(char const* begin, char const* end) const {
-  Value const* value = find(begin, end);
-  return nullptr != value;
-}
-bool Value::isMember(char const* key) const {
-  return isMember(key, key + strlen(key));
-}
-bool Value::isMember(String const& key) const {
-  return isMember(key.data(), key.data() + key.length());
-}
-
-#ifdef JSON_USE_CPPTL
-bool Value::isMember(const CppTL::ConstString& key) const {
-  return isMember(key.c_str(), key.end_c_str());
-}
-#endif
-
-Value::Members Value::getMemberNames() const {
-  JSON_ASSERT_MESSAGE(
-      type() == nullValue || type() == objectValue,
-      "in Json::Value::getMemberNames(), value must be objectValue");
-  if (type() == nullValue)
-    return Value::Members();
-  Members members;
-  members.reserve(value_.map_->size());
-  ObjectValues::const_iterator it = value_.map_->begin();
-  ObjectValues::const_iterator itEnd = value_.map_->end();
-  for (; it != itEnd; ++it) {
-    members.push_back(String((*it).first.data(), (*it).first.length()));
-  }
-  return members;
-}
-//
-//# ifdef JSON_USE_CPPTL
-// EnumMemberNames
-// Value::enumMemberNames() const
-//{
-//   if ( type() == objectValue )
-//   {
-//      return CppTL::Enum::any(  CppTL::Enum::transform(
-//         CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ),
-//         MemberNamesTransform() ) );
-//   }
-//   return EnumMemberNames();
-//}
-//
-//
-// EnumValues
-// Value::enumValues() const
-//{
-//   if ( type() == objectValue  ||  type() == arrayValue )
-//      return CppTL::Enum::anyValues( *(value_.map_),
-//                                     CppTL::Type<const Value &>() );
-//   return EnumValues();
-//}
-//
-//# endif
-
-static bool IsIntegral(double d) {
-  double integral_part;
-  return modf(d, &integral_part) == 0.0;
-}
-
-bool Value::isNull() const { return type() == nullValue; }
-
-bool Value::isBool() const { return type() == booleanValue; }
-
-bool Value::isInt() const {
-  switch (type()) {
-  case intValue:
-#if defined(JSON_HAS_INT64)
-    return value_.int_ >= minInt && value_.int_ <= maxInt;
-#else
-    return true;
-#endif
-  case uintValue:
-    return value_.uint_ <= UInt(maxInt);
-  case realValue:
-    return value_.real_ >= minInt && value_.real_ <= maxInt &&
-           IsIntegral(value_.real_);
-  default:
-    break;
-  }
-  return false;
-}
-
-bool Value::isUInt() const {
-  switch (type()) {
-  case intValue:
-#if defined(JSON_HAS_INT64)
-    return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt);
-#else
-    return value_.int_ >= 0;
-#endif
-  case uintValue:
-#if defined(JSON_HAS_INT64)
-    return value_.uint_ <= maxUInt;
-#else
-    return true;
-#endif
-  case realValue:
-    return value_.real_ >= 0 && value_.real_ <= maxUInt &&
-           IsIntegral(value_.real_);
-  default:
-    break;
-  }
-  return false;
-}
-
-bool Value::isInt64() const {
-#if defined(JSON_HAS_INT64)
-  switch (type()) {
-  case intValue:
-    return true;
-  case uintValue:
-    return value_.uint_ <= UInt64(maxInt64);
-  case realValue:
-    // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a
-    // double, so double(maxInt64) will be rounded up to 2^63. Therefore we
-    // require the value to be strictly less than the limit.
-    return value_.real_ >= double(minInt64) &&
-           value_.real_ < double(maxInt64) && IsIntegral(value_.real_);
-  default:
-    break;
-  }
-#endif // JSON_HAS_INT64
-  return false;
-}
-
-bool Value::isUInt64() const {
-#if defined(JSON_HAS_INT64)
-  switch (type()) {
-  case intValue:
-    return value_.int_ >= 0;
-  case uintValue:
-    return true;
-  case realValue:
-    // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
-    // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
-    // require the value to be strictly less than the limit.
-    return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble &&
-           IsIntegral(value_.real_);
-  default:
-    break;
-  }
-#endif // JSON_HAS_INT64
-  return false;
-}
-
-bool Value::isIntegral() const {
-  switch (type()) {
-  case intValue:
-  case uintValue:
-    return true;
-  case realValue:
-#if defined(JSON_HAS_INT64)
-    // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
-    // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
-    // require the value to be strictly less than the limit.
-    return value_.real_ >= double(minInt64) &&
-           value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_);
-#else
-    return value_.real_ >= minInt && value_.real_ <= maxUInt &&
-           IsIntegral(value_.real_);
-#endif // JSON_HAS_INT64
-  default:
-    break;
-  }
-  return false;
-}
-
-bool Value::isDouble() const {
-  return type() == intValue || type() == uintValue || type() == realValue;
-}
-
-bool Value::isNumeric() const { return isDouble(); }
-
-bool Value::isString() const { return type() == stringValue; }
-
-bool Value::isArray() const { return type() == arrayValue; }
-
-bool Value::isObject() const { return type() == objectValue; }
-
-void Value::setComment(const char* comment,
-                       size_t len,
-                       CommentPlacement placement) {
-  if (!comments_)
-    comments_ = new CommentInfo[numberOfCommentPlacement];
-  if ((len > 0) && (comment[len - 1] == '\n')) {
-    // Always discard trailing newline, to aid indentation.
-    len -= 1;
-  }
-  comments_[placement].setComment(comment, len);
-}
-
-void Value::setComment(const char* comment, CommentPlacement placement) {
-  setComment(comment, strlen(comment), placement);
-}
-
-void Value::setComment(const String& comment, CommentPlacement placement) {
-  setComment(comment.c_str(), comment.length(), placement);
-}
-
-bool Value::hasComment(CommentPlacement placement) const {
-  return comments_ != nullptr && comments_[placement].comment_ != nullptr;
-}
-
-String Value::getComment(CommentPlacement placement) const {
-  if (hasComment(placement))
-    return comments_[placement].comment_;
-  return "";
-}
-
-void Value::setOffsetStart(ptrdiff_t start) { start_ = start; }
-
-void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; }
-
-ptrdiff_t Value::getOffsetStart() const { return start_; }
-
-ptrdiff_t Value::getOffsetLimit() const { return limit_; }
-
-String Value::toStyledString() const {
-  StreamWriterBuilder builder;
-
-  String out = this->hasComment(commentBefore) ? "\n" : "";
-  out += Json::writeString(builder, *this);
-  out += '\n';
-
-  return out;
-}
-
-Value::const_iterator Value::begin() const {
-  switch (type()) {
-  case arrayValue:
-  case objectValue:
-    if (value_.map_)
-      return const_iterator(value_.map_->begin());
-    break;
-  default:
-    break;
-  }
-  return {};
-}
-
-Value::const_iterator Value::end() const {
-  switch (type()) {
-  case arrayValue:
-  case objectValue:
-    if (value_.map_)
-      return const_iterator(value_.map_->end());
-    break;
-  default:
-    break;
-  }
-  return {};
-}
-
-Value::iterator Value::begin() {
-  switch (type()) {
-  case arrayValue:
-  case objectValue:
-    if (value_.map_)
-      return iterator(value_.map_->begin());
-    break;
-  default:
-    break;
-  }
-  return iterator();
-}
-
-Value::iterator Value::end() {
-  switch (type()) {
-  case arrayValue:
-  case objectValue:
-    if (value_.map_)
-      return iterator(value_.map_->end());
-    break;
-  default:
-    break;
-  }
-  return iterator();
-}
-
-// class PathArgument
-// //////////////////////////////////////////////////////////////////
-
-PathArgument::PathArgument() : key_() {}
-
-PathArgument::PathArgument(ArrayIndex index)
-    : key_(), index_(index), kind_(kindIndex) {}
-
-PathArgument::PathArgument(const char* key)
-    : key_(key), index_(), kind_(kindKey) {}
-
-PathArgument::PathArgument(const String& key)
-    : key_(key.c_str()), index_(), kind_(kindKey) {}
-
-// class Path
-// //////////////////////////////////////////////////////////////////
-
-Path::Path(const String& path,
-           const PathArgument& a1,
-           const PathArgument& a2,
-           const PathArgument& a3,
-           const PathArgument& a4,
-           const PathArgument& a5) {
-  InArgs in;
-  in.reserve(5);
-  in.push_back(&a1);
-  in.push_back(&a2);
-  in.push_back(&a3);
-  in.push_back(&a4);
-  in.push_back(&a5);
-  makePath(path, in);
-}
-
-void Path::makePath(const String& path, const InArgs& in) {
-  const char* current = path.c_str();
-  const char* end = current + path.length();
-  auto itInArg = in.begin();
-  while (current != end) {
-    if (*current == '[') {
-      ++current;
-      if (*current == '%')
-        addPathInArg(path, in, itInArg, PathArgument::kindIndex);
-      else {
-        ArrayIndex index = 0;
-        for (; current != end && *current >= '0' && *current <= '9'; ++current)
-          index = index * 10 + ArrayIndex(*current - '0');
-        args_.push_back(index);
-      }
-      if (current == end || *++current != ']')
-        invalidPath(path, int(current - path.c_str()));
-    } else if (*current == '%') {
-      addPathInArg(path, in, itInArg, PathArgument::kindKey);
-      ++current;
-    } else if (*current == '.' || *current == ']') {
-      ++current;
-    } else {
-      const char* beginName = current;
-      while (current != end && !strchr("[.", *current))
-        ++current;
-      args_.push_back(String(beginName, current));
-    }
-  }
-}
-
-void Path::addPathInArg(const String& /*path*/,
-                        const InArgs& in,
-                        InArgs::const_iterator& itInArg,
-                        PathArgument::Kind kind) {
-  if (itInArg == in.end()) {
-    // Error: missing argument %d
-  } else if ((*itInArg)->kind_ != kind) {
-    // Error: bad argument type
-  } else {
-    args_.push_back(**itInArg++);
-  }
-}
-
-void Path::invalidPath(const String& /*path*/, int /*location*/) {
-  // Error: invalid path.
-}
-
-const Value& Path::resolve(const Value& root) const {
-  const Value* node = &root;
-  for (const auto& arg : args_) {
-    if (arg.kind_ == PathArgument::kindIndex) {
-      if (!node->isArray() || !node->isValidIndex(arg.index_)) {
-        // Error: unable to resolve path (array value expected at position...
-        return Value::null;
-      }
-      node = &((*node)[arg.index_]);
-    } else if (arg.kind_ == PathArgument::kindKey) {
-      if (!node->isObject()) {
-        // Error: unable to resolve path (object value expected at position...)
-        return Value::null;
-      }
-      node = &((*node)[arg.key_]);
-      if (node == &Value::nullSingleton()) {
-        // Error: unable to resolve path (object has no member named '' at
-        // position...)
-        return Value::null;
-      }
-    }
-  }
-  return *node;
-}
-
-Value Path::resolve(const Value& root, const Value& defaultValue) const {
-  const Value* node = &root;
-  for (const auto& arg : args_) {
-    if (arg.kind_ == PathArgument::kindIndex) {
-      if (!node->isArray() || !node->isValidIndex(arg.index_))
-        return defaultValue;
-      node = &((*node)[arg.index_]);
-    } else if (arg.kind_ == PathArgument::kindKey) {
-      if (!node->isObject())
-        return defaultValue;
-      node = &((*node)[arg.key_]);
-      if (node == &Value::nullSingleton())
-        return defaultValue;
-    }
-  }
-  return *node;
-}
-
-Value& Path::make(Value& root) const {
-  Value* node = &root;
-  for (const auto& arg : args_) {
-    if (arg.kind_ == PathArgument::kindIndex) {
-      if (!node->isArray()) {
-        // Error: node is not an array at position ...
-      }
-      node = &((*node)[arg.index_]);
-    } else if (arg.kind_ == PathArgument::kindKey) {
-      if (!node->isObject()) {
-        // Error: node is not an object at position...
-      }
-      node = &((*node)[arg.key_]);
-    }
-  }
-  return *node;
-}
-
-} // namespace Json
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: src/lib_json/json_value.cpp
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
-
-// //////////////////////////////////////////////////////////////////////
-// Beginning of content of file: src/lib_json/json_writer.cpp
-// //////////////////////////////////////////////////////////////////////
-
-// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#if !defined(JSON_IS_AMALGAMATION)
-#include "json_tool.h"
-#include <json/writer.h>
-#endif // if !defined(JSON_IS_AMALGAMATION)
-#include <cassert>
-#include <cstring>
-#include <iomanip>
-#include <memory>
-#include <set>
-#include <sstream>
-#include <utility>
-
-#if __cplusplus >= 201103L
-#include <cmath>
-#include <cstdio>
-
-#if !defined(isnan)
-#define isnan std::isnan
-#endif
-
-#if !defined(isfinite)
-#define isfinite std::isfinite
-#endif
-
-#else
-#include <cmath>
-#include <cstdio>
-
-#if defined(_MSC_VER)
-#if !defined(isnan)
-#include <float.h>
-#define isnan _isnan
-#endif
-
-#if !defined(isfinite)
-#include <float.h>
-#define isfinite _finite
-#endif
-
-#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
-#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
-#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
-
-#endif //_MSC_VER
-
-#if defined(__sun) && defined(__SVR4) // Solaris
-#if !defined(isfinite)
-#include <ieeefp.h>
-#define isfinite finite
-#endif
-#endif
-
-#if defined(__hpux)
-#if !defined(isfinite)
-#if defined(__ia64) && !defined(finite)
-#define isfinite(x)                                                            \
-  ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x)))
-#endif
-#endif
-#endif
-
-#if !defined(isnan)
-// IEEE standard states that NaN values will not compare to themselves
-#define isnan(x) (x != x)
-#endif
-
-#if !defined(__APPLE__)
-#if !defined(isfinite)
-#define isfinite finite
-#endif
-#endif
-#endif
-
-#if defined(_MSC_VER)
-// Disable warning about strdup being deprecated.
-#pragma warning(disable : 4996)
-#endif
-
-namespace Json {
-
-#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
-typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
-#else
-typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
-#endif
-
-String valueToString(LargestInt value) {
-  UIntToStringBuffer buffer;
-  char* current = buffer + sizeof(buffer);
-  if (value == Value::minLargestInt) {
-    uintToString(LargestUInt(Value::maxLargestInt) + 1, current);
-    *--current = '-';
-  } else if (value < 0) {
-    uintToString(LargestUInt(-value), current);
-    *--current = '-';
-  } else {
-    uintToString(LargestUInt(value), current);
-  }
-  assert(current >= buffer);
-  return current;
-}
-
-String valueToString(LargestUInt value) {
-  UIntToStringBuffer buffer;
-  char* current = buffer + sizeof(buffer);
-  uintToString(value, current);
-  assert(current >= buffer);
-  return current;
-}
-
-#if defined(JSON_HAS_INT64)
-
-String valueToString(Int value) { return valueToString(LargestInt(value)); }
-
-String valueToString(UInt value) { return valueToString(LargestUInt(value)); }
-
-#endif // # if defined(JSON_HAS_INT64)
-
-namespace {
-String valueToString(double value,
-                     bool useSpecialFloats,
-                     unsigned int precision,
-                     PrecisionType precisionType) {
-  // Print into the buffer. We need not request the alternative representation
-  // that always has a decimal point because JSON doesn't distinguish the
-  // concepts of reals and integers.
-  if (!isfinite(value)) {
-    static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"},
-                                           {"null", "-1e+9999", "1e+9999"}};
-    return reps[useSpecialFloats ? 0 : 1]
-               [isnan(value) ? 0 : (value < 0) ? 1 : 2];
-  }
-
-  String buffer(size_t(36), '\0');
-  while (true) {
-    int len = jsoncpp_snprintf(
-        &*buffer.begin(), buffer.size(),
-        (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f",
-        precision, value);
-    assert(len >= 0);
-    auto wouldPrint = static_cast<size_t>(len);
-    if (wouldPrint >= buffer.size()) {
-      buffer.resize(wouldPrint + 1);
-      continue;
-    }
-    buffer.resize(wouldPrint);
-    break;
-  }
-
-  buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());
-
-  // strip the zero padding from the right
-  if (precisionType == PrecisionType::decimalPlaces) {
-    buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end()), buffer.end());
-  }
-
-  // try to ensure we preserve the fact that this was given to us as a double on
-  // input
-  if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {
-    buffer += ".0";
-  }
-  return buffer;
-}
-} // namespace
-
-String valueToString(double value,
-                     unsigned int precision,
-                     PrecisionType precisionType) {
-  return valueToString(value, false, precision, precisionType);
-}
-
-String valueToString(bool value) { return value ? "true" : "false"; }
-
-static bool isAnyCharRequiredQuoting(char const* s, size_t n) {
-  assert(s || !n);
-
-  char const* const end = s + n;
-  for (char const* cur = s; cur < end; ++cur) {
-    if (*cur == '\\' || *cur == '\"' || *cur < ' ' ||
-        static_cast<unsigned char>(*cur) < 0x80)
-      return true;
-  }
-  return false;
-}
-
-static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
-  const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;
-
-  unsigned int firstByte = static_cast<unsigned char>(*s);
-
-  if (firstByte < 0x80)
-    return firstByte;
-
-  if (firstByte < 0xE0) {
-    if (e - s < 2)
-      return REPLACEMENT_CHARACTER;
-
-    unsigned int calculated =
-        ((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F);
-    s += 1;
-    // oversized encoded characters are invalid
-    return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;
-  }
-
-  if (firstByte < 0xF0) {
-    if (e - s < 3)
-      return REPLACEMENT_CHARACTER;
-
-    unsigned int calculated = ((firstByte & 0x0F) << 12) |
-                              ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) |
-                              (static_cast<unsigned int>(s[2]) & 0x3F);
-    s += 2;
-    // surrogates aren't valid codepoints itself
-    // shouldn't be UTF-8 encoded
-    if (calculated >= 0xD800 && calculated <= 0xDFFF)
-      return REPLACEMENT_CHARACTER;
-    // oversized encoded characters are invalid
-    return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;
-  }
-
-  if (firstByte < 0xF8) {
-    if (e - s < 4)
-      return REPLACEMENT_CHARACTER;
-
-    unsigned int calculated = ((firstByte & 0x07) << 18) |
-                              ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) |
-                              ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) |
-                              (static_cast<unsigned int>(s[3]) & 0x3F);
-    s += 3;
-    // oversized encoded characters are invalid
-    return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated;
-  }
-
-  return REPLACEMENT_CHARACTER;
-}
-
-static const char hex2[] = "000102030405060708090a0b0c0d0e0f"
-                           "101112131415161718191a1b1c1d1e1f"
-                           "202122232425262728292a2b2c2d2e2f"
-                           "303132333435363738393a3b3c3d3e3f"
-                           "404142434445464748494a4b4c4d4e4f"
-                           "505152535455565758595a5b5c5d5e5f"
-                           "606162636465666768696a6b6c6d6e6f"
-                           "707172737475767778797a7b7c7d7e7f"
-                           "808182838485868788898a8b8c8d8e8f"
-                           "909192939495969798999a9b9c9d9e9f"
-                           "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
-                           "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
-                           "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
-                           "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
-                           "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
-                           "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
-
-static String toHex16Bit(unsigned int x) {
-  const unsigned int hi = (x >> 8) & 0xff;
-  const unsigned int lo = x & 0xff;
-  String result(4, ' ');
-  result[0] = hex2[2 * hi];
-  result[1] = hex2[2 * hi + 1];
-  result[2] = hex2[2 * lo];
-  result[3] = hex2[2 * lo + 1];
-  return result;
-}
-
-static String valueToQuotedStringN(const char* value, unsigned length) {
-  if (value == nullptr)
-    return "";
-
-  if (!isAnyCharRequiredQuoting(value, length))
-    return String("\"") + value + "\"";
-  // We have to walk value and escape any special characters.
-  // Appending to String is not efficient, but this should be rare.
-  // (Note: forward slashes are *not* rare, but I am not escaping them.)
-  String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL
-  String result;
-  result.reserve(maxsize); // to avoid lots of mallocs
-  result += "\"";
-  char const* end = value + length;
-  for (const char* c = value; c != end; ++c) {
-    switch (*c) {
-    case '\"':
-      result += "\\\"";
-      break;
-    case '\\':
-      result += "\\\\";
-      break;
-    case '\b':
-      result += "\\b";
-      break;
-    case '\f':
-      result += "\\f";
-      break;
-    case '\n':
-      result += "\\n";
-      break;
-    case '\r':
-      result += "\\r";
-      break;
-    case '\t':
-      result += "\\t";
-      break;
-    // case '/':
-    // Even though \/ is considered a legal escape in JSON, a bare
-    // slash is also legal, so I see no reason to escape it.
-    // (I hope I am not misunderstanding something.)
-    // blep notes: actually escaping \/ may be useful in javascript to avoid </
-    // sequence.
-    // Should add a flag to allow this compatibility mode and prevent this
-    // sequence from occurring.
-    default: {
-      unsigned int cp = utf8ToCodepoint(c, end);
-      // don't escape non-control characters
-      // (short escape sequence are applied above)
-      if (cp < 0x80 && cp >= 0x20)
-        result += static_cast<char>(cp);
-      else if (cp < 0x10000) { // codepoint is in Basic Multilingual Plane
-        result += "\\u";
-        result += toHex16Bit(cp);
-      } else { // codepoint is not in Basic Multilingual Plane
-               // convert to surrogate pair first
-        cp -= 0x10000;
-        result += "\\u";
-        result += toHex16Bit((cp >> 10) + 0xD800);
-        result += "\\u";
-        result += toHex16Bit((cp & 0x3FF) + 0xDC00);
-      }
-    } break;
-    }
-  }
-  result += "\"";
-  return result;
-}
-
-String valueToQuotedString(const char* value) {
-  return valueToQuotedStringN(value, static_cast<unsigned int>(strlen(value)));
-}
-
-// Class Writer
-// //////////////////////////////////////////////////////////////////
-Writer::~Writer() = default;
-
-// Class FastWriter
-// //////////////////////////////////////////////////////////////////
-
-FastWriter::FastWriter()
-
-    = default;
-
-void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
-
-void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
-
-void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
-
-String FastWriter::write(const Value& root) {
-  document_.clear();
-  writeValue(root);
-  if (!omitEndingLineFeed_)
-    document_ += '\n';
-  return document_;
-}
-
-void FastWriter::writeValue(const Value& value) {
-  switch (value.type()) {
-  case nullValue:
-    if (!dropNullPlaceholders_)
-      document_ += "null";
-    break;
-  case intValue:
-    document_ += valueToString(value.asLargestInt());
-    break;
-  case uintValue:
-    document_ += valueToString(value.asLargestUInt());
-    break;
-  case realValue:
-    document_ += valueToString(value.asDouble());
-    break;
-  case stringValue: {
-    // Is NULL possible for value.string_? No.
-    char const* str;
-    char const* end;
-    bool ok = value.getString(&str, &end);
-    if (ok)
-      document_ += valueToQuotedStringN(str, static_cast<unsigned>(end - str));
-    break;
-  }
-  case booleanValue:
-    document_ += valueToString(value.asBool());
-    break;
-  case arrayValue: {
-    document_ += '[';
-    ArrayIndex size = value.size();
-    for (ArrayIndex index = 0; index < size; ++index) {
-      if (index > 0)
-        document_ += ',';
-      writeValue(value[index]);
-    }
-    document_ += ']';
-  } break;
-  case objectValue: {
-    Value::Members members(value.getMemberNames());
-    document_ += '{';
-    for (auto it = members.begin(); it != members.end(); ++it) {
-      const String& name = *it;
-      if (it != members.begin())
-        document_ += ',';
-      document_ += valueToQuotedStringN(name.data(),
-                                        static_cast<unsigned>(name.length()));
-      document_ += yamlCompatibilityEnabled_ ? ": " : ":";
-      writeValue(value[name]);
-    }
-    document_ += '}';
-  } break;
-  }
-}
-
-// Class StyledWriter
-// //////////////////////////////////////////////////////////////////
-
-StyledWriter::StyledWriter() = default;
-
-String StyledWriter::write(const Value& root) {
-  document_.clear();
-  addChildValues_ = false;
-  indentString_.clear();
-  writeCommentBeforeValue(root);
-  writeValue(root);
-  writeCommentAfterValueOnSameLine(root);
-  document_ += '\n';
-  return document_;
-}
-
-void StyledWriter::writeValue(const Value& value) {
-  switch (value.type()) {
-  case nullValue:
-    pushValue("null");
-    break;
-  case intValue:
-    pushValue(valueToString(value.asLargestInt()));
-    break;
-  case uintValue:
-    pushValue(valueToString(value.asLargestUInt()));
-    break;
-  case realValue:
-    pushValue(valueToString(value.asDouble()));
-    break;
-  case stringValue: {
-    // Is NULL possible for value.string_? No.
-    char const* str;
-    char const* end;
-    bool ok = value.getString(&str, &end);
-    if (ok)
-      pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
-    else
-      pushValue("");
-    break;
-  }
-  case booleanValue:
-    pushValue(valueToString(value.asBool()));
-    break;
-  case arrayValue:
-    writeArrayValue(value);
-    break;
-  case objectValue: {
-    Value::Members members(value.getMemberNames());
-    if (members.empty())
-      pushValue("{}");
-    else {
-      writeWithIndent("{");
-      indent();
-      auto it = members.begin();
-      for (;;) {
-        const String& name = *it;
-        const Value& childValue = value[name];
-        writeCommentBeforeValue(childValue);
-        writeWithIndent(valueToQuotedString(name.c_str()));
-        document_ += " : ";
-        writeValue(childValue);
-        if (++it == members.end()) {
-          writeCommentAfterValueOnSameLine(childValue);
-          break;
-        }
-        document_ += ',';
-        writeCommentAfterValueOnSameLine(childValue);
-      }
-      unindent();
-      writeWithIndent("}");
-    }
-  } break;
-  }
-}
-
-void StyledWriter::writeArrayValue(const Value& value) {
-  unsigned size = value.size();
-  if (size == 0)
-    pushValue("[]");
-  else {
-    bool isArrayMultiLine = isMultilineArray(value);
-    if (isArrayMultiLine) {
-      writeWithIndent("[");
-      indent();
-      bool hasChildValue = !childValues_.empty();
-      unsigned index = 0;
-      for (;;) {
-        const Value& childValue = value[index];
-        writeCommentBeforeValue(childValue);
-        if (hasChildValue)
-          writeWithIndent(childValues_[index]);
-        else {
-          writeIndent();
-          writeValue(childValue);
-        }
-        if (++index == size) {
-          writeCommentAfterValueOnSameLine(childValue);
-          break;
-        }
-        document_ += ',';
-        writeCommentAfterValueOnSameLine(childValue);
-      }
-      unindent();
-      writeWithIndent("]");
-    } else // output on a single line
-    {
-      assert(childValues_.size() == size);
-      document_ += "[ ";
-      for (unsigned index = 0; index < size; ++index) {
-        if (index > 0)
-          document_ += ", ";
-        document_ += childValues_[index];
-      }
-      document_ += " ]";
-    }
-  }
-}
-
-bool StyledWriter::isMultilineArray(const Value& value) {
-  ArrayIndex const size = value.size();
-  bool isMultiLine = size * 3 >= rightMargin_;
-  childValues_.clear();
-  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
-    const Value& childValue = value[index];
-    isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
-                   !childValue.empty());
-  }
-  if (!isMultiLine) // check if line length > max line length
-  {
-    childValues_.reserve(size);
-    addChildValues_ = true;
-    ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
-    for (ArrayIndex index = 0; index < size; ++index) {
-      if (hasCommentForValue(value[index])) {
-        isMultiLine = true;
-      }
-      writeValue(value[index]);
-      lineLength += static_cast<ArrayIndex>(childValues_[index].length());
-    }
-    addChildValues_ = false;
-    isMultiLine = isMultiLine || lineLength >= rightMargin_;
-  }
-  return isMultiLine;
-}
-
-void StyledWriter::pushValue(const String& value) {
-  if (addChildValues_)
-    childValues_.push_back(value);
-  else
-    document_ += value;
-}
-
-void StyledWriter::writeIndent() {
-  if (!document_.empty()) {
-    char last = document_[document_.length() - 1];
-    if (last == ' ') // already indented
-      return;
-    if (last != '\n') // Comments may add new-line
-      document_ += '\n';
-  }
-  document_ += indentString_;
-}
-
-void StyledWriter::writeWithIndent(const String& value) {
-  writeIndent();
-  document_ += value;
-}
-
-void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); }
-
-void StyledWriter::unindent() {
-  assert(indentString_.size() >= indentSize_);
-  indentString_.resize(indentString_.size() - indentSize_);
-}
-
-void StyledWriter::writeCommentBeforeValue(const Value& root) {
-  if (!root.hasComment(commentBefore))
-    return;
-
-  document_ += '\n';
-  writeIndent();
-  const String& comment = root.getComment(commentBefore);
-  String::const_iterator iter = comment.begin();
-  while (iter != comment.end()) {
-    document_ += *iter;
-    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
-      writeIndent();
-    ++iter;
-  }
-
-  // Comments are stripped of trailing newlines, so add one here
-  document_ += '\n';
-}
-
-void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
-  if (root.hasComment(commentAfterOnSameLine))
-    document_ += " " + root.getComment(commentAfterOnSameLine);
-
-  if (root.hasComment(commentAfter)) {
-    document_ += '\n';
-    document_ += root.getComment(commentAfter);
-    document_ += '\n';
-  }
-}
-
-bool StyledWriter::hasCommentForValue(const Value& value) {
-  return value.hasComment(commentBefore) ||
-         value.hasComment(commentAfterOnSameLine) ||
-         value.hasComment(commentAfter);
-}
-
-// Class StyledStreamWriter
-// //////////////////////////////////////////////////////////////////
-
-StyledStreamWriter::StyledStreamWriter(String indentation)
-    : document_(nullptr), indentation_(std::move(indentation)),
-      addChildValues_(), indented_(false) {}
-
-void StyledStreamWriter::write(OStream& out, const Value& root) {
-  document_ = &out;
-  addChildValues_ = false;
-  indentString_.clear();
-  indented_ = true;
-  writeCommentBeforeValue(root);
-  if (!indented_)
-    writeIndent();
-  indented_ = true;
-  writeValue(root);
-  writeCommentAfterValueOnSameLine(root);
-  *document_ << "\n";
-  document_ = nullptr; // Forget the stream, for safety.
-}
-
-void StyledStreamWriter::writeValue(const Value& value) {
-  switch (value.type()) {
-  case nullValue:
-    pushValue("null");
-    break;
-  case intValue:
-    pushValue(valueToString(value.asLargestInt()));
-    break;
-  case uintValue:
-    pushValue(valueToString(value.asLargestUInt()));
-    break;
-  case realValue:
-    pushValue(valueToString(value.asDouble()));
-    break;
-  case stringValue: {
-    // Is NULL possible for value.string_? No.
-    char const* str;
-    char const* end;
-    bool ok = value.getString(&str, &end);
-    if (ok)
-      pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
-    else
-      pushValue("");
-    break;
-  }
-  case booleanValue:
-    pushValue(valueToString(value.asBool()));
-    break;
-  case arrayValue:
-    writeArrayValue(value);
-    break;
-  case objectValue: {
-    Value::Members members(value.getMemberNames());
-    if (members.empty())
-      pushValue("{}");
-    else {
-      writeWithIndent("{");
-      indent();
-      auto it = members.begin();
-      for (;;) {
-        const String& name = *it;
-        const Value& childValue = value[name];
-        writeCommentBeforeValue(childValue);
-        writeWithIndent(valueToQuotedString(name.c_str()));
-        *document_ << " : ";
-        writeValue(childValue);
-        if (++it == members.end()) {
-          writeCommentAfterValueOnSameLine(childValue);
-          break;
-        }
-        *document_ << ",";
-        writeCommentAfterValueOnSameLine(childValue);
-      }
-      unindent();
-      writeWithIndent("}");
-    }
-  } break;
-  }
-}
-
-void StyledStreamWriter::writeArrayValue(const Value& value) {
-  unsigned size = value.size();
-  if (size == 0)
-    pushValue("[]");
-  else {
-    bool isArrayMultiLine = isMultilineArray(value);
-    if (isArrayMultiLine) {
-      writeWithIndent("[");
-      indent();
-      bool hasChildValue = !childValues_.empty();
-      unsigned index = 0;
-      for (;;) {
-        const Value& childValue = value[index];
-        writeCommentBeforeValue(childValue);
-        if (hasChildValue)
-          writeWithIndent(childValues_[index]);
-        else {
-          if (!indented_)
-            writeIndent();
-          indented_ = true;
-          writeValue(childValue);
-          indented_ = false;
-        }
-        if (++index == size) {
-          writeCommentAfterValueOnSameLine(childValue);
-          break;
-        }
-        *document_ << ",";
-        writeCommentAfterValueOnSameLine(childValue);
-      }
-      unindent();
-      writeWithIndent("]");
-    } else // output on a single line
-    {
-      assert(childValues_.size() == size);
-      *document_ << "[ ";
-      for (unsigned index = 0; index < size; ++index) {
-        if (index > 0)
-          *document_ << ", ";
-        *document_ << childValues_[index];
-      }
-      *document_ << " ]";
-    }
-  }
-}
-
-bool StyledStreamWriter::isMultilineArray(const Value& value) {
-  ArrayIndex const size = value.size();
-  bool isMultiLine = size * 3 >= rightMargin_;
-  childValues_.clear();
-  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
-    const Value& childValue = value[index];
-    isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
-                   !childValue.empty());
-  }
-  if (!isMultiLine) // check if line length > max line length
-  {
-    childValues_.reserve(size);
-    addChildValues_ = true;
-    ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
-    for (ArrayIndex index = 0; index < size; ++index) {
-      if (hasCommentForValue(value[index])) {
-        isMultiLine = true;
-      }
-      writeValue(value[index]);
-      lineLength += static_cast<ArrayIndex>(childValues_[index].length());
-    }
-    addChildValues_ = false;
-    isMultiLine = isMultiLine || lineLength >= rightMargin_;
-  }
-  return isMultiLine;
-}
-
-void StyledStreamWriter::pushValue(const String& value) {
-  if (addChildValues_)
-    childValues_.push_back(value);
-  else
-    *document_ << value;
-}
-
-void StyledStreamWriter::writeIndent() {
-  // blep intended this to look at the so-far-written string
-  // to determine whether we are already indented, but
-  // with a stream we cannot do that. So we rely on some saved state.
-  // The caller checks indented_.
-  *document_ << '\n' << indentString_;
-}
-
-void StyledStreamWriter::writeWithIndent(const String& value) {
-  if (!indented_)
-    writeIndent();
-  *document_ << value;
-  indented_ = false;
-}
-
-void StyledStreamWriter::indent() { indentString_ += indentation_; }
-
-void StyledStreamWriter::unindent() {
-  assert(indentString_.size() >= indentation_.size());
-  indentString_.resize(indentString_.size() - indentation_.size());
-}
-
-void StyledStreamWriter::writeCommentBeforeValue(const Value& root) {
-  if (!root.hasComment(commentBefore))
-    return;
-
-  if (!indented_)
-    writeIndent();
-  const String& comment = root.getComment(commentBefore);
-  String::const_iterator iter = comment.begin();
-  while (iter != comment.end()) {
-    *document_ << *iter;
-    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
-      // writeIndent();  // would include newline
-      *document_ << indentString_;
-    ++iter;
-  }
-  indented_ = false;
-}
-
-void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) {
-  if (root.hasComment(commentAfterOnSameLine))
-    *document_ << ' ' << root.getComment(commentAfterOnSameLine);
-
-  if (root.hasComment(commentAfter)) {
-    writeIndent();
-    *document_ << root.getComment(commentAfter);
-  }
-  indented_ = false;
-}
-
-bool StyledStreamWriter::hasCommentForValue(const Value& value) {
-  return value.hasComment(commentBefore) ||
-         value.hasComment(commentAfterOnSameLine) ||
-         value.hasComment(commentAfter);
-}
-
-//////////////////////////
-// BuiltStyledStreamWriter
-
-/// Scoped enums are not available until C++11.
-struct CommentStyle {
-  /// Decide whether to write comments.
-  enum Enum {
-    None, ///< Drop all comments.
-    Most, ///< Recover odd behavior of previous versions (not implemented yet).
-    All   ///< Keep all comments.
-  };
-};
-
-struct BuiltStyledStreamWriter : public StreamWriter {
-  BuiltStyledStreamWriter(String indentation,
-                          CommentStyle::Enum cs,
-                          String colonSymbol,
-                          String nullSymbol,
-                          String endingLineFeedSymbol,
-                          bool useSpecialFloats,
-                          unsigned int precision,
-                          PrecisionType precisionType);
-  int write(Value const& root, OStream* sout) override;
-
-private:
-  void writeValue(Value const& value);
-  void writeArrayValue(Value const& value);
-  bool isMultilineArray(Value const& value);
-  void pushValue(String const& value);
-  void writeIndent();
-  void writeWithIndent(String const& value);
-  void indent();
-  void unindent();
-  void writeCommentBeforeValue(Value const& root);
-  void writeCommentAfterValueOnSameLine(Value const& root);
-  static bool hasCommentForValue(const Value& value);
-
-  typedef std::vector<String> ChildValues;
-
-  ChildValues childValues_;
-  String indentString_;
-  unsigned int rightMargin_;
-  String indentation_;
-  CommentStyle::Enum cs_;
-  String colonSymbol_;
-  String nullSymbol_;
-  String endingLineFeedSymbol_;
-  bool addChildValues_ : 1;
-  bool indented_ : 1;
-  bool useSpecialFloats_ : 1;
-  unsigned int precision_;
-  PrecisionType precisionType_;
-};
-BuiltStyledStreamWriter::BuiltStyledStreamWriter(String indentation,
-                                                 CommentStyle::Enum cs,
-                                                 String colonSymbol,
-                                                 String nullSymbol,
-                                                 String endingLineFeedSymbol,
-                                                 bool useSpecialFloats,
-                                                 unsigned int precision,
-                                                 PrecisionType precisionType)
-    : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),
-      colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),
-      endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),
-      addChildValues_(false), indented_(false),
-      useSpecialFloats_(useSpecialFloats), precision_(precision),
-      precisionType_(precisionType) {}
-int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {
-  sout_ = sout;
-  addChildValues_ = false;
-  indented_ = true;
-  indentString_.clear();
-  writeCommentBeforeValue(root);
-  if (!indented_)
-    writeIndent();
-  indented_ = true;
-  writeValue(root);
-  writeCommentAfterValueOnSameLine(root);
-  *sout_ << endingLineFeedSymbol_;
-  sout_ = nullptr;
-  return 0;
-}
-void BuiltStyledStreamWriter::writeValue(Value const& value) {
-  switch (value.type()) {
-  case nullValue:
-    pushValue(nullSymbol_);
-    break;
-  case intValue:
-    pushValue(valueToString(value.asLargestInt()));
-    break;
-  case uintValue:
-    pushValue(valueToString(value.asLargestUInt()));
-    break;
-  case realValue:
-    pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_,
-                            precisionType_));
-    break;
-  case stringValue: {
-    // Is NULL is possible for value.string_? No.
-    char const* str;
-    char const* end;
-    bool ok = value.getString(&str, &end);
-    if (ok)
-      pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
-    else
-      pushValue("");
-    break;
-  }
-  case booleanValue:
-    pushValue(valueToString(value.asBool()));
-    break;
-  case arrayValue:
-    writeArrayValue(value);
-    break;
-  case objectValue: {
-    Value::Members members(value.getMemberNames());
-    if (members.empty())
-      pushValue("{}");
-    else {
-      writeWithIndent("{");
-      indent();
-      auto it = members.begin();
-      for (;;) {
-        String const& name = *it;
-        Value const& childValue = value[name];
-        writeCommentBeforeValue(childValue);
-        writeWithIndent(valueToQuotedStringN(
-            name.data(), static_cast<unsigned>(name.length())));
-        *sout_ << colonSymbol_;
-        writeValue(childValue);
-        if (++it == members.end()) {
-          writeCommentAfterValueOnSameLine(childValue);
-          break;
-        }
-        *sout_ << ",";
-        writeCommentAfterValueOnSameLine(childValue);
-      }
-      unindent();
-      writeWithIndent("}");
-    }
-  } break;
-  }
-}
-
-void BuiltStyledStreamWriter::writeArrayValue(Value const& value) {
-  unsigned size = value.size();
-  if (size == 0)
-    pushValue("[]");
-  else {
-    bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value);
-    if (isMultiLine) {
-      writeWithIndent("[");
-      indent();
-      bool hasChildValue = !childValues_.empty();
-      unsigned index = 0;
-      for (;;) {
-        Value const& childValue = value[index];
-        writeCommentBeforeValue(childValue);
-        if (hasChildValue)
-          writeWithIndent(childValues_[index]);
-        else {
-          if (!indented_)
-            writeIndent();
-          indented_ = true;
-          writeValue(childValue);
-          indented_ = false;
-        }
-        if (++index == size) {
-          writeCommentAfterValueOnSameLine(childValue);
-          break;
-        }
-        *sout_ << ",";
-        writeCommentAfterValueOnSameLine(childValue);
-      }
-      unindent();
-      writeWithIndent("]");
-    } else // output on a single line
-    {
-      assert(childValues_.size() == size);
-      *sout_ << "[";
-      if (!indentation_.empty())
-        *sout_ << " ";
-      for (unsigned index = 0; index < size; ++index) {
-        if (index > 0)
-          *sout_ << ((!indentation_.empty()) ? ", " : ",");
-        *sout_ << childValues_[index];
-      }
-      if (!indentation_.empty())
-        *sout_ << " ";
-      *sout_ << "]";
-    }
-  }
-}
-
-bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) {
-  ArrayIndex const size = value.size();
-  bool isMultiLine = size * 3 >= rightMargin_;
-  childValues_.clear();
-  for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
-    Value const& childValue = value[index];
-    isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
-                   !childValue.empty());
-  }
-  if (!isMultiLine) // check if line length > max line length
-  {
-    childValues_.reserve(size);
-    addChildValues_ = true;
-    ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
-    for (ArrayIndex index = 0; index < size; ++index) {
-      if (hasCommentForValue(value[index])) {
-        isMultiLine = true;
-      }
-      writeValue(value[index]);
-      lineLength += static_cast<ArrayIndex>(childValues_[index].length());
-    }
-    addChildValues_ = false;
-    isMultiLine = isMultiLine || lineLength >= rightMargin_;
-  }
-  return isMultiLine;
-}
-
-void BuiltStyledStreamWriter::pushValue(String const& value) {
-  if (addChildValues_)
-    childValues_.push_back(value);
-  else
-    *sout_ << value;
-}
-
-void BuiltStyledStreamWriter::writeIndent() {
-  // blep intended this to look at the so-far-written string
-  // to determine whether we are already indented, but
-  // with a stream we cannot do that. So we rely on some saved state.
-  // The caller checks indented_.
-
-  if (!indentation_.empty()) {
-    // In this case, drop newlines too.
-    *sout_ << '\n' << indentString_;
-  }
-}
-
-void BuiltStyledStreamWriter::writeWithIndent(String const& value) {
-  if (!indented_)
-    writeIndent();
-  *sout_ << value;
-  indented_ = false;
-}
-
-void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; }
-
-void BuiltStyledStreamWriter::unindent() {
-  assert(indentString_.size() >= indentation_.size());
-  indentString_.resize(indentString_.size() - indentation_.size());
-}
-
-void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
-  if (cs_ == CommentStyle::None)
-    return;
-  if (!root.hasComment(commentBefore))
-    return;
-
-  if (!indented_)
-    writeIndent();
-  const String& comment = root.getComment(commentBefore);
-  String::const_iterator iter = comment.begin();
-  while (iter != comment.end()) {
-    *sout_ << *iter;
-    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
-      // writeIndent();  // would write extra newline
-      *sout_ << indentString_;
-    ++iter;
-  }
-  indented_ = false;
-}
-
-void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(
-    Value const& root) {
-  if (cs_ == CommentStyle::None)
-    return;
-  if (root.hasComment(commentAfterOnSameLine))
-    *sout_ << " " + root.getComment(commentAfterOnSameLine);
-
-  if (root.hasComment(commentAfter)) {
-    writeIndent();
-    *sout_ << root.getComment(commentAfter);
-  }
-}
-
-// static
-bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) {
-  return value.hasComment(commentBefore) ||
-         value.hasComment(commentAfterOnSameLine) ||
-         value.hasComment(commentAfter);
-}
-
-///////////////
-// StreamWriter
-
-StreamWriter::StreamWriter() : sout_(nullptr) {}
-StreamWriter::~StreamWriter() = default;
-StreamWriter::Factory::~Factory() = default;
-StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); }
-StreamWriterBuilder::~StreamWriterBuilder() = default;
-StreamWriter* StreamWriterBuilder::newStreamWriter() const {
-  String indentation = settings_["indentation"].asString();
-  String cs_str = settings_["commentStyle"].asString();
-  String pt_str = settings_["precisionType"].asString();
-  bool eyc = settings_["enableYAMLCompatibility"].asBool();
-  bool dnp = settings_["dropNullPlaceholders"].asBool();
-  bool usf = settings_["useSpecialFloats"].asBool();
-  unsigned int pre = settings_["precision"].asUInt();
-  CommentStyle::Enum cs = CommentStyle::All;
-  if (cs_str == "All") {
-    cs = CommentStyle::All;
-  } else if (cs_str == "None") {
-    cs = CommentStyle::None;
-  } else {
-    throwRuntimeError("commentStyle must be 'All' or 'None'");
-  }
-  PrecisionType precisionType(significantDigits);
-  if (pt_str == "significant") {
-    precisionType = PrecisionType::significantDigits;
-  } else if (pt_str == "decimal") {
-    precisionType = PrecisionType::decimalPlaces;
-  } else {
-    throwRuntimeError("precisionType must be 'significant' or 'decimal'");
-  }
-  String colonSymbol = " : ";
-  if (eyc) {
-    colonSymbol = ": ";
-  } else if (indentation.empty()) {
-    colonSymbol = ":";
-  }
-  String nullSymbol = "null";
-  if (dnp) {
-    nullSymbol.clear();
-  }
-  if (pre > 17)
-    pre = 17;
-  String endingLineFeedSymbol;
-  return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,
-                                     endingLineFeedSymbol, usf, pre,
-                                     precisionType);
-}
-static void getValidWriterKeys(std::set<String>* valid_keys) {
-  valid_keys->clear();
-  valid_keys->insert("indentation");
-  valid_keys->insert("commentStyle");
-  valid_keys->insert("enableYAMLCompatibility");
-  valid_keys->insert("dropNullPlaceholders");
-  valid_keys->insert("useSpecialFloats");
-  valid_keys->insert("precision");
-  valid_keys->insert("precisionType");
-}
-bool StreamWriterBuilder::validate(Json::Value* invalid) const {
-  Json::Value my_invalid;
-  if (!invalid)
-    invalid = &my_invalid; // so we do not need to test for NULL
-  Json::Value& inv = *invalid;
-  std::set<String> valid_keys;
-  getValidWriterKeys(&valid_keys);
-  Value::Members keys = settings_.getMemberNames();
-  size_t n = keys.size();
-  for (size_t i = 0; i < n; ++i) {
-    String const& key = keys[i];
-    if (valid_keys.find(key) == valid_keys.end()) {
-      inv[key] = settings_[key];
-    }
-  }
-  return inv.empty();
-}
-Value& StreamWriterBuilder::operator[](const String& key) {
-  return settings_[key];
-}
-// static
-void StreamWriterBuilder::setDefaults(Json::Value* settings) {
-  //! [StreamWriterBuilderDefaults]
-  (*settings)["commentStyle"] = "All";
-  (*settings)["indentation"] = "\t";
-  (*settings)["enableYAMLCompatibility"] = false;
-  (*settings)["dropNullPlaceholders"] = false;
-  (*settings)["useSpecialFloats"] = false;
-  (*settings)["precision"] = 17;
-  (*settings)["precisionType"] = "significant";
-  //! [StreamWriterBuilderDefaults]
-}
-
-String writeString(StreamWriter::Factory const& factory, Value const& root) {
-  OStringStream sout;
-  StreamWriterPtr const writer(factory.newStreamWriter());
-  writer->write(root, &sout);
-  return sout.str();
-}
-
-OStream& operator<<(OStream& sout, Value const& root) {
-  StreamWriterBuilder builder;
-  StreamWriterPtr const writer(builder.newStreamWriter());
-  writer->write(root, &sout);
-  return sout;
-}
-
-} // namespace Json
-
-// //////////////////////////////////////////////////////////////////////
-// End of content of file: src/lib_json/json_writer.cpp
-// //////////////////////////////////////////////////////////////////////
-
-
-
-
-
--- a/Resources/CodeGeneration/testWasmIntegrated/main.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,186 +0,0 @@
-#include <iostream>
-#include <sstream>
-#include <emscripten/emscripten.h>
-#include "TestStoneCodeGen_generated.hpp"
-
-using std::stringstream;
-
-int main()
-{
-    std::cout << "Hello world from testWasmIntegrated! (this is sent from C++)" << std::endl;
-    try
-    {
-    const char* jsonData = R"bgo({"definition":
-    {
-      "val" : [ "berk", 42 ],
-      "zozo" : { "23": "zloutch", "lalala": 42}
-    }
-    })bgo";
-    std::string strValue(jsonData);
-    
-    Json::Value readValue;
-
-    Json::CharReaderBuilder builder;
-    Json::CharReader* reader = builder.newCharReader();
-
-    StoneSmartPtr<Json::CharReader> ptr(reader);
-
-    std::string errors;
-
-    bool ok = reader->parse(
-      strValue.c_str(),
-      strValue.c_str() + strValue.size(),
-      &readValue,
-      &errors
-    );
-    if (!ok)
-    {
-      std::stringstream ss;
-      ss << "Jsoncpp parsing error: " << errors;
-      throw std::runtime_error(ss.str());
-    }
-    std::cout << "Json parsing OK" << std::endl;
-    std::cout << readValue  << std::endl;
-    }
-    catch(std::exception& e)
-    {
-      std::cout << "Json parsing THROW" << std::endl;
-      std::cout << "e.what() = " << e.what() << std::endl;
-    }
-}
-
-extern "C" void SendMessageFromCppJS(const char* message);
-extern "C" void SendFreeTextFromCppJS(const char* message);
-
-#define HANDLE_MESSAGE(Type,value) \
-  stringstream ss; \
-  ss << "Received an instance of:\n" #Type "\n. Here's the dump:\n"; \
-  TestStoneCodeGen::StoneDumpValue(ss, value, 0); \
-  SendFreeTextFromCppJS(ss.str().c_str()); \
-  return true;
-
-#define ECHO_MESSAGE(Type,value) \
-  stringstream ss; \
-  ss << "Received an instance of:\n" #Type "\n. Here's the dump:\n"; \
-  TestStoneCodeGen::StoneDumpValue(ss, value, 0); \
-  SendFreeTextFromCppJS(ss.str().c_str()); \
-  std::string serializedInCpp = StoneSerialize(value); \
-  SendMessageFromCppJS(serializedInCpp.c_str()); \
-  return true;
-
-class MyHandler : public TestStoneCodeGen::IHandler
-{
-  public:
-    virtual bool Handle(const TestStoneCodeGen::A& value) override
-    {
-      HANDLE_MESSAGE(TestStoneCodeGen::A,value)
-    }
-    virtual bool Handle(const TestStoneCodeGen::B& value) override
-    {
-      HANDLE_MESSAGE(TestStoneCodeGen::B,value)
-    }
-
-    virtual bool Handle(const TestStoneCodeGen::Message1& value) override
-    {
-      HANDLE_MESSAGE(TestStoneCodeGen::Message1,value)
-    }
-
-    virtual bool Handle(const TestStoneCodeGen::Message2& value) override
-    {
-      HANDLE_MESSAGE(TestStoneCodeGen::Message2,value)
-    }
-
-    virtual bool Handle(const TestStoneCodeGen::C& value) override
-    {
-      HANDLE_MESSAGE(TestStoneCodeGen::C,value)
-    }
-};
-
-class MyEchoHandler : public TestStoneCodeGen::IHandler
-{
-  public:
-    virtual bool Handle(const TestStoneCodeGen::A& value) override
-    {
-      ECHO_MESSAGE(TestStoneCodeGen::A,value)
-    }
-    virtual bool Handle(const TestStoneCodeGen::B& value) override
-    {
-      ECHO_MESSAGE(TestStoneCodeGen::B,value)
-    }
-
-    virtual bool Handle(const TestStoneCodeGen::Message1& value) override
-    {
-      ECHO_MESSAGE(TestStoneCodeGen::Message1,value)
-    }
-
-    virtual bool Handle(const TestStoneCodeGen::Message2& value) override
-    {
-      ECHO_MESSAGE(TestStoneCodeGen::Message2,value)
-    }
-
-    virtual bool Handle(const TestStoneCodeGen::C& value) override
-    {
-      ECHO_MESSAGE(TestStoneCodeGen::C,value)
-    }
-};
-
-extern "C" void EMSCRIPTEN_KEEPALIVE SendMessageToCpp(const char* message)
-{
-    MyHandler handler;
-    try
-    {
-      bool handled = TestStoneCodeGen::StoneDispatchToHandler(message,&handler);
-      if(!handled)
-      {
-        SendFreeTextFromCppJS("This message is valid JSON, but was not handled!");  
-      }
-    }
-    catch(std::exception& e)
-    {
-      stringstream ss;
-      ss << "Error while parsing message: " << e.what() << "\n";
-      SendFreeTextFromCppJS(ss.str().c_str());  
-    }
-}
-
-extern "C" void EMSCRIPTEN_KEEPALIVE SendMessageToCppForEcho(const char* message)
-{
-    MyEchoHandler echoHandler;
-    try
-    {
-      bool handled = TestStoneCodeGen::StoneDispatchToHandler(message,&echoHandler);
-      if(!handled)
-      {
-        SendFreeTextFromCppJS("This message is valid JSON, but was not handled by the echo handler!");  
-      }
-    }
-    catch(std::exception& e)
-    {
-      stringstream ss;
-      ss << "Error while parsing message: " << e.what() << "\n";
-      SendFreeTextFromCppJS(ss.str().c_str());  
-    }
-}
-
-void EMSCRIPTEN_KEEPALIVE StartWasmApplication(const char* baseUri)
-{
-    printf("Hello! (this is sent from C++)\n");
-
-//     // recreate a command line from uri arguments and parse it
-//     boost::program_options::variables_map parameters;
-//     boost::program_options::options_description options;
-//     application->DeclareStartupOptions(options);
-//     startupParametersBuilder.GetStartupParameters(parameters, options);
-
-//     context.reset(new OrthancStone::StoneApplicationContext(broker));
-//     context->SetOrthancBaseUrl(baseUri);
-//     printf("Base URL to Orthanc API: [%s]\n", baseUri);
-//     context->SetWebService(OrthancStone::WasmWebService::GetInstance());
-//     context->SetDelayedCallExecutor(OrthancStone::WasmDelayedCallExecutor::GetInstance());
-//     application->Initialize(context.get(), statusBar_, parameters);
-//     application->InitializeWasm();
-
-// //    viewport->SetSize(width_, height_);
-//     printf("StartWasmApplication - completed\n");
-    SendFreeTextFromCppJS("Hello world from C++!");
-}
--- a/Resources/CodeGeneration/testWasmIntegrated/serve.py	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-# tested on python 3.4 ,python of lower version  has different module organization.
-# from https://gist.github.com/HaiyangXu/ec88cbdce3cdbac7b8d5
-import http.server
-from http.server import HTTPServer, BaseHTTPRequestHandler
-import socketserver
-
-PORT = 8080
-
-Handler = http.server.SimpleHTTPRequestHandler
-
-Handler.extensions_map = {
-  '.manifest': 'text/cache-manifest',
-  '.html': 'text/html',
-  '.png': 'image/png',
-  '.jpg': 'image/jpg',
-  '.svg': 'image/svg+xml',
-  '.wasm': 'application/wasm',
-  '.css': 'text/css',
-  '.js': 'application/x-javascript',
-  '': 'application/octet-stream',  # Default
-}
-
-httpd = socketserver.TCPServer(("", PORT), Handler)
-
-print("serving at port", PORT)
-httpd.serve_forever()
--- a/Resources/CodeGeneration/testWasmIntegrated/styles.css	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-.TestWasm-grid-container {
-    display: grid;
-    grid-template-columns: 0.55fr 0.55fr 0.55fr 0.55fr 0.6fr 1.1fr 1.1fr;
-    grid-template-rows: 1.1fr 0.9fr 0.2fr 0.3fr 0.1fr 0.3fr 0.1fr;
-    grid-template-areas: 
-        "SerializedInput SerializedInput SerializedInput SerializedInput ButtonContainer CppOutput CppOutput" 
-        "SerializedInput SerializedInput SerializedInput SerializedInput ButtonContainer CppOutput CppOutput" 
-        ". . . . . . ." 
-        "Test1 Test2 Test3 Test4 . . ." 
-        ". . . . . . ." 
-        "Test5 Test6 Test7 Test8 . . ."
-        "TestTsCppTs . . . . . ." 
-        ". . . . . . ." 
-        ;
-    height: 480px;
-  }
-
-  .TestWasm-ButtonContainer {
-    display: grid;
-    grid-template-columns: 0.2fr 0.8fr 0.2fr;
-    grid-template-rows: 0.2fr 0.5fr 0.2fr 0.5fr 0.2fr 0.5fr 0.2fr;
-    grid-template-areas: 
-        ". . ."
-        ". TriggerButton ." 
-        ". . ."
-        ". ClearButton ." 
-        ". . ."
-        ". ShowSchemaButton ." 
-        ". . ."
-        ;
-  }
-
-  .TestWasm-TriggerButton { grid-area: TriggerButton; }
-  
-  .TestWasm-ClearButton { grid-area: ClearButton; }
-  
-  .TestWasm-ShowSchemaButton { grid-area: ShowSchemaButton; }
-
-
-.TestWasm-SerializedInput { grid-area: SerializedInput; }
-
-.TestWasm-CppOutput { grid-area: CppOutput; }
-
-.TestWasm-ButtonContainer { grid-area: ButtonContainer; }
-
-.TestWasm-Test1 { grid-area: Test1; }
-
-.TestWasm-Test2 { grid-area: Test2; }
-
-.TestWasm-Test3 { grid-area: Test3; }
-
-.TestWasm-Test4 { grid-area: Test4; }
-
-.TestWasm-Test5 { grid-area: Test5; }
-
-.TestWasm-Test6 { grid-area: Test6; }
-
-.TestWasm-Test7 { grid-area: Test7; }
-
-.TestWasm-Test8 { grid-area: Test8; }
-
-.TestWasm-ts-cpp-ts { grid-area: TestTsCppTs; }
-
-.TestWasm-button {
-    width:80px;
-}
--- a/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.html	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-<!doctype html>
-
-<html lang="us">
-  <head>
-    <meta charset="utf-8" />
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
-    <title>Javascript to WASM message passing</title>
-    <link href="styles.css" rel="stylesheet" />
-    <!-- <link href="styles2.css" rel="stylesheet" /> -->
-    <!-- 
-    <link href="testwasm_bootstrap.css" rel="stylesheet" /> 
-    -->
-    <body>
-    <div class="TestWasm-grid-container">
-        <textarea id="TestWasm-SerializedInput" class="TestWasm-SerializedInput">Serialized data should be put here.</textarea>
-        <textarea id="TestWasm-CppOutput" class="TestWasm-CppOutput">Free text and messages from C++ will appear here.</textarea>
-        <div class="TestWasm-ButtonContainer">
-          <div class="TestWasm-TriggerButton">
-            <button class="TestWasm-button" tool-selector="Trigger">Send message</button>
-          </div>
-          <div class="TestWasm-ClearButton">
-            <button class="TestWasm-button" tool-selector="Clear">Clear</button>
-          </div>
-          <div class="TestWasm-ShowSchemaButton">
-            <button class="TestWasm-button" tool-selector="ShowSchema">Show schema</button>
-          </div>
-           <!-- <button class="TestWasm-button TestWasm-hvcenter" tool-selector="Trigger">Send message</button>
-            <button class="TestWasm-button TestWasm-hvcenter" tool-selector="Clear">Clear</button>
-            <button class="TestWasm-button TestWasm-hvcenter" tool-selector="Show schema">Show schema</button> -->
-        </div>
-        
-        <div class="TestWasm-Test1">
-          <button class="TestWasm-button" tool-selector="Test CppHandler message2">Test CppHandler message2</button>
-        </div>
-        <div class="TestWasm-Test2">
-          <button class="TestWasm-button" tool-selector="Test 2">Test 2</button>
-        </div>
-        <div class="TestWasm-Test3">
-          <button class="TestWasm-button" tool-selector="Test 3">Test 3</button>
-        </div>
-        <div class="TestWasm-Test4">
-          <button class="TestWasm-button" tool-selector="Test 4">Test 4</button>
-        </div>
-        <div class="TestWasm-Test5">
-          <button class="TestWasm-button" tool-selector="Test 5">Test 5</button>
-        </div>
-        <div class="TestWasm-Test6">
-          <button class="TestWasm-button" tool-selector="Test 6">Test 6</button>
-        </div>
-        <div class="TestWasm-Test7">
-          <button class="TestWasm-button" tool-selector="Test 7">Test 7</button>
-        </div>
-        <div class="TestWasm-Test8">
-          <button class="TestWasm-button" tool-selector="Test 8">Test 8</button>
-        </div>
-        <div class="TestWasm-ts-cpp-ts">
-          <button class="TestWasm-button" tool-selector="Test-ts-cpp-ts">Test ts-cpp-ts</button>
-        </div>
-  
-        <!-- <button class="TestWasm-button" class="TestWasm-Test1" tool-selector="Test 1">Test 1</button>
-        <button class="TestWasm-button" class="TestWasm-Test2" tool-selector="Test 2">Test 2</button>
-        <button class="TestWasm-button" class="TestWasm-Test3" tool-selector="Test 3">Test 3</button>
-        <button class="TestWasm-button" class="TestWasm-Test4" tool-selector="Test 4">Test 4</button> -->
-
-
-
-        <!-- <div class="Trigger"></div>
-        <div class="Test1"></div>
-        <div class="Test2"></div>
-        <div class="Test3"></div>
-        <div class="Test4"></div> -->
-      </div>
-
-  <!-- <div id="toolbox" style="height: 50px">
-    <button tool-selector="line-measure" class="tool-selector">line</button>
-  </div> -->
-  <script type="text/javascript" src="testWasmIntegratedCpp.js"></script>
-  <script type="text/javascript" src="testWasmIntegratedApp.js"></script>
-</body>
-
-</html>
--- a/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.ts	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,210 +0,0 @@
-var SendMessageToCpp: Function = null;
-export var TestWasmIntegratedModule : any;
-
-import * as TestStoneCodeGen from './build-wasm/TestStoneCodeGen_generated'
-
-/*
-+--------------------------------------------------+
-|  install emscripten handlers                     |
-+--------------------------------------------------+
-*/
-
-// (<any> window).Module = {
-//   preRun: [ 
-//     function() {
-//     console.log('Loading the Stone Framework using WebAssembly');
-//     }
-//   ],
-//   postRun: [ 
-//     function()  {
-//     // This function is called by ".js" wrapper once the ".wasm"
-//     // WebAssembly module has been loaded and compiled by the
-//     // browser
-//     console.log('WebAssembly is ready');
-//     // window.SendMessageToCpp = (<any> window).Module.cwrap('SendMessageToCpp', 'string', ['string']);
-//     // window.SendFreeTextToCpp = (<any> window).Module.cwrap('SendFreeTextToCpp', 'string', ['string']);
-//     }
-//   ],
-//   print: function(text : string) {
-//     console.log(text);
-//   },
-//   printErr: function(text : string) {
-//     console.error(text);
-//   },
-//   totalDependencies: 0
-// };
-
-/*
-+--------------------------------------------------+
-|  install handlers                                |
-+--------------------------------------------------+
-*/
-document.querySelectorAll(".TestWasm-button").forEach((e) => {
-  (e as HTMLButtonElement).addEventListener("click", () => {
-    ButtonClick(e.attributes["tool-selector"].value);
-  });
-});
-
-/*
-+--------------------------------------------------+
-|  define stock messages                           |
-+--------------------------------------------------+
-*/
-let schemaText: string = null;
-fetch("testTestStoneCodeGen.yaml").then(function(res) {return res.text();}).then(function(text) {schemaText = text;});
-
-let stockSerializedMessages = new Map<string,string>();
-stockSerializedMessages["Test CppHandler message2"] = null;
-fetch("cppHandler_test_Message2.json").then(function(res) {return res.text();}).then(function(text) {stockSerializedMessages["Test CppHandler message2"] = text;});
-
-stockSerializedMessages["Test 2"] = ` {
-  "type" : "TestStoneCodeGen.Message1",
-  "value" : {
-    "memberInt32" : -987,
-    "memberString" : "Salomé",
-    "memberEnumMonth" : "March",
-    "memberBool" : true,
-    "memberFloat32" : 0.1,
-    "memberFloat64" : -0.2,
-    "extraMember" : "don't care"
-  }
-}`;
-stockSerializedMessages["Test 3"] = "Test 3 stock message sdfsfsdfsdf";
-stockSerializedMessages["Test 4"] = "Test 4 stock message 355345345";
-stockSerializedMessages["Test 5"] = "Test 5 stock message 34535";
-stockSerializedMessages["Test 6"] = "Test 6 stock message xcvcxvx";
-stockSerializedMessages["Test 7"] = "Test 7 stock message fgwqewqdgg";
-stockSerializedMessages["Test 8"] = "Test 8 stock message fgfsdfsdgg";
-
-/*
-+--------------------------------------------------+
-|  define handler                                  |
-+--------------------------------------------------+
-*/
-
-function setSerializedInputValue(text: string) {
-  let e : HTMLTextAreaElement = document.getElementById('TestWasm-SerializedInput') as HTMLTextAreaElement;
-  e.value = text;
-}
-
-function getSerializedInputValue(): string {
-  let e : HTMLTextAreaElement = document.getElementById('TestWasm-SerializedInput') as HTMLTextAreaElement;
-  return e.value;
-}
-
-function setCppOutputValue(text: string) {
-  let e : HTMLTextAreaElement = document.getElementById('TestWasm-CppOutput') as HTMLTextAreaElement;
-  e.value = text;
-}
-
-function getCppOutputValue(): string {
-  let e : HTMLTextAreaElement = document.getElementById('TestWasm-CppOutput') as HTMLTextAreaElement;
-  return e.value;
-}
-
-function SendFreeTextFromCpp(txt: string):string
-{
-  setCppOutputValue(getCppOutputValue() + "\n" + txt);
-  return "";
-}
-(<any> window).SendFreeTextFromCpp = SendFreeTextFromCpp;
-
-var referenceMessages = Array<any>();
-
-function testTsCppTs() {
-  var r = new TestStoneCodeGen.Message2();
-  r.memberEnumMovieType = TestStoneCodeGen.MovieType.RomCom;
-  r.memberStringWithDefault = "overriden";
-  r.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives] = 0.5;
-  r.memberString = "reference-messsage2-test1";
-
-  referenceMessages[r.memberString] = r;
-  var strMsg2 = r.StoneSerialize();
-  let SendMessageToCppForEchoLocal = (<any> window).Module.cwrap('SendMessageToCppForEcho', 'string', ['string']);
-  SendMessageToCppForEchoLocal(strMsg2);
-}
-
-class MyEchoHandler implements TestStoneCodeGen.IHandler
-{
-  public HandleMessage2(value:  TestStoneCodeGen.Message2): boolean
-  {
-    if (value.memberString in referenceMessages) {
-      let r = referenceMessages[value.memberString];
-      let equals = (value.memberStringWithDefault == r.memberStringWithDefault);
-      if (TestStoneCodeGen.CrispType.CreamAndChives in r.memberMapEnumFloat) {
-        equals == equals && r.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives] == value.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives];
-      }
-      // TODO continue comparison
-
-      if (equals) {
-        console.log("objects are equals after round trip");
-        return true;
-      }
-    }
-    console.log("problem after round trip");
-    return true;
-  }
-}
-
-function SendMessageFromCpp(txt: string):string
-{
-  setCppOutputValue(getCppOutputValue() + "\n" + txt);
-  TestStoneCodeGen.StoneDispatchToHandler(txt, new MyEchoHandler());
-  return "";
-}
-(<any> window).SendMessageFromCpp = SendMessageFromCpp;
-
-
-
-function ButtonClick(buttonName: string) {
-  if (buttonName.startsWith('Test ')) {
-    setSerializedInputValue(stockSerializedMessages[buttonName]);
-  }
-  else if (buttonName == "Test-ts-cpp-ts") {
-    testTsCppTs();
-  }
-  else if(buttonName == 'Trigger')
-  {
-    let serializedInputValue:string = getSerializedInputValue();
-
-    let SendMessageToCppLocal = (<any> window).Module.cwrap('SendMessageToCpp', 'string', ['string']);
-    SendMessageToCppLocal(serializedInputValue);
-  }
-  else if(buttonName == 'Clear')
-  {
-    setCppOutputValue("");
-  }
-  else if(buttonName == 'ShowSchema')
-  {
-    setCppOutputValue(schemaText);
-  }
-  else
-  {
-    throw new Error("Internal error!");
-  }
-}
-
-
-
-// this method is called "from the C++ code" when the StoneApplication is updated.
-// it can be used to update the UI of the application
-function UpdateWebApplicationWithString(statusUpdateMessageString: string) {
-  console.log("updating web application (string): ", statusUpdateMessageString);
-  let statusUpdateMessage = JSON.parse(statusUpdateMessageString);
-
-  if ("event" in statusUpdateMessage)
-  {
-    let eventName = statusUpdateMessage["event"];
-    if (eventName == "appStatusUpdated")
-    {
-      //ui.onAppStatusUpdated(statusUpdateMessage["data"]);
-    }
-  }
-}
-
-
-function UpdateWebApplicationWithSerializedMessage(statusUpdateMessageString: string) {
-  console.log("updating web application (serialized message): ", statusUpdateMessageString);
-  console.log("<not supported!>");
-}
- 
\ No newline at end of file
--- a/Resources/CodeGeneration/test_data/test2.yaml	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-enum EnumMonth0:
-  - January
-  - February
-  - Month
-
-struct Message1:
-  a: int32
-  b: string
-  c: EnumMonth0
-  d: bool
-
-struct Message2:
-  toto: string
-  tata: vector<Message1>
-  tutu: vector<string>
-  titi: map<string, string>
-  lulu: map<string, Message1>
--- a/Resources/CodeGeneration/test_data/testTestStoneCodeGen.yaml	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-#
-#        1         2         3         4         5         6         7         8
-# 345678901234567890123456789012345678901234567890123456789012345678901234567890
-#
-rootName: TestStoneCodeGen
-
-struct B:
-  __handler: cpp
-
-  someAs: vector<A>
-  someInts: vector<int32>
-
-struct C:
-  __handler: cpp
-
-  someBs: vector<B>
-  ddd:    vector<string>
-
-struct A:
-  __handler: cpp
-
-  someStrings: vector<string>
-  someInts2: vector<int32>
-  movies: vector<MovieType>
-
-struct Message1:
-  __handler: cpp
-
-  memberInt32: int32
-  memberString: string
-  memberEnumMonth: EnumMonth0
-  memberBool: bool
-  memberFloat32: float32
-  memberFloat64: float64
-
-struct Message2:
-  __handler: [cpp, ts]
-
-  memberString: string
-  memberStringWithDefault: string = "my-default-value"
-  memberVectorOfMessage1: vector<Message1>
-  memberVectorOfString: vector<string>
-  memberMapStringString: map<string, string>
-  memberMapStringStruct: map<string, Message1>
-  memberMapEnumFloat: map<CrispType, float32>
-  memberEnumMovieType: MovieType
-  memberJson: json
-
-enum MovieType:
-  - RomCom
-  - Horror
-  - ScienceFiction
-  - Vegetables
-
-enum CrispType:
-  - SaltAndPepper
-  - CreamAndChives
-  - Paprika
-  - Barbecue
-
-enum EnumMonth0:
-  - January
-  - February
-  - March
--- a/Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,275 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "BasicScene.h"
-
-// From Stone
-#include "Framework/Scene2D/Scene2D.h"
-#include "Framework/Scene2D/ColorTextureSceneLayer.h"
-#include "Framework/Scene2D/PolylineSceneLayer.h"
-#include "Framework/Scene2D/TextSceneLayer.h"
-
-#include "Framework/Scene2D/PanSceneTracker.h"
-#include "Framework/Scene2D/ZoomSceneTracker.h"
-#include "Framework/Scene2D/RotateSceneTracker.h"
-
-#include "Framework/Scene2D/CairoCompositor.h"
-
-// From Orthanc framework
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Images/PngWriter.h>
-
-using namespace OrthancStone;
-
-const unsigned int BASIC_SCENE_FONT_SIZE = 32;
-const int BASIC_SCENE_LAYER_POSITION = 150;
-
-void PrepareScene(Scene2D& scene)
-{
-  //Scene2D& scene(*controller->GetScene());
-  // Texture of 2x2 size
-  {
-    Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false);
-
-    uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0));
-    p[0] = 255;
-    p[1] = 0;
-    p[2] = 0;
-
-    p[3] = 0;
-    p[4] = 255;
-    p[5] = 0;
-
-    p = reinterpret_cast<uint8_t*>(i.GetRow(1));
-    p[0] = 0;
-    p[1] = 0;
-    p[2] = 255;
-
-    p[3] = 255;
-    p[4] = 0;
-    p[5] = 0;
-
-    scene.SetLayer(12, new ColorTextureSceneLayer(i));
-
-    std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
-    l->SetOrigin(-3, 2);
-    l->SetPixelSpacing(1.5, 1);
-    l->SetAngle(20.0 / 180.0 * 3.14);
-    scene.SetLayer(14, l.release());
-  }
-
-  // Texture of 1x1 size
-  {
-    Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false);
-
-    uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0));
-    p[0] = 255;
-    p[1] = 0;
-    p[2] = 0;
-
-    std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
-    l->SetOrigin(-2, 1);
-    l->SetAngle(20.0 / 180.0 * 3.14);
-    scene.SetLayer(13, l.release());
-  }
-
-  // Some lines
-  {
-    std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer);
-
-    layer->SetThickness(1);
-
-    PolylineSceneLayer::Chain chain;
-    chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5));
-    chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5));
-    chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5));
-    chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5));
-    layer->AddChain(chain, true, 255, 0, 0);
-
-    chain.clear();
-    chain.push_back(ScenePoint2D(-5, -5));
-    chain.push_back(ScenePoint2D(5, -5));
-    chain.push_back(ScenePoint2D(5, 5));
-    chain.push_back(ScenePoint2D(-5, 5));
-    layer->AddChain(chain, true, 0, 255, 0);
-
-    double dy = 1.01;
-    chain.clear();
-    chain.push_back(ScenePoint2D(-4, -4));
-    chain.push_back(ScenePoint2D(4, -4 + dy));
-    chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy));
-    chain.push_back(ScenePoint2D(4, 2));
-    layer->AddChain(chain, false, 0, 0, 255);
-
-    //    layer->SetColor(0,255, 255);
-    scene.SetLayer(50, layer.release());
-  }
-
-  // Some text
-  {
-    std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-    layer->SetText("Hello");
-    scene.SetLayer(100, layer.release());
-  }
-}
-
-#if ORTHANC_SANDBOXED == 0
-void TakeScreenshot(const std::string& target,
-                    const OrthancStone::Scene2D& scene,
-                    unsigned int canvasWidth,
-                    unsigned int canvasHeight)
-{
-  using namespace OrthancStone;
-  // Take a screenshot, then save it as PNG file
-  CairoCompositor compositor(scene, canvasWidth, canvasHeight);
-  compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1);
-  compositor.Refresh();
-
-  Orthanc::ImageAccessor canvas;
-  compositor.GetCanvas().GetReadOnlyAccessor(canvas);
-
-  Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
-  Orthanc::ImageProcessing::Convert(png, canvas);
-
-  Orthanc::PngWriter writer;
-  writer.WriteToFile(target, png);
-}
-#endif
-
-void ShowCursorInfo(Scene2D& scene, const PointerEvent& pointerEvent)
-{
-  ScenePoint2D p = pointerEvent.GetMainPosition().Apply(scene.GetCanvasToSceneTransform());
-
-  char buf[64];
-  sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY());
-
-  if (scene.HasLayer(BASIC_SCENE_LAYER_POSITION))
-  {
-    TextSceneLayer& layer =
-        dynamic_cast<TextSceneLayer&>(scene.GetLayer(BASIC_SCENE_LAYER_POSITION));
-    layer.SetText(buf);
-    layer.SetPosition(p.GetX(), p.GetY());
-  }
-  else
-  {
-    std::unique_ptr<TextSceneLayer>
-        layer(new TextSceneLayer);
-    layer->SetColor(0, 255, 0);
-    layer->SetText(buf);
-    layer->SetBorder(20);
-    layer->SetAnchor(BitmapAnchor_BottomCenter);
-    layer->SetPosition(p.GetX(), p.GetY());
-    scene.SetLayer(BASIC_SCENE_LAYER_POSITION, layer.release());
-  }
-}
-
-
-
-bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent)
-{
-  if (currentTracker_.get() != NULL)
-  {
-    switch (event.type)
-    {
-    case GUIADAPTER_EVENT_MOUSEUP:
-    {
-      currentTracker_->PointerUp(pointerEvent);
-      if (!currentTracker_->IsAlive())
-      {
-        currentTracker_.reset();
-      }
-    };break;
-    case GUIADAPTER_EVENT_MOUSEMOVE:
-    {
-      currentTracker_->PointerMove(pointerEvent);
-    };break;
-    default:
-      return false;
-    }
-    return true;
-  }
-  else if (event.type == GUIADAPTER_EVENT_MOUSEDOWN)
-  {
-    if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT)
-    {
-      currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent));
-    }
-    else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE)
-    {
-      currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent));
-    }
-    else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT)
-    {
-      currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, viewportController_->GetViewport().GetCanvasHeight()));
-    }
-  }
-  else if (event.type == GUIADAPTER_EVENT_MOUSEMOVE)
-  {
-    if (showCursorInfo_)
-    {
-      Scene2D& scene(viewportController_->GetScene());
-      ShowCursorInfo(scene, pointerEvent);
-    }
-    return true;
-  }
-  return false;
-}
-
-bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent)
-{
-  if (guiEvent.type == GUIADAPTER_EVENT_KEYDOWN)
-  {
-    switch (guiEvent.sym[0])
-    {
-    case 's':
-    {
-      //viewportController_->FitContent(viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight());
-      viewportController_->FitContent();
-      return true;
-    };
-#if ORTHANC_SANDBOXED == 0
-    case 'c':
-    {
-      Scene2D& scene(viewportController_->GetScene());
-      TakeScreenshot("screenshot.png", scene, viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight());
-      return true;
-    }
-#endif
-    case 'd':
-    {
-      showCursorInfo_ = !showCursorInfo_;
-      if (!showCursorInfo_)
-      {
-        Scene2D& scene(viewportController_->GetScene());
-        scene.DeleteLayer(BASIC_SCENE_LAYER_POSITION);
-      }
-
-      return true;
-    }
-    }
-  }
-  return false;
-}
-
-bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent)
-{
-  return false;
-}
--- a/Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/**
- * 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 <boost/shared_ptr.hpp>
-#include "Framework/Scene2DViewport/ViewportController.h"
-#include "Framework/Scene2D/Scene2D.h"
-
-extern const unsigned int BASIC_SCENE_FONT_SIZE;
-extern const int BASIC_SCENE_LAYER_POSITION;
-
-extern void PrepareScene(OrthancStone::Scene2D& scene);
-extern void TakeScreenshot(const std::string& target,
-                           const OrthancStone::Scene2D& scene,
-                           unsigned int canvasWidth,
-                           unsigned int canvasHeight);
-
-
-#include "Applications/Generic/Scene2DInteractor.h"
-#include "Framework/Scene2DViewport/IFlexiblePointerTracker.h"
-
-
-class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor
-{
-  boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>  currentTracker_;
-  bool                                                      showCursorInfo_;
-public:
-  BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) :
-    Scene2DInteractor(viewportController),
-    showCursorInfo_(false)
-  {}
-
-  virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override;
-  virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent) override;
-  virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent) override;
-};
-
--- a/Samples/Deprecated/MultiPlatform/BasicScene/mainQt.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/**
- * 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/>.
- **/
-
-#define GLEW_STATIC 1
-// From Stone
-#include "../../Framework/OpenGL/OpenGLIncludes.h"
-#include "../../Applications/Sdl/SdlWindow.h"
-#include "../../Framework/Scene2D/CairoCompositor.h"
-#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/Scene2D/PanSceneTracker.h"
-#include "../../Framework/Scene2D/RotateSceneTracker.h"
-#include "../../Framework/Scene2D/Scene2D.h"
-#include "../../Framework/Scene2D/ZoomSceneTracker.h"
-#include "../../Framework/Scene2DViewport/ViewportController.h"
-#include "../../Framework/Scene2DViewport/UndoStack.h"
-
-#include "../../Framework/StoneInitialization.h"
-#include "../../Framework/Messages/MessageBroker.h"
-
-// From Orthanc framework
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Images/PngWriter.h>
-
-#include <boost/make_shared.hpp>
-#include <boost/ref.hpp>
-#include "EmbeddedResources.h"
-
-#include <stdio.h>
-#include <QDebug>
-#include <QWindow>
-
-#include "BasicScene.h"
-
-
-using namespace OrthancStone;
-
-
-
-static void GLAPIENTRY OpenGLMessageCallback(GLenum source,
-                                             GLenum type,
-                                             GLuint id,
-                                             GLenum severity,
-                                             GLsizei length,
-                                             const GLchar* message,
-                                             const void* userParam )
-{
-  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
-  {
-    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
-            ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
-            type, severity, message );
-  }
-}
-
-extern void InitGL();
-
-#include <QApplication>
-#include "BasicSceneWindow.h"
-
-int main(int argc, char* argv[])
-{
-  QApplication a(argc, argv);
-
-  OrthancStone::Samples::BasicSceneWindow window;
-  window.show();
-  window.GetOpenGlWidget().Init();
-
-  MessageBroker broker;
-  boost::shared_ptr<UndoStack> undoStack(new UndoStack);
-  boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), window.GetOpenGlWidget());
-  PrepareScene(controller->GetScene());
-
-  window.GetOpenGlWidget().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
-                     BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1);
-
-  boost::shared_ptr<OrthancStone::Scene2DInteractor> interactor(new BasicScene2DInteractor(controller));
-  window.GetOpenGlWidget().SetInteractor(interactor);
-
-  controller->FitContent();
-
-  return a.exec();
-}
--- a/Samples/Deprecated/MultiPlatform/BasicScene/mainSdl.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,199 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-// From Stone
-#include "Framework/Viewport/SdlViewport.h"
-#include "Framework/Scene2D/OpenGLCompositor.h"
-#include "Framework/Scene2DViewport/UndoStack.h"
-#include "Framework/StoneInitialization.h"
-#include "Framework/Messages/MessageBroker.h"
-
-// From Orthanc framework
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-#include <boost/make_shared.hpp>
-#include <boost/ref.hpp>
-
-#include <SDL.h>
-#include <stdio.h>
-
-
-#include "BasicScene.h"
-
-using namespace OrthancStone;
-
-boost::shared_ptr<BasicScene2DInteractor> interactor;
-
-void HandleApplicationEvent(boost::shared_ptr<OrthancStone::ViewportController> controller,
-                            const SDL_Event& event)
-{
-  using namespace OrthancStone;
-  Scene2D& scene(controller->GetScene());
-  if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEMOTION)
-  {
-    // TODO: this code is copy/pasted from GuiAdapter::Run() -> find the right place
-    int scancodeCount = 0;
-    const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
-    bool ctrlPressed(false);
-    bool shiftPressed(false);
-    bool altPressed(false);
-
-    if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL])
-      ctrlPressed = true;
-    if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL])
-      ctrlPressed = true;
-    if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT])
-      shiftPressed = true;
-    if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT])
-      shiftPressed = true;
-    if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT])
-      altPressed = true;
-
-    GuiAdapterMouseEvent guiEvent;
-    ConvertFromPlatform(guiEvent, ctrlPressed, shiftPressed, altPressed, event);
-    PointerEvent pointerEvent;
-    pointerEvent.AddPosition(controller->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
-
-    interactor->OnMouseEvent(guiEvent, pointerEvent);
-    return;
-  }
-  else if ((event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) && event.key.repeat == 0  /* Ignore key bounce */)
-  {
-    GuiAdapterKeyboardEvent guiEvent;
-    ConvertFromPlatform(guiEvent, event);
-
-    interactor->OnKeyboardEvent(guiEvent);
-  }
-
-}
-
-
-static void GLAPIENTRY
-OpenGLMessageCallback(GLenum source,
-                      GLenum type,
-                      GLuint id,
-                      GLenum severity,
-                      GLsizei length,
-                      const GLchar* message,
-                      const void* userParam )
-{
-  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
-  {
-    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
-            ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
-            type, severity, message );
-  }
-}
-
-
-void Run(boost::shared_ptr<OrthancStone::ViewportController> controller)
-{
-  SdlViewport& sdlViewport = dynamic_cast<SdlViewport&>(controller->GetViewport());
-
-  glEnable(GL_DEBUG_OUTPUT);
-  glDebugMessageCallback(OpenGLMessageCallback, 0);
-
-  controller->GetViewport().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
-                     BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1);
-
-  controller->GetViewport().Refresh();
-  controller->FitContent();
-
-
-  bool stop = false;
-  while (!stop)
-  {
-    controller->GetViewport().Refresh();
-
-    SDL_Event event;
-    while (!stop &&
-           SDL_PollEvent(&event))
-    {
-      if (event.type == SDL_QUIT)
-      {
-        stop = true;
-        break;
-      }
-      else if (event.type == SDL_WINDOWEVENT &&
-               event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
-      {
-        sdlViewport.UpdateSize(event.window.data1, event.window.data2);
-      }
-      else if (event.type == SDL_KEYDOWN &&
-               event.key.repeat == 0 /* Ignore key bounce */)
-      {
-        switch (event.key.keysym.sym)
-        {
-          case SDLK_f:
-            sdlViewport.GetWindow().ToggleMaximize();
-            break;
-              
-          case SDLK_q:
-            stop = true;
-            break;
-
-          default:
-            break;
-        }
-      }
-      
-      HandleApplicationEvent(controller, event);
-    }
-
-    SDL_Delay(1);
-  }
-  interactor.reset();
-}
-
-
-
-
-/**
- * IMPORTANT: The full arguments to "main()" are needed for SDL on
- * Windows. Otherwise, one gets the linking error "undefined reference
- * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
- **/
-int main(int argc, char* argv[])
-{
-  using namespace OrthancStone;
-  StoneInitialize();
-  Orthanc::Logging::EnableInfoLevel(true);
-
-  try
-  {
-    SdlOpenGLViewport viewport("Hello", 1024, 768);
-    MessageBroker broker;
-    boost::shared_ptr<UndoStack> undoStack(new UndoStack);
-    boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), boost::ref(viewport));
-    interactor.reset(new BasicScene2DInteractor(controller));
-    PrepareScene(controller->GetScene());
-    Run(controller);
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "EXCEPTION: " << e.What();
-  }
-
-  StoneFinalize();
-
-  return 0;
-}
--- a/Samples/Deprecated/Qt/BasicSceneWindow.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/**
- * 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/>.
- **/
-#include "../../Framework/OpenGL/OpenGLIncludes.h"
-#include "BasicSceneWindow.h"
-
-/**
- * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as
- * this makes CMake unable to detect when the UI file changes.
- **/
-#include <ui_BasicSceneWindow.h>
-#include "../../Applications/Samples/SampleApplicationBase.h"
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-
-    BasicSceneWindow::BasicSceneWindow(
-      QWidget *parent) :
-      ui_(new Ui::BasicSceneWindow)
-    {
-      ui_->setupUi(this);
-    }
-
-    BasicSceneWindow::~BasicSceneWindow()
-    {
-      delete ui_;
-    }
-
-    QStoneOpenGlWidget& BasicSceneWindow::GetOpenGlWidget()
-    {
-      return *(ui_->centralWidget);
-    }
-
-  }
-}
--- a/Samples/Deprecated/Qt/BasicSceneWindow.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/**
- * 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 <QMainWindow>
-#include <QStoneOpenGlWidget.h>
-// #include "../../Qt/QCairoWidget.h"
-// #include "../../Qt/QStoneMainWindow.h"
-
-namespace Ui 
-{
-  class BasicSceneWindow;
-}
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-
-    //class SampleSingleCanvasApplicationBase;
-
-    class BasicSceneWindow : public QMainWindow
-    {
-      Q_OBJECT
-
-    private:
-      Ui::BasicSceneWindow*   ui_;
-      //SampleSingleCanvasApplicationBase&  stoneSampleApplication_;
-
-    public:
-      explicit BasicSceneWindow(QWidget *parent = 0);
-      ~BasicSceneWindow();
-
-      QStoneOpenGlWidget& GetOpenGlWidget();
-    };
-  }
-}
--- a/Samples/Deprecated/Qt/BasicSceneWindow.ui	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>BasicSceneWindow</class>
- <widget class="QMainWindow" name="BasicSceneWindow">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>903</width>
-    <height>634</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>500</width>
-    <height>300</height>
-   </size>
-  </property>
-  <property name="baseSize">
-   <size>
-    <width>500</width>
-    <height>300</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Stone of Orthanc</string>
-  </property>
-  <property name="layoutDirection">
-   <enum>Qt::LeftToRight</enum>
-  </property>
-  <widget class="QWidget" name="mainWidget">
-   <property name="sizePolicy">
-    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-     <horstretch>0</horstretch>
-     <verstretch>0</verstretch>
-    </sizepolicy>
-   </property>
-   <property name="layoutDirection">
-    <enum>Qt::LeftToRight</enum>
-   </property>
-   <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0">
-    <property name="sizeConstraint">
-     <enum>QLayout::SetDefaultConstraint</enum>
-    </property>
-    <item>
-     <widget class="OrthancStone::QStoneOpenGlWidget" name="centralWidget" native="true">
-      <property name="minimumSize">
-       <size>
-        <width>0</width>
-        <height>500</height>
-       </size>
-      </property>
-     </widget>
-    </item>
-   </layout>
-  </widget>
-  <widget class="QMenuBar" name="menubar">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>0</y>
-     <width>903</width>
-     <height>21</height>
-    </rect>
-   </property>
-   <widget class="QMenu" name="menuTest">
-    <property name="title">
-     <string>Test</string>
-    </property>
-   </widget>
-   <addaction name="menuTest"/>
-  </widget>
-  <widget class="QStatusBar" name="statusbar"/>
- </widget>
- <customwidgets>
-  <customwidget>
-   <class>QStoneOpenGlWidget</class>
-   <extends>QWidget</extends>
-   <header location="global">QStoneOpenGlWidget.h</header>
-  </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
--- a/Samples/Deprecated/Qt/CMakeLists.txt	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-cmake_minimum_required(VERSION 2.8.3)
-
-#####################################################################
-## Configuration of the Orthanc framework
-#####################################################################
-
-# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it
-# must be the first inclusion
-include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/Version.cmake)
-
-if (ORTHANC_STONE_VERSION STREQUAL "mainline")
-  set(ORTHANC_FRAMEWORK_VERSION "mainline")
-  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
-else()
-  set(ORTHANC_FRAMEWORK_VERSION "1.5.7")
-  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
-endif()
-
-set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")")
-set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"")
-set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
-
-
-#####################################################################
-## Configuration of the Stone framework
-#####################################################################
-
-include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake)
-include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
-
-DownloadPackage(
-  "a24b8136b8f3bb93f166baf97d9328de"
-  "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip"
-  "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83")
-
-set(ORTHANC_STONE_APPLICATION_RESOURCES
-  UBUNTU_FONT  ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf
-  )
-
-SET(ENABLE_GOOGLE_TEST OFF)
-SET(ENABLE_LOCALE ON)
-SET(ENABLE_QT ON)
-SET(ENABLE_SDL OFF)
-SET(ENABLE_WEB_CLIENT ON)
-SET(ORTHANC_SANDBOXED OFF)
-LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
-
-include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake)
-
-add_definitions(
-  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
-  )
-#####################################################################
-## Build the samples
-#####################################################################
-
-add_library(OrthancStone STATIC
-  ${ORTHANC_STONE_SOURCES}
-  )
-
-list(APPEND BASIC_SCENE_APPLICATIONS_SOURCES
-  BasicSceneWindow.cpp
-  )
-
-ORTHANC_QT_WRAP_UI(BASIC_SCENE_APPLICATIONS_SOURCES
-  BasicSceneWindow.ui
-  )
-
-ORTHANC_QT_WRAP_CPP(BASIC_SCENE_APPLICATIONS_SOURCES
-  BasicSceneWindow.h
-  QStoneOpenGlWidget.h
-  )
-
-add_executable(MpBasicScene
-  ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.h
-  ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.cpp
-  ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/mainQt.cpp
-  QStoneOpenGlWidget.cpp
-  ${BASIC_SCENE_APPLICATIONS_SOURCES}
-  )
-
-target_include_directories(MpBasicScene PUBLIC ${CMAKE_SOURCE_DIR} ${ORTHANC_STONE_ROOT})
-target_link_libraries(MpBasicScene OrthancStone)
--- a/Samples/Deprecated/Qt/QStoneOpenGlWidget.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,171 +0,0 @@
-#include "../../Framework/OpenGL/OpenGLIncludes.h"
-#include "QStoneOpenGlWidget.h"
-
-#include <QMouseEvent>
-
-using namespace OrthancStone;
-
-void QStoneOpenGlWidget::initializeGL()
-{
-  glewInit();
-}
-
-void QStoneOpenGlWidget::MakeCurrent()
-{
-  this->makeCurrent();
-}
-
-void QStoneOpenGlWidget::resizeGL(int w, int h)
-{
-
-}
-
-void QStoneOpenGlWidget::paintGL()
-{
-  if (compositor_)
-  {
-    compositor_->Refresh();
-  }
-  doneCurrent();
-}
-
-void ConvertFromPlatform(
-    OrthancStone::GuiAdapterMouseEvent& guiEvent,
-    PointerEvent& pointerEvent,
-    const QMouseEvent& qtEvent,
-    const IViewport& viewport)
-{
-  guiEvent.targetX = qtEvent.x();
-  guiEvent.targetY = qtEvent.y();
-  pointerEvent.AddPosition(viewport.GetPixelCenterCoordinates(guiEvent.targetX, guiEvent.targetY));
-
-  switch (qtEvent.button())
-  {
-  case Qt::LeftButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT; break;
-  case Qt::MiddleButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_MIDDLE; break;
-  case Qt::RightButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_RIGHT; break;
-  default:
-    guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT;
-  }
-
-  if (qtEvent.modifiers().testFlag(Qt::ShiftModifier))
-  {
-    guiEvent.shiftKey = true;
-  }
-  if (qtEvent.modifiers().testFlag(Qt::ControlModifier))
-  {
-    guiEvent.ctrlKey = true;
-  }
-  if (qtEvent.modifiers().testFlag(Qt::AltModifier))
-  {
-    guiEvent.altKey = true;
-  }
-}
-
-void QStoneOpenGlWidget::mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType)
-{
-  OrthancStone::GuiAdapterMouseEvent guiEvent;
-  PointerEvent pointerEvent;
-  ConvertFromPlatform(guiEvent, pointerEvent, *qtEvent, *this);
-  guiEvent.type = guiEventType;
-
-  if (sceneInteractor_.get() != NULL && compositor_.get() != NULL)
-  {
-    sceneInteractor_->OnMouseEvent(guiEvent, pointerEvent);
-  }
-
-  // force redraw of the OpenGL widget
-  update();
-}
-
-void QStoneOpenGlWidget::mousePressEvent(QMouseEvent* qtEvent)
-{
-  mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEDOWN);
-}
-
-void QStoneOpenGlWidget::mouseMoveEvent(QMouseEvent* qtEvent)
-{
-  mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEMOVE);
-}
-
-void QStoneOpenGlWidget::mouseReleaseEvent(QMouseEvent* qtEvent)
-{
-  mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEUP);
-}
-
-void ConvertFromPlatform(
-    OrthancStone::GuiAdapterKeyboardEvent& guiEvent,
-    const QKeyEvent& qtEvent)
-{
-  if (qtEvent.text().length() > 0)
-  {
-    guiEvent.sym[0] = qtEvent.text()[0].cell();
-  }
-  else
-  {
-    guiEvent.sym[0] = 0;
-  }
-  guiEvent.sym[1] = 0;
-
-  if (qtEvent.modifiers().testFlag(Qt::ShiftModifier))
-  {
-    guiEvent.shiftKey = true;
-  }
-  if (qtEvent.modifiers().testFlag(Qt::ControlModifier))
-  {
-    guiEvent.ctrlKey = true;
-  }
-  if (qtEvent.modifiers().testFlag(Qt::AltModifier))
-  {
-    guiEvent.altKey = true;
-  }
-
-}
-
-
-bool QStoneOpenGlWidget::keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType)
-{
-  bool handled = false;
-  OrthancStone::GuiAdapterKeyboardEvent guiEvent;
-  ConvertFromPlatform(guiEvent, *qtEvent);
-  guiEvent.type = guiEventType;
-
-  if (sceneInteractor_.get() != NULL && compositor_.get() != NULL)
-  {
-    handled = sceneInteractor_->OnKeyboardEvent(guiEvent);
-
-    if (handled)
-    {
-      // force redraw of the OpenGL widget
-      update();
-    }
-  }
-  return handled;
-}
-
-void QStoneOpenGlWidget::keyPressEvent(QKeyEvent *qtEvent)
-{
-  bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYDOWN);
-  if (!handled)
-  {
-    QOpenGLWidget::keyPressEvent(qtEvent);
-  }
-}
-
-void QStoneOpenGlWidget::keyReleaseEvent(QKeyEvent *qtEvent)
-{
-  bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYUP);
-  if (!handled)
-  {
-    QOpenGLWidget::keyPressEvent(qtEvent);
-  }
-}
-
-void QStoneOpenGlWidget::wheelEvent(QWheelEvent *qtEvent)
-{
-  OrthancStone::GuiAdapterWheelEvent guiEvent;
-  throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-
-  // force redraw of the OpenGL widget
-  update();
-}
--- a/Samples/Deprecated/Qt/QStoneOpenGlWidget.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-#pragma once
-#include "../../Framework/OpenGL/OpenGLIncludes.h"
-#include <QOpenGLWidget>
-#include <QOpenGLFunctions>
-#include <QOpenGLContext>
-
-#include <boost/shared_ptr.hpp>
-#include "../../Framework/OpenGL/IOpenGLContext.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/Viewport/ViewportBase.h"
-#include "../../Applications/Generic/Scene2DInteractor.h"
-
-namespace OrthancStone
-{
-  class QStoneOpenGlWidget :
-      public QOpenGLWidget,
-      public OpenGL::IOpenGLContext,
-      public ViewportBase
-  {
-    std::unique_ptr<OrthancStone::OpenGLCompositor> compositor_;
-    boost::shared_ptr<Scene2DInteractor> sceneInteractor_;
-    QOpenGLContext                        openGlContext_;
-
-  public:
-    QStoneOpenGlWidget(QWidget *parent) :
-      QOpenGLWidget(parent),
-      ViewportBase("QtStoneOpenGlWidget")  // TODO: we shall be able to define a name but construction time is too early !
-    {
-      setFocusPolicy(Qt::StrongFocus);  // to enable keyPressEvent
-      setMouseTracking(true);           // to enable mouseMoveEvent event when no button is pressed
-    }
-
-    void Init()
-    {
-      QSurfaceFormat requestedFormat;
-      requestedFormat.setVersion( 2, 0 );
-      openGlContext_.setFormat( requestedFormat );
-      openGlContext_.create();
-      openGlContext_.makeCurrent(context()->surface());
-
-      compositor_.reset(new OpenGLCompositor(*this, GetScene()));
-    }
-
-  protected:
-
-    //**** QWidget overrides
-    void initializeGL() override;
-    void resizeGL(int w, int h) override;
-    void paintGL() override;
-
-    void mousePressEvent(QMouseEvent* event) override;
-    void mouseMoveEvent(QMouseEvent* event) override;
-    void mouseReleaseEvent(QMouseEvent* event) override;
-    void keyPressEvent(QKeyEvent* event) override;
-    void keyReleaseEvent(QKeyEvent *event) override;
-    void wheelEvent(QWheelEvent* event) override;
-
-    //**** IOpenGLContext overrides
-
-    virtual void MakeCurrent() override;
-    virtual void SwapBuffer() override {}
-
-    virtual unsigned int GetCanvasWidth() const override
-    {
-      return this->width();
-    }
-
-    virtual unsigned int GetCanvasHeight() const override
-    {
-      return this->height();
-    }
-
-  public:
-
-    void SetInteractor(boost::shared_ptr<Scene2DInteractor> sceneInteractor)
-    {
-      sceneInteractor_ = sceneInteractor;
-    }
-
-    virtual ICompositor& GetCompositor()
-    {
-      return *compositor_;
-    }
-
-  protected:
-    void mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType);
-    bool keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType);
-
-  };
-}
--- a/Samples/Deprecated/Qt/Scene2DInteractor.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-#include "Scene2DInteractor.h"
-
-#include "../../Framework/Scene2D/PanSceneTracker.h"
-#include "../../Framework/Scene2D/ZoomSceneTracker.h"
-#include "../../Framework/Scene2D/RotateSceneTracker.h"
-
-
-namespace OrthancStone
-{
-
-}
-
-using namespace OrthancStone;
-
-
-bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent)
-{
-  if (currentTracker_.get() != NULL)
-  {
-    switch (event.type)
-    {
-    case GUIADAPTER_EVENT_MOUSEUP:
-    {
-      currentTracker_->PointerUp(pointerEvent);
-      if (!currentTracker_->IsAlive())
-      {
-        currentTracker_.reset();
-      }
-    };break;
-    case GUIADAPTER_EVENT_MOUSEMOVE:
-    {
-      currentTracker_->PointerMove(pointerEvent);
-    };break;
-    }
-    return true;
-  }
-  else
-  {
-    if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT)
-    {
-      currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent));
-    }
-    else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE)
-    {
-      currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent));
-    }
-    else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT && compositor_.get() != NULL)
-    {
-      currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, compositor_->GetHeight()));
-    }
-    return true;
-  }
-  return false;
-}
-
-bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent)
-{
-  switch (guiEvent.sym[0])
-  {
-  case 's':
-  {
-    viewportController_->FitContent(compositor_->GetWidth(), compositor_->GetHeight());
-    return true;
-  };
-  }
-  return false;
-}
-
-bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent)
-{
-  return false;
-}
--- a/Samples/Deprecated/Qt/Scene2DInteractor.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-#pragma once
-
-#include "../../Applications/Generic/Scene2DInteractor.h"
-#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
-
-
-class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor
-{
-  boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>  currentTracker_;
-public:
-  BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) :
-    Scene2DInteractor(viewportController)
-  {}
-
-  virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override;
-  virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent);
-  virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent);
-};
-
--- a/Samples/Deprecated/README.md	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,195 +0,0 @@
-
-**These samples are deprecated and not guaranteed to work. A migration to a 
-new version of the Stone API is underway.**
-
-Please read orthanc-stone/Samples/README.md first for general requirements.
-
-
-
-Deprecated -- to be sorted
-===========================
-
-The following assumes that the source code to be downloaded in
-`~/orthanc-stone` and Orthanc source code to be checked out in
-`~/orthanc`. 
-
-Building the WASM samples
--------------------------------------
-
-```
-cd ~/orthanc-stone/Applications/Samples
-./build-wasm.sh
-```
-
-Serving the WASM samples
-------------------------------------
-```
-# launch an Orthanc listening on 8042 port:
-Orthanc
-
-# launch an nginx that will serve the WASM static files and reverse
-# proxy
-sudo nginx -p $(pwd) -c nginx.local.conf
-```
-
-You can now open the samples in http://localhost:9977
-
-Building the SDL native samples (SimpleViewer only)
----------------------------------------------------
-
-The following also assumes that you have checked out the Orthanc
-source code in an `orthanc` folder next to the Stone of Orthanc
-repository, please enter the following:
-
-**Simple make generator with dynamic build**
-
-```
-# Please set $currentDir to the current folder
-mkdir -p ~/builds/orthanc-stone-build
-cd ~/builds/orthanc-stone-build
-cmake -DORTHANC_FRAMEWORK_SOURCE=path \
-    -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc \
-    -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON \
-    ~/orthanc-stone/Applications/Samples/
-```
-
-**Ninja generator with static SDL build (pwsh script)**
-
-```
-# Please yourself one level above the orthanc-stone and orthanc folders
-if( -not (test-path stone_build_sdl)) { mkdir stone_build_sdl }
-cd stone_build_sdl
-cmake -G Ninja -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples/
-```
-
-**Ninja generator with static SDL build (bash/zsh script)**
-
-```
-# Please yourself one level above the orthanc-stone and orthanc folders
-if( -not (test-path stone_build_sdl)) { mkdir stone_build_sdl }
-cd stone_build_sdl
-cmake -G Ninja -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="`pwd`/../orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples/
-```
-
-**Visual Studio 2017 generator with static SDL build  (pwsh script)**
-
-```
-# The following will use Visual Studio 2017 to build the SDL samples
-# in debug mode (with multiple compilers in parallel). NOTE: place 
-# yourself one level above the `orthanc-stone` and `orthanc` folders
-
-if( -not (test-path stone_build_sdl)) { mkdir stone_build_sdl }
-cd stone_build_sdl
-cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples/
-cmake --build . --config Debug
-```
-
-If you are working on Windows, add the correct generator option to
-cmake to, for instance, generate msbuild files for Visual Studio.
-
-Then, under Linux:
-```
-cmake --build . --target OrthancStoneSimpleViewer -- -j 5
-```
-
-Note: replace `$($pwd)` with the current directory when not using Powershell
-
-Building the Qt native samples (SimpleViewer only) under Windows:
-------------------------------------------------------------------
-
-**Visual Studio 2017 generator with static Qt build  (pwsh script)**
-
-For instance, if Qt is installed in `C:\Qt\5.12.0\msvc2017_64`
-
-```
-# The following will use Visual Studio 2017 to build the SDL samples
-# in debug mode (with multiple compilers in parallel). NOTE: place 
-# yourself one level above the `orthanc-stone` and `orthanc` folders
-
-if( -not (test-path stone_build_qt)) { mkdir stone_build_qt }
-cd stone_build_qt
-cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DCMAKE_PREFIX_PATH=C:\Qt\5.12.0\msvc2017_64 -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_QT=ON  ../orthanc-stone/Applications/Samples/
-cmake --build . --config Debug
-```
-
-Note: replace `$($pwd)` with the current directory when not using Powershell
-
-
-
-
-
-
-Building the SDL native samples (SimpleViewer only) under Windows:
-------------------------------------------------------------------
-`cmake -DSTATIC_BUILD=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON -G "Visual Studio 15 2017 Win64" ../orthanc-stone/Applications/Samples/`
-
-Note: replace `$($pwd)` with the current directory when not using Powershell
-
-Executing the native samples:
---------------------------------
-```
-# launch an Orthanc listening on 8042 port:
-Orthanc
-
-# launch the sample
-./OrthancStoneSimpleViewer --studyId=XX
-``` 
-
-Build the Application Samples
------------------------------
-
-**Visual Studio 2008 (v90) **
-
-```
-cmake -G "Visual Studio 9 2008" -DUSE_LEGACY_JSONCPP=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples
-```
-
-**Visual Studio 2019 (v142) **
-
-```
-cmake -G "Visual Studio 16 2019" -A x64 -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples
-```
-
-**Visual Studio 2017 (v140) **
-
-```
-cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples
-```
-
-
-Build the core Samples
----------------------------
-How to build the newest (2019-04-29) SDL samples under Windows, *inside* a
-folder that is sibling to the orthanc-stone folder: 
-
-**Visual Studio 2019 (v142) **
-
-```
-cmake -G "Visual Studio 16 2019" -A x64 -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl
-```
-
-**Visual Studio 2017 (v140) **
-
-```
-cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl
-```
-
-**Visual Studio 2008 (v90) **
-
-```
-cmake -G "Visual Studio 9 2008" -DUSE_LEGACY_JSONCPP=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl
-```
-
-And under Ubuntu (note the /mnt/c/osi/dev/orthanc folder):
-```
-cmake -G "Ninja" -DENABLE_OPENGL=ON -DSTATIC_BUILD=OFF -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="/mnt/c/osi/dev/orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl
-```
-
-TODO trackers:
-- CANCELLED (using outlined text now) text overlay 50% --> ColorTextureLayer 50%
-- DONE angle tracker: draw arcs
-- Handles on arc
-- Select measure tool with hit test --> Delete command
-
-
-
--- a/Samples/Deprecated/Sdl/BasicScene.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,418 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-// From Stone
-#include "../../Framework/Viewport/SdlViewport.h"
-#include "../../Framework/Scene2D/CairoCompositor.h"
-#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/Scene2D/PanSceneTracker.h"
-#include "../../Framework/Scene2D/RotateSceneTracker.h"
-#include "../../Framework/Scene2D/ZoomSceneTracker.h"
-#include "../../Framework/Scene2DViewport/ViewportController.h"
-#include "../../Framework/Scene2DViewport/UndoStack.h"
-
-#include "../../Framework/StoneInitialization.h"
-#include "../../Framework/Messages/MessageBroker.h"
-
-// From Orthanc framework
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Images/PngWriter.h>
-
-#include <boost/make_shared.hpp>
-
-#include <SDL.h>
-#include <stdio.h>
-
-static const unsigned int FONT_SIZE = 32;
-static const int LAYER_POSITION = 150;
-
-#define OPENGL_ENABLED 0
-
-void PrepareScene(OrthancStone::Scene2D& scene)
-{
-  using namespace OrthancStone;
-
-  // Texture of 2x2 size
-  {
-    Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false);
-    
-    uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0));
-    p[0] = 255;
-    p[1] = 0;
-    p[2] = 0;
-
-    p[3] = 0;
-    p[4] = 255;
-    p[5] = 0;
-
-    p = reinterpret_cast<uint8_t*>(i.GetRow(1));
-    p[0] = 0;
-    p[1] = 0;
-    p[2] = 255;
-
-    p[3] = 255;
-    p[4] = 0;
-    p[5] = 0;
-
-    scene.SetLayer(12, new ColorTextureSceneLayer(i));
-
-    std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
-    l->SetOrigin(-3, 2);
-    l->SetPixelSpacing(1.5, 1);
-    l->SetAngle(20.0 / 180.0 * M_PI);
-    scene.SetLayer(14, l.release());
-  }
-
-  // Texture of 1x1 size
-  {
-    Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false);
-    
-    uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0));
-    p[0] = 255;
-    p[1] = 0;
-    p[2] = 0;
-
-    std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
-    l->SetOrigin(-2, 1);
-    l->SetAngle(20.0 / 180.0 * M_PI);
-    scene.SetLayer(13, l.release());
-  }
-
-  // Some lines
-  {
-    std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer);
-
-    layer->SetThickness(10);
-
-    PolylineSceneLayer::Chain chain;
-    chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5));
-    chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5));
-    chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5));
-    chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5));
-    layer->AddChain(chain, true, 255, 0, 0);
-
-    chain.clear();
-    chain.push_back(ScenePoint2D(-5, -5));
-    chain.push_back(ScenePoint2D(5, -5));
-    chain.push_back(ScenePoint2D(5, 5));
-    chain.push_back(ScenePoint2D(-5, 5));
-    layer->AddChain(chain, true, 0, 255, 0);
-
-    double dy = 1.01;
-    chain.clear();
-    chain.push_back(ScenePoint2D(-4, -4));
-    chain.push_back(ScenePoint2D(4, -4 + dy));
-    chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy));
-    chain.push_back(ScenePoint2D(4, 2));
-    layer->AddChain(chain, false, 0, 0, 255);
-
-    scene.SetLayer(50, layer.release());
-  }
-
-  // Some text
-  {
-    std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-    layer->SetText("Hello");
-    scene.SetLayer(100, layer.release());
-  }
-}
-
-
-void TakeScreenshot(const std::string& target,
-                    const OrthancStone::Scene2D& scene,
-                    unsigned int canvasWidth,
-                    unsigned int canvasHeight)
-{
-  using namespace OrthancStone;
-  // Take a screenshot, then save it as PNG file
-  CairoCompositor compositor(scene, canvasWidth, canvasHeight);
-  compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE, Orthanc::Encoding_Latin1);
-  compositor.Refresh();
-
-  Orthanc::ImageAccessor canvas;
-  compositor.GetCanvas().GetReadOnlyAccessor(canvas);
-
-  Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
-  Orthanc::ImageProcessing::Convert(png, canvas);
-        
-  Orthanc::PngWriter writer;
-  writer.WriteToFile(target, png);
-}
-
-
-void HandleApplicationEvent(const SDL_Event& event,
-                            boost::shared_ptr<OrthancStone::ViewportController>& controller,
-                            boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>& activeTracker)
-{
-  using namespace OrthancStone;
-
-  Scene2D& scene = controller->GetScene();
-  IViewport& viewport = controller->GetViewport();
-
-  if (event.type == SDL_MOUSEMOTION)
-  {
-    int scancodeCount = 0;
-    const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
-
-    if (activeTracker.get() == NULL &&
-        SDL_SCANCODE_LCTRL < scancodeCount &&
-        keyboardState[SDL_SCANCODE_LCTRL])
-    {
-      // The "left-ctrl" key is down, while no tracker is present
-
-      PointerEvent e;
-      e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y));
-
-      ScenePoint2D p = e.GetMainPosition().Apply(scene.GetCanvasToSceneTransform());
-
-      char buf[64];
-      sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY());
-
-      if (scene.HasLayer(LAYER_POSITION))
-      {
-        TextSceneLayer& layer =
-          dynamic_cast<TextSceneLayer&>(scene.GetLayer(LAYER_POSITION));
-        layer.SetText(buf);
-        layer.SetPosition(p.GetX(), p.GetY());
-      }
-      else
-      {
-        std::unique_ptr<TextSceneLayer> 
-          layer(new TextSceneLayer);
-        layer->SetColor(0, 255, 0);
-        layer->SetText(buf);
-        layer->SetBorder(20);
-        layer->SetAnchor(BitmapAnchor_BottomCenter);
-        layer->SetPosition(p.GetX(), p.GetY());
-        scene.SetLayer(LAYER_POSITION, layer.release());
-      }
-    }
-    else
-    {
-      scene.DeleteLayer(LAYER_POSITION);
-    }
-  }
-  else if (event.type == SDL_MOUSEBUTTONDOWN)
-  {
-    PointerEvent e;
-    e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y));
-
-    switch (event.button.button)
-    {
-      case SDL_BUTTON_MIDDLE:
-        activeTracker = boost::make_shared<PanSceneTracker>(controller, e);
-        break;
-
-      case SDL_BUTTON_RIGHT:
-        activeTracker = boost::make_shared<ZoomSceneTracker>
-          (controller, e, viewport.GetCanvasHeight());
-        break;
-
-      case SDL_BUTTON_LEFT:
-        activeTracker = boost::make_shared<RotateSceneTracker>(controller, e);
-        break;
-
-      default:
-        break;
-    }
-  }
-  else if (event.type == SDL_KEYDOWN &&
-           event.key.repeat == 0 /* Ignore key bounce */)
-  {
-    switch (event.key.keysym.sym)
-    {
-      case SDLK_s:
-        controller->FitContent(viewport.GetCanvasWidth(), 
-                               viewport.GetCanvasHeight());
-        break;
-              
-      case SDLK_c:
-        TakeScreenshot("screenshot.png", scene, 
-                       viewport.GetCanvasWidth(), 
-                       viewport.GetCanvasHeight());
-        break;
-              
-      default:
-        break;
-    }
-  }
-}
-
-#if OPENGL_ENABLED==1
-static void GLAPIENTRY
-OpenGLMessageCallback(GLenum source,
-                      GLenum type,
-                      GLuint id,
-                      GLenum severity,
-                      GLsizei length,
-                      const GLchar* message,
-                      const void* userParam )
-{
-  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
-  {
-    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
-            ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
-            type, severity, message );
-  }
-}
-#endif
-
-void Run(OrthancStone::MessageBroker& broker,
-         OrthancStone::SdlViewport& viewport)
-{
-  using namespace OrthancStone;
-  
-  boost::shared_ptr<ViewportController> controller(
-    new ViewportController(boost::make_shared<UndoStack>(), broker, viewport));
-  
-#if OPENGL_ENABLED==1
-  glEnable(GL_DEBUG_OUTPUT);
-  glDebugMessageCallback(OpenGLMessageCallback, 0);
-#endif
-
-  boost::shared_ptr<IFlexiblePointerTracker> tracker;
-
-  bool firstShown = true;
-  bool stop = false;
-  while (!stop)
-  {
-    viewport.Refresh();
-
-    SDL_Event event;
-    while (!stop &&
-           SDL_PollEvent(&event))
-    {
-      if (event.type == SDL_QUIT)
-      {
-        stop = true;
-        break;
-      }
-      else if (event.type == SDL_MOUSEMOTION)
-      {
-        if (tracker)
-        {
-          PointerEvent e;
-          e.AddPosition(viewport.GetPixelCenterCoordinates(
-            event.button.x, event.button.y));
-          tracker->PointerMove(e);
-        }
-      }
-      else if (event.type == SDL_MOUSEBUTTONUP)
-      {
-        if (tracker)
-        {
-          PointerEvent e;
-          e.AddPosition(viewport.GetPixelCenterCoordinates(
-            event.button.x, event.button.y));
-          tracker->PointerUp(e);
-          if(!tracker->IsAlive())
-            tracker.reset();
-        }
-      }
-      else if (event.type == SDL_WINDOWEVENT)
-      {
-        switch (event.window.event)
-        {
-          case SDL_WINDOWEVENT_SIZE_CHANGED:
-            tracker.reset();
-            viewport.UpdateSize(event.window.data1, event.window.data2);
-            break;
-
-          case SDL_WINDOWEVENT_SHOWN:
-            if (firstShown)
-            {
-              // Once the window is first shown, fit the content to its size
-              controller->FitContent(viewport.GetCanvasWidth(), viewport.GetCanvasHeight());
-              firstShown = false;
-            }
-            
-            break;
-
-          default:
-            break;
-        }
-      }
-      else if (event.type == SDL_KEYDOWN &&
-               event.key.repeat == 0 /* Ignore key bounce */)
-      {
-        switch (event.key.keysym.sym)
-        {
-          case SDLK_f:
-            viewport.GetWindow().ToggleMaximize();
-            break;
-              
-          case SDLK_q:
-            stop = true;
-            break;
-
-          default:
-            break;
-        }
-      }
-      
-      HandleApplicationEvent(event, controller, tracker);
-    }
-
-    SDL_Delay(1);
-  }
-}
-
-
-
-
-/**
- * IMPORTANT: The full arguments to "main()" are needed for SDL on
- * Windows. Otherwise, one gets the linking error "undefined reference
- * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
- **/
-int main(int argc, char* argv[])
-{
-  OrthancStone::StoneInitialize();
-  Orthanc::Logging::EnableInfoLevel(true);
-
-  try
-  {
-#if OPENGL_ENABLED==1
-    OrthancStone::SdlOpenGLViewport viewport("Hello", 1024, 768);
-#else
-    OrthancStone::SdlCairoViewport viewport("Hello", 1024, 768);
-#endif
-    PrepareScene(viewport.GetScene());
-
-    viewport.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
-                                     FONT_SIZE, Orthanc::Encoding_Latin1);
-    
-    OrthancStone::MessageBroker broker;
-    Run(broker, viewport);
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "EXCEPTION: " << e.What();
-  }
-
-  OrthancStone::StoneFinalize();
-
-  return 0;
-}
--- a/Samples/Deprecated/Sdl/CMakeLists.txt	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-cmake_minimum_required(VERSION 2.8.3)
-
-#####################################################################
-## Configuration of the Orthanc framework
-#####################################################################
-
-# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it
-# must be the first inclusion
-include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/Version.cmake)
-
-if (ORTHANC_STONE_VERSION STREQUAL "mainline")
-  set(ORTHANC_FRAMEWORK_VERSION "mainline")
-  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
-else()
-  set(ORTHANC_FRAMEWORK_VERSION "1.5.7")
-  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
-endif()
-
-set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")")
-set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"")
-set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
-
-
-#####################################################################
-## Configuration of the Stone framework
-#####################################################################
-
-include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake)
-include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
-
-DownloadPackage(
-  "a24b8136b8f3bb93f166baf97d9328de"
-  "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip"
-  "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83")
-
-set(ORTHANC_STONE_APPLICATION_RESOURCES
-  UBUNTU_FONT  ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf
-  )
-
-SET(ENABLE_SDL_CONSOLE OFF CACHE BOOL "Enable the use of the MIT-licensed SDL_Console")
-SET(ENABLE_GOOGLE_TEST OFF)
-SET(ENABLE_LOCALE ON)
-SET(ENABLE_SDL ON)
-SET(ENABLE_WEB_CLIENT ON)
-SET(ORTHANC_SANDBOXED OFF)
-LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
-
-include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake)
-
-add_definitions(
-  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
-  )
-
-
-#####################################################################
-## Build the samples
-#####################################################################
-
-add_library(OrthancStone STATIC
-  ${ORTHANC_STONE_SOURCES}
-  )
-
-#
-# BasicScene
-# 
-
-add_executable(BasicScene
-  BasicScene.cpp
-  )
-
-target_link_libraries(BasicScene OrthancStone)
-
-#
-# TrackerSample
-# 
-
-LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSample.cpp")
-LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.cpp")
-LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.h")
-
-if (MSVC AND MSVC_VERSION GREATER 1700)
-  LIST(APPEND TRACKERSAMPLE_SOURCE "cpp.hint")
-endif()
-
-add_executable(TrackerSample
-  ${TRACKERSAMPLE_SOURCE}
-  )
-
-target_link_libraries(TrackerSample OrthancStone)
-
-#
-# Loader
-# 
-
-add_executable(Loader
-  Loader.cpp
-  )
-
-target_link_libraries(Loader OrthancStone)
-
-#
-# FusionMprSdl
-# 
-
-add_executable(FusionMprSdl
-  FusionMprSdl.cpp
-  FusionMprSdl.h
-)
-
-target_link_libraries(FusionMprSdl OrthancStone)
-
-#
-# Multiplatform Basic Scene
-#
-
-LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.cpp")
-LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.h")
-LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/mainSdl.cpp")
-
-if (MSVC AND MSVC_VERSION GREATER 1700)
-  LIST(APPEND MP_BASIC_SCENE_SOURCE "cpp.hint")
-endif()
-
-add_executable(MpBasicScene
-  ${MP_BASIC_SCENE_SOURCE}
-  )
-
-target_include_directories(MpBasicScene PUBLIC ${ORTHANC_STONE_ROOT})
-target_link_libraries(MpBasicScene OrthancStone)
--- a/Samples/Deprecated/Sdl/FusionMprSdl.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,805 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "FusionMprSdl.h"
-
-#include "../../Framework/OpenGL/SdlOpenGLContext.h"
-
-#include "../../Framework/StoneInitialization.h"
-
-#include "../../Framework/Scene2D/CairoCompositor.h"
-#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/Scene2D/PanSceneTracker.h"
-#include "../../Framework/Scene2D/ZoomSceneTracker.h"
-#include "../../Framework/Scene2D/RotateSceneTracker.h"
-
-#include "../../Framework/Scene2DViewport/UndoStack.h"
-#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h"
-#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h"
-#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
-#include "../../Framework/Scene2DViewport/MeasureTool.h"
-#include "../../Framework/Scene2DViewport/PredeclaredTypes.h"
-
-#include "../../Framework/Volumes/VolumeSceneLayerSource.h"
-
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Images/PngWriter.h>
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-#include <boost/make_shared.hpp>
-
-#include <stdio.h>
-#include "../../Framework/Oracle/GetOrthancWebViewerJpegCommand.h"
-#include "../../Framework/Oracle/ThreadedOracle.h"
-#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
-#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h"
-#include "../../Framework/Loaders/DicomStructureSetLoader.h"
-#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h"
-#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h"
-#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h"
-#include "Core/SystemToolbox.h"
-
-namespace OrthancStone
-{
-  const char* FusionMprMeasureToolToString(size_t i)
-  {
-    static const char* descs[] = {
-      "FusionMprGuiTool_Rotate",
-      "FusionMprGuiTool_Pan",
-      "FusionMprGuiTool_Zoom",
-      "FusionMprGuiTool_LineMeasure",
-      "FusionMprGuiTool_CircleMeasure",
-      "FusionMprGuiTool_AngleMeasure",
-      "FusionMprGuiTool_EllipseMeasure",
-      "FusionMprGuiTool_LAST"
-    };
-    if (i >= FusionMprGuiTool_LAST)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index");
-    }
-    return descs[i];
-  }
-
-  Scene2D& FusionMprSdlApp::GetScene()
-  {
-    return controller_->GetScene();
-  }
-
-  const Scene2D& FusionMprSdlApp::GetScene() const
-  {
-    return controller_->GetScene();
-  }
-
-  void FusionMprSdlApp::SelectNextTool()
-  {
-    currentTool_ = static_cast<FusionMprGuiTool>(currentTool_ + 1);
-    if (currentTool_ == FusionMprGuiTool_LAST)
-      currentTool_ = static_cast<FusionMprGuiTool>(0);;
-    printf("Current tool is now: %s\n", FusionMprMeasureToolToString(currentTool_));
-  }
-
-  void FusionMprSdlApp::DisplayInfoText()
-  {
-    // do not try to use stuff too early!
-    ICompositor* pCompositor = &(viewport_.GetCompositor());
-    if (pCompositor == NULL)
-      return;
-
-    std::stringstream msg;
-	
-	for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin();
-		kv != infoTextMap_.end(); ++kv)
-    {
-      msg << kv->first << " : " << kv->second << std::endl;
-    }
-	std::string msgS = msg.str();
-
-    TextSceneLayer* layerP = NULL;
-    if (GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX))
-    {
-      TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>(
-        GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX));
-      layerP = &layer;
-    }
-    else
-    {
-      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-      layerP = layer.get();
-      layer->SetColor(0, 255, 0);
-      layer->SetFontIndex(1);
-      layer->SetBorder(20);
-      layer->SetAnchor(BitmapAnchor_TopLeft);
-      //layer->SetPosition(0,0);
-      GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release());
-    }
-    // position the fixed info text in the upper right corner
-    layerP->SetText(msgS.c_str());
-    double cX = viewport_.GetCompositor().GetCanvasWidth() * (-0.5);
-    double cY = viewport_.GetCompositor().GetCanvasHeight() * (-0.5);
-    GetScene().GetCanvasToSceneTransform().Apply(cX,cY);
-    layerP->SetPosition(cX, cY);
-  }
-
-  void FusionMprSdlApp::DisplayFloatingCtrlInfoText(const PointerEvent& e)
-  {
-    ScenePoint2D p = e.GetMainPosition().Apply(GetScene().GetCanvasToSceneTransform());
-
-    char buf[128];
-    sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", 
-      p.GetX(), p.GetY(), 
-      e.GetMainPosition().GetX(), e.GetMainPosition().GetY());
-
-    if (GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX))
-    {
-      TextSceneLayer& layer =
-        dynamic_cast<TextSceneLayer&>(GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX));
-      layer.SetText(buf);
-      layer.SetPosition(p.GetX(), p.GetY());
-    }
-    else
-    {
-      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-      layer->SetColor(0, 255, 0);
-      layer->SetText(buf);
-      layer->SetBorder(20);
-      layer->SetAnchor(BitmapAnchor_BottomCenter);
-      layer->SetPosition(p.GetX(), p.GetY());
-      GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release());
-    }
-  }
-
-  void FusionMprSdlApp::HideInfoText()
-  {
-    GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX);
-  }
-
-  void FusionMprSdlApp::HandleApplicationEvent(
-    const SDL_Event & event)
-  {
-    DisplayInfoText();
-
-    if (event.type == SDL_MOUSEMOTION)
-    {
-      int scancodeCount = 0;
-      const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
-
-      if (activeTracker_.get() == NULL &&
-        SDL_SCANCODE_LALT < scancodeCount &&
-        keyboardState[SDL_SCANCODE_LALT])
-      {
-        // The "left-ctrl" key is down, while no tracker is present
-        // Let's display the info text
-        PointerEvent e;
-        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
-          event.button.x, event.button.y));
-
-        DisplayFloatingCtrlInfoText(e);
-      }
-      else
-      {
-        HideInfoText();
-        //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)";
-        if (activeTracker_.get() != NULL)
-        {
-          //LOG(TRACE) << "(activeTracker_.get() != NULL)";
-          PointerEvent e;
-          e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
-            event.button.x, event.button.y));
-          
-          //LOG(TRACE) << "event.button.x = " << event.button.x << "     " <<
-          //  "event.button.y = " << event.button.y;
-          LOG(TRACE) << "activeTracker_->PointerMove(e); " <<
-            e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY();
-          
-          activeTracker_->PointerMove(e);
-          if (!activeTracker_->IsAlive())
-            activeTracker_.reset();
-        }
-      }
-    }
-    else if (event.type == SDL_MOUSEBUTTONUP)
-    {
-      if (activeTracker_)
-      {
-        PointerEvent e;
-        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
-        activeTracker_->PointerUp(e);
-        if (!activeTracker_->IsAlive())
-          activeTracker_.reset();
-      }
-    }
-    else if (event.type == SDL_MOUSEBUTTONDOWN)
-    {
-      PointerEvent e;
-      e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
-        event.button.x, event.button.y));
-      if (activeTracker_)
-      {
-        activeTracker_->PointerDown(e);
-        if (!activeTracker_->IsAlive())
-          activeTracker_.reset();
-      }
-      else
-      {
-        // we ATTEMPT to create a tracker if need be
-        activeTracker_ = CreateSuitableTracker(event, e);
-      }
-    }
-    else if (event.type == SDL_KEYDOWN &&
-      event.key.repeat == 0 /* Ignore key bounce */)
-    {
-      switch (event.key.keysym.sym)
-      {
-      case SDLK_ESCAPE:
-        if (activeTracker_)
-        {
-          activeTracker_->Cancel();
-          if (!activeTracker_->IsAlive())
-            activeTracker_.reset();
-        }
-        break;
-
-      case SDLK_t:
-        if (!activeTracker_)
-          SelectNextTool();
-        else
-        {
-          LOG(WARNING) << "You cannot change the active tool when an interaction"
-            " is taking place";
-        }
-        break;
-      case SDLK_s:
-        controller_->FitContent(viewport_.GetCompositor().GetCanvasWidth(),
-          viewport_.GetCompositor().GetCanvasHeight());
-        break;
-
-      case SDLK_z:
-        LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
-        if (event.key.keysym.mod & KMOD_CTRL)
-        {
-          if (controller_->CanUndo())
-          {
-            LOG(TRACE) << "Undoing...";
-            controller_->Undo();
-          }
-          else
-          {
-            LOG(WARNING) << "Nothing to undo!!!";
-          }
-        }
-        break;
-
-      case SDLK_y:
-        LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
-        if (event.key.keysym.mod & KMOD_CTRL)
-        {
-          if (controller_->CanRedo())
-          {
-            LOG(TRACE) << "Redoing...";
-            controller_->Redo();
-          }
-          else
-          {
-            LOG(WARNING) << "Nothing to redo!!!";
-          }
-        }
-        break;
-
-      case SDLK_c:
-        TakeScreenshot(
-          "screenshot.png",
-          viewport_.GetCompositor().GetCanvasWidth(),
-          viewport_.GetCompositor().GetCanvasHeight());
-        break;
-
-      default:
-        break;
-      }
-    }
-  }
-
-
-  void FusionMprSdlApp::OnSceneTransformChanged(
-    const ViewportController::SceneTransformChanged& message)
-  {
-    DisplayInfoText();
-  }
-
-  boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::CreateSuitableTracker(
-    const SDL_Event & event,
-    const PointerEvent & e)
-  {
-    using namespace Orthanc;
-
-    switch (event.button.button)
-    {
-    case SDL_BUTTON_MIDDLE:
-      return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker
-        (controller_, e));
-
-    case SDL_BUTTON_RIGHT:
-      return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker
-        (controller_, e, viewport_.GetCompositor().GetCanvasHeight()));
-
-    case SDL_BUTTON_LEFT:
-    {
-      //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:";
-      // TODO: we need to iterate on the set of measuring tool and perform
-      // a hit test to check if a tracker needs to be created for edition.
-      // Otherwise, depending upon the active tool, we might want to create
-      // a "measuring tool creation" tracker
-
-      // TODO: if there are conflicts, we should prefer a tracker that 
-      // pertains to the type of measuring tool currently selected (TBD?)
-      boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e);
-
-      if (hitTestTracker != NULL)
-      {
-        //LOG(TRACE) << "hitTestTracker != NULL";
-        return hitTestTracker;
-      }
-      else
-      {
-        switch (currentTool_)
-        {
-        case FusionMprGuiTool_Rotate:
-          //LOG(TRACE) << "Creating RotateSceneTracker";
-          return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker(
-            controller_, e));
-        case FusionMprGuiTool_Pan:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker(
-            controller_, e));
-        case FusionMprGuiTool_Zoom:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker(
-            controller_, e, viewport_.GetCompositor().GetCanvasHeight()));
-        //case GuiTool_AngleMeasure:
-        //  return new AngleMeasureTracker(GetScene(), e);
-        //case GuiTool_CircleMeasure:
-        //  return new CircleMeasureTracker(GetScene(), e);
-        //case GuiTool_EllipseMeasure:
-        //  return new EllipseMeasureTracker(GetScene(), e);
-        case FusionMprGuiTool_LineMeasure:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker(
-            IObserver::GetBroker(), controller_, e));
-        case FusionMprGuiTool_AngleMeasure:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker(
-            IObserver::GetBroker(), controller_, e));
-        case FusionMprGuiTool_CircleMeasure:
-          LOG(ERROR) << "Not implemented yet!";
-          return boost::shared_ptr<IFlexiblePointerTracker>();
-        case FusionMprGuiTool_EllipseMeasure:
-          LOG(ERROR) << "Not implemented yet!";
-          return boost::shared_ptr<IFlexiblePointerTracker>();
-        default:
-          throw OrthancException(ErrorCode_InternalError, "Wrong tool!");
-        }
-      }
-    }
-    default:
-      return boost::shared_ptr<IFlexiblePointerTracker>();
-    }
-  }
-
-
-  FusionMprSdlApp::FusionMprSdlApp(MessageBroker& broker)
-    : IObserver(broker)
-    , broker_(broker)
-    , oracleObservable_(broker)
-    , oracle_(*this)
-    , currentTool_(FusionMprGuiTool_Rotate)
-    , undoStack_(new UndoStack)
-    , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled
-  {
-    //oracleObservable.RegisterObserverCallback
-    //(new Callable
-    //  <FusionMprSdlApp, SleepOracleCommand::TimeoutMessage>(*this, &FusionMprSdlApp::Handle));
-
-    //oracleObservable.RegisterObserverCallback
-    //(new Callable
-    //  <Toto, GetOrthancImageCommand::SuccessMessage>(*this, &FusionMprSdlApp::Handle));
-
-    //oracleObservable.RegisterObserverCallback
-    //(new Callable
-    //  <FusionMprSdlApp, GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &ToFusionMprSdlAppto::Handle));
-
-    oracleObservable_.RegisterObserverCallback
-    (new Callable
-      <FusionMprSdlApp, OracleCommandExceptionMessage>(*this, &FusionMprSdlApp::Handle));
-    
-    controller_ = boost::shared_ptr<ViewportController>(
-      new ViewportController(undoStack_, broker_, viewport_));
-
-    controller_->RegisterObserverCallback(
-      new Callable<FusionMprSdlApp, ViewportController::SceneTransformChanged>
-      (*this, &FusionMprSdlApp::OnSceneTransformChanged));
-
-    TEXTURE_2x2_1_ZINDEX = 1;
-    TEXTURE_1x1_ZINDEX = 2;
-    TEXTURE_2x2_2_ZINDEX = 3;
-    LINESET_1_ZINDEX = 4;
-    LINESET_2_ZINDEX = 5;
-    FLOATING_INFOTEXT_LAYER_ZINDEX = 6;
-    FIXED_INFOTEXT_LAYER_ZINDEX = 7;
-  }
-
-  void FusionMprSdlApp::PrepareScene()
-  {
-    // Texture of 2x2 size
-    {
-      Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false);
-
-      uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0));
-      p[0] = 255;
-      p[1] = 0;
-      p[2] = 0;
-
-      p[3] = 0;
-      p[4] = 255;
-      p[5] = 0;
-
-      p = reinterpret_cast<uint8_t*>(i.GetRow(1));
-      p[0] = 0;
-      p[1] = 0;
-      p[2] = 255;
-
-      p[3] = 255;
-      p[4] = 0;
-      p[5] = 0;
-
-      GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i));
-    }
-  }
-
-  void FusionMprSdlApp::DisableTracker()
-  {
-    if (activeTracker_)
-    {
-      activeTracker_->Cancel();
-      activeTracker_.reset();
-    }
-  }
-
-  void FusionMprSdlApp::TakeScreenshot(const std::string& target,
-    unsigned int canvasWidth,
-    unsigned int canvasHeight)
-  {
-    CairoCompositor compositor(GetScene(), canvasWidth, canvasHeight);
-    compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1);
-    compositor.Refresh();
-
-    Orthanc::ImageAccessor canvas;
-    compositor.GetCanvas().GetReadOnlyAccessor(canvas);
-
-    Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
-    Orthanc::ImageProcessing::Convert(png, canvas);
-
-    Orthanc::PngWriter writer;
-    writer.WriteToFile(target, png);
-  }
-
-
-  boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::TrackerHitTest(const PointerEvent & e)
-  {
-    // std::vector<boost::shared_ptr<MeasureTool>> measureTools_;
-    return boost::shared_ptr<IFlexiblePointerTracker>();
-  }
-
-  static void GLAPIENTRY
-    OpenGLMessageCallback(GLenum source,
-      GLenum type,
-      GLuint id,
-      GLenum severity,
-      GLsizei length,
-      const GLchar* message,
-      const void* userParam)
-  {
-    if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
-    {
-      fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
-        (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
-        type, severity, message);
-    }
-  }
-
-  static bool g_stopApplication = false;
-  
-
-  void FusionMprSdlApp::Handle(const DicomVolumeImage::GeometryReadyMessage& message)
-  {
-    printf("Geometry ready\n");
-
-    //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry();
-    //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry();
-    plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry();
-    plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f));
-
-    //Refresh();
-  }
-
-
-  void FusionMprSdlApp::Handle(const OracleCommandExceptionMessage& message)
-  {
-    printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType());
-
-    switch (message.GetCommand().GetType())
-    {
-    case IOracleCommand::Type_GetOrthancWebViewerJpeg:
-      printf("URI: [%s]\n", dynamic_cast<const GetOrthancWebViewerJpegCommand&>
-        (message.GetCommand()).GetUri().c_str());
-      break;
-
-    default:
-      break;
-    }
-  }
-
-  void FusionMprSdlApp::SetVolume1(int depth,
-    const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
-    OrthancStone::ILayerStyleConfigurator* style)
-  {
-    source1_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume));
-
-    if (style != NULL)
-    {
-      source1_->SetConfigurator(style);
-    }
-  }
-
-  void FusionMprSdlApp::SetVolume2(int depth,
-    const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
-    OrthancStone::ILayerStyleConfigurator* style)
-  {
-    source2_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume));
-
-    if (style != NULL)
-    {
-      source2_->SetConfigurator(style);
-    }
-  }
-
-  void FusionMprSdlApp::SetStructureSet(int depth,
-    const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume)
-  {
-    source3_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume));
-  }
-  
-  void FusionMprSdlApp::Run()
-  {
-    // False means we do NOT let Windows treat this as a legacy application
-    // that needs to be scaled
-    controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight());
-
-    glEnable(GL_DEBUG_OUTPUT);
-    glDebugMessageCallback(OpenGLMessageCallback, 0);
-
-    viewport_.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
-      FONT_SIZE_0, Orthanc::Encoding_Latin1);
-    viewport_.GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
-      FONT_SIZE_1, Orthanc::Encoding_Latin1);
-
-
-    //////// from loader
-    {
-      Orthanc::WebServiceParameters p;
-      //p.SetUrl("http://localhost:8043/");
-      p.SetCredentials("orthanc", "orthanc");
-      oracle_.SetOrthancParameters(p);
-    }
-
-    //////// from Run
-
-    boost::shared_ptr<DicomVolumeImage>  ct(new DicomVolumeImage);
-    boost::shared_ptr<DicomVolumeImage>  dose(new DicomVolumeImage);
-
-
-    boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> ctLoader;
-    boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader;
-    boost::shared_ptr<DicomStructureSetLoader>  rtstructLoader;
-
-    {
-      ctLoader.reset(new OrthancSeriesVolumeProgressiveLoader(ct, oracle_, oracleObservable_));
-      doseLoader.reset(new OrthancMultiframeVolumeLoader(dose, oracle_, oracleObservable_));
-      rtstructLoader.reset(new DicomStructureSetLoader(oracle_, oracleObservable_));
-    }
-
-    //toto->SetReferenceLoader(*ctLoader);
-    //doseLoader->RegisterObserverCallback
-    //(new Callable
-    //  <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle));
-    ctLoader->RegisterObserverCallback
-    (new Callable
-      <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle));
-
-    this->SetVolume1(0, ctLoader, new GrayscaleStyleConfigurator);
-
-    {
-      std::unique_ptr<LookupTableStyleConfigurator> config(new LookupTableStyleConfigurator);
-      config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT);
-
-      boost::shared_ptr<DicomVolumeImageMPRSlicer> tmp(new DicomVolumeImageMPRSlicer(dose));
-      this->SetVolume2(1, tmp, config.release());
-    }
-
-    this->SetStructureSet(2, rtstructLoader);
-
-#if 1
-    /*
-    BGO data
-    http://localhost:8042/twiga-orthanc-viewer-demo/twiga-orthanc-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa
-    &
-    dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb
-    &
-    struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9
-    */
-    ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
-    doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb");  // RT-DOSE
-    rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
-#else
-    //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618");  // CT
-    //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6");  // RT-DOSE
-    //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6");  // RT-STRUCT
-
-    // 2017-05-16
-    ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
-    doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad");  // RT-DOSE
-    rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
-#endif
-
-    oracle_.Start();
-
-//// END from loader
-
-    while (!g_stopApplication)
-    {
-      viewport_.GetCompositor().Refresh();
-
-//////// from loader
-      if (source1_.get() != NULL)
-      {
-        source1_->Update(plane_);
-      }
-
-      if (source2_.get() != NULL)
-      {
-        source2_->Update(plane_);
-      }
-
-      if (source3_.get() != NULL)
-      {
-        source3_->Update(plane_);
-      }
-//// END from loader
-
-      SDL_Event event;
-      while (!g_stopApplication && SDL_PollEvent(&event))
-      {
-        if (event.type == SDL_QUIT)
-        {
-          g_stopApplication = true;
-          break;
-        }
-        else if (event.type == SDL_WINDOWEVENT &&
-          event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
-        {
-          DisableTracker(); // was: tracker.reset(NULL);
-        }
-        else if (event.type == SDL_KEYDOWN &&
-          event.key.repeat == 0 /* Ignore key bounce */)
-        {
-          switch (event.key.keysym.sym)
-          {
-          case SDLK_f:
-            viewport_.GetWindow().ToggleMaximize();
-            break;
-
-          case SDLK_s:
-            controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight());
-            break;
-
-          case SDLK_q:
-            g_stopApplication = true;
-            break;
-          default:
-            break;
-          }
-        }
-        HandleApplicationEvent(event);
-      }
-      SDL_Delay(1);
-    }
-
-    //// from loader
-
-    //Orthanc::SystemToolbox::ServerBarrier();
-
-    /**
-     * WARNING => The oracle must be stopped BEFORE the objects using
-     * it are destroyed!!! This forces to wait for the completion of
-     * the running callback methods. Otherwise, the callbacks methods
-     * might still be running while their parent object is destroyed,
-     * resulting in crashes. This is very visible if adding a sleep(),
-     * as in (*).
-     **/
-
-    oracle_.Stop();
-    //// END from loader
-  }
-
-  void FusionMprSdlApp::SetInfoDisplayMessage(
-    std::string key, std::string value)
-  {
-    if (value == "")
-      infoTextMap_.erase(key);
-    else
-      infoTextMap_[key] = value;
-    DisplayInfoText();
-  }
-
-}
-
-
-boost::weak_ptr<OrthancStone::FusionMprSdlApp> g_app;
-
-void FusionMprSdl_SetInfoDisplayMessage(std::string key, std::string value)
-{
-  boost::shared_ptr<OrthancStone::FusionMprSdlApp> app = g_app.lock();
-  if (app)
-  {
-    app->SetInfoDisplayMessage(key, value);
-  }
-}
-
-/**
- * IMPORTANT: The full arguments to "main()" are needed for SDL on
- * Windows. Otherwise, one gets the linking error "undefined reference
- * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
- **/
-int main(int argc, char* argv[])
-{
-  using namespace OrthancStone;
-
-  StoneInitialize();
-  Orthanc::Logging::EnableInfoLevel(true);
-//  Orthanc::Logging::EnableTraceLevel(true);
-
-  try
-  {
-    OrthancStone::MessageBroker broker;
-    boost::shared_ptr<FusionMprSdlApp> app(new FusionMprSdlApp(broker));
-    g_app = app;
-    app->PrepareScene();
-    app->Run();
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "EXCEPTION: " << e.What();
-  }
-
-  StoneFinalize();
-
-  return 0;
-}
-
-
--- a/Samples/Deprecated/Sdl/FusionMprSdl.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,206 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "../../Framework/Viewport/SdlViewport.h"
-
-#include "../../Framework/Messages/IObserver.h"
-#include "../../Framework/Messages/IMessageEmitter.h"
-#include "../../Framework/Oracle/OracleCommandExceptionMessage.h"
-#include "../../Framework/Scene2DViewport/ViewportController.h"
-#include "../../Framework/Volumes/DicomVolumeImage.h"
-#include "../../Framework/Oracle/ThreadedOracle.h"
-
-#include <boost/enable_shared_from_this.hpp>
-#include <boost/thread.hpp>
-#include <boost/noncopyable.hpp>
-
-#include <SDL.h>
-
-namespace OrthancStone
-{
-  class OpenGLCompositor;
-  class IVolumeSlicer;
-  class ILayerStyleConfigurator;
-  class DicomStructureSetLoader;
-  class IOracle;
-  class ThreadedOracle;
-  class VolumeSceneLayerSource;
-  class NativeFusionMprApplicationContext;
-  class SdlOpenGLViewport;
-   
-  enum FusionMprGuiTool
-  {
-    FusionMprGuiTool_Rotate = 0,
-    FusionMprGuiTool_Pan,
-    FusionMprGuiTool_Zoom,
-    FusionMprGuiTool_LineMeasure,
-    FusionMprGuiTool_CircleMeasure,
-    FusionMprGuiTool_AngleMeasure,
-    FusionMprGuiTool_EllipseMeasure,
-    FusionMprGuiTool_LAST
-  };
-
-  const char* MeasureToolToString(size_t i);
-
-  static const unsigned int FONT_SIZE_0 = 32;
-  static const unsigned int FONT_SIZE_1 = 24;
-
-  class Scene2D;
-  class UndoStack;
-
-  /**
-  This application subclasses IMessageEmitter to use a mutex before forwarding Oracle messages (that
-  can be sent from multiple threads)
-  */
-  class FusionMprSdlApp : public IObserver
-    , public boost::enable_shared_from_this<FusionMprSdlApp>
-    , public IMessageEmitter
-  {
-  public:
-    // 12 because.
-    FusionMprSdlApp(MessageBroker& broker);
-
-    void PrepareScene();
-    void Run();
-    void SetInfoDisplayMessage(std::string key, std::string value);
-    void DisableTracker();
-
-    Scene2D&       GetScene();
-    const Scene2D& GetScene() const;
-
-    void HandleApplicationEvent(const SDL_Event& event);
-
-    /**
-    This method is called when the scene transform changes. It allows to
-    recompute the visual elements whose content depend upon the scene transform
-    */
-    void OnSceneTransformChanged(
-      const ViewportController::SceneTransformChanged& message);
-
-
-    virtual void EmitMessage(const IObserver& observer,
-      const IMessage& message) ORTHANC_OVERRIDE
-    {
-      try
-      {
-        boost::unique_lock<boost::shared_mutex>  lock(mutex_);
-        oracleObservable_.EmitMessage(observer, message);
-      }
-      catch (Orthanc::OrthancException& e)
-      {
-        LOG(ERROR) << "Exception while emitting a message: " << e.What();
-        throw;
-      }
-    }
-    
-  private:
-#if 1
-    // if threaded (not wasm)
-    MessageBroker& broker_;
-    IObservable oracleObservable_;
-    ThreadedOracle oracle_;
-    boost::shared_mutex mutex_; // to serialize messages from the ThreadedOracle
-#endif
-
-    void SelectNextTool();
-
-    /**
-    This returns a random point in the canvas part of the scene, but in
-    scene coordinates
-    */
-    ScenePoint2D GetRandomPointInScene() const;
-
-    boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e);
-
-    boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker(
-      const SDL_Event& event,
-      const PointerEvent& e);
-
-    void TakeScreenshot(
-      const std::string& target,
-      unsigned int canvasWidth,
-      unsigned int canvasHeight);
-
-    /**
-      This adds the command at the top of the undo stack
-    */
-    void Commit(boost::shared_ptr<TrackerCommand> cmd);
-    void Undo();
-    void Redo();
-
-
-    // TODO private
-    void Handle(const DicomVolumeImage::GeometryReadyMessage& message);
-    void Handle(const OracleCommandExceptionMessage& message);
-
-    void SetVolume1(
-      int depth,
-      const boost::shared_ptr<IVolumeSlicer>& volume,
-      ILayerStyleConfigurator* style);
-    
-    void SetVolume2(
-      int depth,
-      const boost::shared_ptr<IVolumeSlicer>& volume,
-      ILayerStyleConfigurator* style);
-
-    void SetStructureSet(
-      int depth, 
-      const boost::shared_ptr<DicomStructureSetLoader>& volume);
-
-
-
-  private:
-    void DisplayFloatingCtrlInfoText(const PointerEvent& e);
-    void DisplayInfoText();
-    void HideInfoText();
-
-  private:
-    CoordinateSystem3D  plane_;
-
-    boost::shared_ptr<VolumeSceneLayerSource>  source1_, source2_, source3_;
-
-    /**
-    WARNING: the measuring tools do store a reference to the scene, and it
-    paramount that the scene gets destroyed AFTER the measurement tools.
-    */
-    boost::shared_ptr<ViewportController> controller_;
-
-    std::map<std::string, std::string> infoTextMap_;
-    boost::shared_ptr<IFlexiblePointerTracker> activeTracker_;
-
-    //static const int LAYER_POSITION = 150;
-
-    int TEXTURE_2x2_1_ZINDEX;
-    int TEXTURE_1x1_ZINDEX;
-    int TEXTURE_2x2_2_ZINDEX;
-    int LINESET_1_ZINDEX;
-    int LINESET_2_ZINDEX;
-    int FLOATING_INFOTEXT_LAYER_ZINDEX;
-    int FIXED_INFOTEXT_LAYER_ZINDEX;
-
-    FusionMprGuiTool currentTool_;
-    boost::shared_ptr<UndoStack> undoStack_;
-    SdlOpenGLViewport viewport_;
-  };
-
-}
-
-
- 
\ No newline at end of file
--- a/Samples/Deprecated/Sdl/Loader.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,518 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "../../Framework/Loaders/DicomStructureSetLoader.h"
-#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h"
-#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
-#include "../../Framework/Oracle/SleepOracleCommand.h"
-#include "../../Framework/Oracle/ThreadedOracle.h"
-#include "../../Framework/Scene2D/CairoCompositor.h"
-#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h"
-#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h"
-#include "../../Framework/StoneInitialization.h"
-#include "../../Framework/Volumes/VolumeSceneLayerSource.h"
-#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h"
-#include "../../Framework/Volumes/DicomVolumeImageReslicer.h"
-
-// From Orthanc framework
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Images/PngWriter.h>
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-#include <Core/SystemToolbox.h>
-
-
-namespace OrthancStone
-{
-  class NativeApplicationContext : public IMessageEmitter
-  {
-  private:
-    boost::shared_mutex  mutex_;
-    MessageBroker        broker_;
-    IObservable          oracleObservable_;
-
-  public:
-    NativeApplicationContext() :
-      oracleObservable_(broker_)
-    {
-    }
-
-
-    virtual void EmitMessage(const IObserver& observer,
-                             const IMessage& message) ORTHANC_OVERRIDE
-    {
-      try
-      {
-        boost::unique_lock<boost::shared_mutex>  lock(mutex_);
-        oracleObservable_.EmitMessage(observer, message);
-      }
-      catch (Orthanc::OrthancException& e)
-      {
-        LOG(ERROR) << "Exception while emitting a message: " << e.What();
-      }
-    }
-
-
-    class ReaderLock : public boost::noncopyable
-    {
-    private:
-      NativeApplicationContext&                that_;
-      boost::shared_lock<boost::shared_mutex>  lock_;
-
-    public:
-      ReaderLock(NativeApplicationContext& that) : 
-        that_(that),
-        lock_(that.mutex_)
-      {
-      }
-    };
-
-
-    class WriterLock : public boost::noncopyable
-    {
-    private:
-      NativeApplicationContext&                that_;
-      boost::unique_lock<boost::shared_mutex>  lock_;
-
-    public:
-      WriterLock(NativeApplicationContext& that) : 
-        that_(that),
-        lock_(that.mutex_)
-      {
-      }
-
-      MessageBroker& GetBroker() 
-      {
-        return that_.broker_;
-      }
-
-      IObservable& GetOracleObservable()
-      {
-        return that_.oracleObservable_;
-      }
-    };
-  };
-}
-
-
-
-class Toto : public OrthancStone::IObserver
-{
-private:
-  OrthancStone::CoordinateSystem3D  plane_;
-  OrthancStone::IOracle&            oracle_;
-  OrthancStone::Scene2D             scene_;
-  std::unique_ptr<OrthancStone::VolumeSceneLayerSource>  source1_, source2_, source3_;
-
-
-  void Refresh()
-  {
-    if (source1_.get() != NULL)
-    {
-      source1_->Update(plane_);
-    }
-      
-    if (source2_.get() != NULL)
-    {
-      source2_->Update(plane_);
-    }
-
-    if (source3_.get() != NULL)
-    {
-      source3_->Update(plane_);
-    }
-
-    scene_.FitContent(1024, 768);
-      
-    {
-      OrthancStone::CairoCompositor compositor(scene_, 1024, 768);
-      compositor.Refresh();
-        
-      Orthanc::ImageAccessor accessor;
-      compositor.GetCanvas().GetReadOnlyAccessor(accessor);
-
-      Orthanc::Image tmp(Orthanc::PixelFormat_RGB24, accessor.GetWidth(), accessor.GetHeight(), false);
-      Orthanc::ImageProcessing::Convert(tmp, accessor);
-        
-      static unsigned int count = 0;
-      char buf[64];
-      sprintf(buf, "scene-%06d.png", count++);
-        
-      Orthanc::PngWriter writer;
-      writer.WriteToFile(buf, tmp);
-    }
-  }
-
-  
-  void Handle(const OrthancStone::DicomVolumeImage::GeometryReadyMessage& message)
-  {
-    printf("Geometry ready\n");
-    
-    plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry();
-    //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry();
-    //plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry();
-    plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f));
-
-    Refresh();
-  }
-  
-  
-  void Handle(const OrthancStone::SleepOracleCommand::TimeoutMessage& message)
-  {
-    if (message.GetOrigin().HasPayload())
-    {
-      printf("TIMEOUT! %d\n", dynamic_cast<const Orthanc::SingleValueObject<unsigned int>& >(message.GetOrigin().GetPayload()).GetValue());
-    }
-    else
-    {
-      printf("TIMEOUT\n");
-
-      Refresh();
-
-      /**
-       * The sleep() leads to a crash if the oracle is still running,
-       * while this object is destroyed. Always stop the oracle before
-       * destroying active objects.  (*)
-       **/
-      // boost::this_thread::sleep(boost::posix_time::seconds(2));
-
-      oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(message.GetOrigin().GetDelay()));
-    }
-  }
-
-  void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message)
-  {
-    Json::Value v;
-    message.ParseJsonBody(v);
-
-    printf("ICI [%s]\n", v.toStyledString().c_str());
-  }
-
-  void Handle(const OrthancStone::GetOrthancImageCommand::SuccessMessage& message)
-  {
-    printf("IMAGE %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight());
-  }
-
-  void Handle(const OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage& message)
-  {
-    printf("WebViewer %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight());
-  }
-
-  void Handle(const OrthancStone::OracleCommandExceptionMessage& message)
-  {
-    printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType());
-
-    switch (message.GetCommand().GetType())
-    {
-      case OrthancStone::IOracleCommand::Type_GetOrthancWebViewerJpeg:
-        printf("URI: [%s]\n", dynamic_cast<const OrthancStone::GetOrthancWebViewerJpegCommand&>
-               (message.GetCommand()).GetUri().c_str());
-        break;
-      
-      default:
-        break;
-    }
-  }
-
-public:
-  Toto(OrthancStone::IOracle& oracle,
-       OrthancStone::IObservable& oracleObservable) :
-    IObserver(oracleObservable.GetBroker()),
-    oracle_(oracle)
-  {
-    oracleObservable.RegisterObserverCallback
-      (new OrthancStone::Callable
-       <Toto, OrthancStone::SleepOracleCommand::TimeoutMessage>(*this, &Toto::Handle));
-
-    oracleObservable.RegisterObserverCallback
-      (new OrthancStone::Callable
-       <Toto, OrthancStone::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle));
-
-    oracleObservable.RegisterObserverCallback
-      (new OrthancStone::Callable
-       <Toto, OrthancStone::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle));
-
-    oracleObservable.RegisterObserverCallback
-      (new OrthancStone::Callable
-       <Toto, OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle));
-
-    oracleObservable.RegisterObserverCallback
-      (new OrthancStone::Callable
-       <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle));
-  }
-
-  void SetReferenceLoader(OrthancStone::IObservable& loader)
-  {
-    loader.RegisterObserverCallback
-      (new OrthancStone::Callable
-       <Toto, OrthancStone::DicomVolumeImage::GeometryReadyMessage>(*this, &Toto::Handle));
-  }
-
-  void SetVolume1(int depth,
-                  const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
-                  OrthancStone::ILayerStyleConfigurator* style)
-  {
-    source1_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
-
-    if (style != NULL)
-    {
-      source1_->SetConfigurator(style);
-    }
-  }
-
-  void SetVolume2(int depth,
-                  const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
-                  OrthancStone::ILayerStyleConfigurator* style)
-  {
-    source2_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
-
-    if (style != NULL)
-    {
-      source2_->SetConfigurator(style);
-    }
-  }
-
-  void SetStructureSet(int depth,
-                       const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume)
-  {
-    source3_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
-  }
-                       
-};
-
-
-void Run(OrthancStone::NativeApplicationContext& context,
-         OrthancStone::ThreadedOracle& oracle)
-{
-  // the oracle has been supplied with the context (as an IEmitter) upon
-  // creation
-  boost::shared_ptr<OrthancStone::DicomVolumeImage>  ct(new OrthancStone::DicomVolumeImage);
-  boost::shared_ptr<OrthancStone::DicomVolumeImage>  dose(new OrthancStone::DicomVolumeImage);
-
-  
-  boost::shared_ptr<Toto> toto;
-  boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader;
-  boost::shared_ptr<OrthancStone::OrthancMultiframeVolumeLoader> doseLoader;
-  boost::shared_ptr<OrthancStone::DicomStructureSetLoader>  rtstructLoader;
-
-  {
-    OrthancStone::NativeApplicationContext::WriterLock lock(context);
-    toto.reset(new Toto(oracle, lock.GetOracleObservable()));
-
-    // the oracle is used to schedule commands
-    // the oracleObservable is used by the loaders to:
-    // - request the broker (lifetime mgmt)
-    // - register the loader callbacks (called indirectly by the oracle)
-    ctLoader.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct, oracle, lock.GetOracleObservable()));
-    doseLoader.reset(new OrthancStone::OrthancMultiframeVolumeLoader(dose, oracle, lock.GetOracleObservable()));
-    rtstructLoader.reset(new OrthancStone::DicomStructureSetLoader(oracle, lock.GetOracleObservable()));
-  }
-
-
-  //toto->SetReferenceLoader(*ctLoader);
-  toto->SetReferenceLoader(*doseLoader);
-
-
-#if 1
-  toto->SetVolume1(0, ctLoader, new OrthancStone::GrayscaleStyleConfigurator);
-#else
-  {
-    boost::shared_ptr<OrthancStone::IVolumeSlicer> reslicer(new OrthancStone::DicomVolumeImageReslicer(ct));
-    toto->SetVolume1(0, reslicer, new OrthancStone::GrayscaleStyleConfigurator);
-  }
-#endif  
-  
-
-  {
-    std::unique_ptr<OrthancStone::LookupTableStyleConfigurator> config(new OrthancStone::LookupTableStyleConfigurator);
-    config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT);
-
-    boost::shared_ptr<OrthancStone::DicomVolumeImageMPRSlicer> tmp(new OrthancStone::DicomVolumeImageMPRSlicer(dose));
-    toto->SetVolume2(1, tmp, config.release());
-  }
-
-  toto->SetStructureSet(2, rtstructLoader);
-  
-  oracle.Schedule(*toto, new OrthancStone::SleepOracleCommand(100));
-
-  if (0)
-  {
-    Json::Value v = Json::objectValue;
-    v["Level"] = "Series";
-    v["Query"] = Json::objectValue;
-
-    std::unique_ptr<OrthancStone::OrthancRestApiCommand>  command(new OrthancStone::OrthancRestApiCommand);
-    command->SetMethod(Orthanc::HttpMethod_Post);
-    command->SetUri("/tools/find");
-    command->SetBody(v);
-
-    oracle.Schedule(*toto, command.release());
-  }
-  
-  if(0)
-  {
-    if (0)
-    {
-      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
-      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg)));
-      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview");
-      oracle.Schedule(*toto, command.release());
-    }
-    
-    if (0)
-    {
-      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
-      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png)));
-      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview");
-      oracle.Schedule(*toto, command.release());
-    }
-    
-    if (0)
-    {
-      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
-      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png)));
-      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
-      oracle.Schedule(*toto, command.release());
-    }
-    
-    if (0)
-    {
-      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
-      command->SetHttpHeader("Accept-Encoding", "gzip");
-      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam)));
-      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
-      oracle.Schedule(*toto, command.release());
-    }
-    
-    if (0)
-    {
-      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
-      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam)));
-      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
-      oracle.Schedule(*toto, command.release());
-    }
-
-    if (0)
-    {
-      std::unique_ptr<OrthancStone::GetOrthancWebViewerJpegCommand>  command(new OrthancStone::GetOrthancWebViewerJpegCommand);
-      command->SetHttpHeader("Accept-Encoding", "gzip");
-      command->SetInstance("e6c7c20b-c9f65d7e-0d76f2e2-830186f2-3e3c600e");
-      command->SetQuality(90);
-      oracle.Schedule(*toto, command.release());
-    }
-
-
-    if (0)
-    {
-      for (unsigned int i = 0; i < 10; i++)
-      {
-        std::unique_ptr<OrthancStone::SleepOracleCommand> command(new OrthancStone::SleepOracleCommand(i * 1000));
-        command->SetPayload(new Orthanc::SingleValueObject<unsigned int>(42 * i));
-        oracle.Schedule(*toto, command.release());
-      }
-    }
-  }
-  
-  // 2017-11-17-Anonymized
-#if 0
-  // BGO data
-  ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
-  doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb");  // RT-DOSE
-  //rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
-#else
-  //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618");  // CT
-  //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6");  // RT-DOSE
-  //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6");  // RT-STRUCT
-
-  // 2017-05-16
-  ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
-  doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad");  // RT-DOSE
-  rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
-#endif
-  // 2015-01-28-Multiframe
-  //doseLoader->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279");  // Multiframe CT
-  
-  // Delphine
-  //ctLoader->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e");  // CT
-  //ctLoader->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5");  // Lung 1/10mm
-
-
-  {
-    LOG(WARNING) << "...Waiting for Ctrl-C...";
-
-    oracle.Start();
-
-    Orthanc::SystemToolbox::ServerBarrier();
-
-    /**
-     * WARNING => The oracle must be stopped BEFORE the objects using
-     * it are destroyed!!! This forces to wait for the completion of
-     * the running callback methods. Otherwise, the callbacks methods
-     * might still be running while their parent object is destroyed,
-     * resulting in crashes. This is very visible if adding a sleep(),
-     * as in (*).
-     **/
-
-    oracle.Stop();
-  }
-}
-
-
-
-/**
- * IMPORTANT: The full arguments to "main()" are needed for SDL on
- * Windows. Otherwise, one gets the linking error "undefined reference
- * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
- **/
-int main(int argc, char* argv[])
-{
-  OrthancStone::StoneInitialize();
-  //Orthanc::Logging::EnableInfoLevel(true);
-
-  try
-  {
-    OrthancStone::NativeApplicationContext context;
-
-    OrthancStone::ThreadedOracle oracle(context);
-    //oracle.SetThreadsCount(1);
-
-    {
-      Orthanc::WebServiceParameters p;
-      //p.SetUrl("http://localhost:8043/");
-      p.SetCredentials("orthanc", "orthanc");
-      oracle.SetOrthancParameters(p);
-    }
-
-    //oracle.Start();
-
-    Run(context, oracle);
-    
-    //oracle.Stop();
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "EXCEPTION: " << e.What();
-  }
-
-  OrthancStone::StoneFinalize();
-
-  return 0;
-}
--- a/Samples/Deprecated/Sdl/RadiographyEditor.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,267 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "../Shared/RadiographyEditorApp.h"
-
-// From Stone
-#include "../../Framework/Oracle/SleepOracleCommand.h"
-#include "../../Framework/Oracle/ThreadedOracle.h"
-#include "../../Applications/Sdl/SdlOpenGLWindow.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/Scene2D/CairoCompositor.h"
-#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/StoneInitialization.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-
-#include <SDL.h>
-#include <stdio.h>
-
-using namespace OrthancStone;
-
-namespace OrthancStone
-{
-  class NativeApplicationContext : public IMessageEmitter
-  {
-  private:
-    boost::shared_mutex  mutex_;
-    MessageBroker        broker_;
-    IObservable          oracleObservable_;
-
-  public:
-    NativeApplicationContext() :
-      oracleObservable_(broker_)
-    {
-    }
-
-
-    virtual void EmitMessage(const IObserver& observer,
-                             const IMessage& message) ORTHANC_OVERRIDE
-    {
-      try
-      {
-        boost::unique_lock<boost::shared_mutex>  lock(mutex_);
-        oracleObservable_.EmitMessage(observer, message);
-      }
-      catch (Orthanc::OrthancException& e)
-      {
-        LOG(ERROR) << "Exception while emitting a message: " << e.What();
-      }
-    }
-
-
-    class ReaderLock : public boost::noncopyable
-    {
-    private:
-      NativeApplicationContext&                that_;
-      boost::shared_lock<boost::shared_mutex>  lock_;
-
-    public:
-      ReaderLock(NativeApplicationContext& that) :
-        that_(that),
-        lock_(that.mutex_)
-      {
-      }
-    };
-
-
-    class WriterLock : public boost::noncopyable
-    {
-    private:
-      NativeApplicationContext&                that_;
-      boost::unique_lock<boost::shared_mutex>  lock_;
-
-    public:
-      WriterLock(NativeApplicationContext& that) :
-        that_(that),
-        lock_(that.mutex_)
-      {
-      }
-
-      MessageBroker& GetBroker()
-      {
-        return that_.broker_;
-      }
-
-      IObservable& GetOracleObservable()
-      {
-        return that_.oracleObservable_;
-      }
-    };
-  };
-}
-
-class OpenGlSdlCompositorFactory : public ICompositorFactory
-{
-  OpenGL::IOpenGLContext& openGlContext_;
-
-public:
-  OpenGlSdlCompositorFactory(OpenGL::IOpenGLContext& openGlContext) :
-    openGlContext_(openGlContext)
-  {}
-
-  ICompositor* GetCompositor(const Scene2D& scene)
-  {
-
-    OpenGLCompositor* compositor = new OpenGLCompositor(openGlContext_, scene);
-    compositor->SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
-                         FONT_SIZE_0, Orthanc::Encoding_Latin1);
-    compositor->SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
-                         FONT_SIZE_1, Orthanc::Encoding_Latin1);
-    return compositor;
-  }
-};
-
-static void GLAPIENTRY
-OpenGLMessageCallback(GLenum source,
-                      GLenum type,
-                      GLuint id,
-                      GLenum severity,
-                      GLsizei length,
-                      const GLchar* message,
-                      const void* userParam)
-{
-  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
-  {
-    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
-            (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
-            type, severity, message);
-  }
-}
-
-
-/**
- * IMPORTANT: The full arguments to "main()" are needed for SDL on
- * Windows. Otherwise, one gets the linking error "undefined reference
- * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
- **/
-int main(int argc, char* argv[])
-{
-  using namespace OrthancStone;
-
-  StoneInitialize();
-  Orthanc::Logging::EnableInfoLevel(true);
-  //  Orthanc::Logging::EnableTraceLevel(true);
-
-  try
-  {
-    OrthancStone::NativeApplicationContext context;
-    OrthancStone::NativeApplicationContext::WriterLock lock(context);
-    OrthancStone::ThreadedOracle oracle(context);
-
-    // False means we do NOT let Windows treat this as a legacy application
-    // that needs to be scaled
-    SdlOpenGLWindow window("Hello", 1024, 1024, false);
-
-    glEnable(GL_DEBUG_OUTPUT);
-    glDebugMessageCallback(OpenGLMessageCallback, 0);
-
-    std::unique_ptr<OpenGlSdlCompositorFactory> compositorFactory(new OpenGlSdlCompositorFactory(window));
-    boost::shared_ptr<RadiographyEditorApp> app(new RadiographyEditorApp(oracle, lock.GetOracleObservable(), compositorFactory.release()));
-    app->PrepareScene();
-    app->FitContent(window.GetCanvasWidth(), window.GetCanvasHeight());
-
-    bool stopApplication = false;
-
-    while (!stopApplication)
-    {
-      app->Refresh();
-
-      SDL_Event event;
-      while (!stopApplication && SDL_PollEvent(&event))
-      {
-        OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None;
-        if (event.key.keysym.mod & KMOD_CTRL)
-          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Control));
-        if (event.key.keysym.mod & KMOD_ALT)
-          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Alt));
-        if (event.key.keysym.mod & KMOD_SHIFT)
-          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Shift));
-
-        OrthancStone::MouseButton button;
-        if (event.button.button == SDL_BUTTON_LEFT)
-          button = OrthancStone::MouseButton_Left;
-        else if (event.button.button == SDL_BUTTON_MIDDLE)
-          button = OrthancStone::MouseButton_Middle;
-        else if (event.button.button == SDL_BUTTON_RIGHT)
-          button = OrthancStone::MouseButton_Right;
-
-        if (event.type == SDL_QUIT)
-        {
-          stopApplication = true;
-          break;
-        }
-        else if (event.type == SDL_WINDOWEVENT &&
-                 event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
-        {
-          app->DisableTracker(); // was: tracker.reset(NULL);
-          app->UpdateSize();
-        }
-        else if (event.type == SDL_KEYDOWN &&
-                 event.key.repeat == 0 /* Ignore key bounce */)
-        {
-          switch (event.key.keysym.sym)
-          {
-          case SDLK_f:
-            window.GetWindow().ToggleMaximize();
-            break;
-
-          case SDLK_q:
-            stopApplication = true;
-            break;
-          default:
-          {
-            app->OnKeyPressed(event.key.keysym.sym, modifiers);
-           }
-          }
-        }
-        else if (event.type == SDL_MOUSEBUTTONDOWN)
-        {
-          app->OnMouseDown(event.button.x, event.button.y, modifiers, button);
-        }
-        else if (event.type == SDL_MOUSEMOTION)
-        {
-          app->OnMouseMove(event.button.x, event.button.y, modifiers);
-        }
-        else if (event.type == SDL_MOUSEBUTTONUP)
-        {
-          app->OnMouseUp(event.button.x, event.button.y, modifiers, button);
-        }
-      }
-      SDL_Delay(1);
-    }
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "EXCEPTION: " << e.What();
-  }
-
-  StoneFinalize();
-
-  return 0;
-}
-
-
--- a/Samples/Deprecated/Sdl/TrackerSample.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "TrackerSampleApp.h"
-
- // From Stone
-#include "../../Framework/OpenGL/SdlOpenGLContext.h"
-#include "../../Framework/Scene2D/CairoCompositor.h"
-#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/StoneInitialization.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-
-#include <SDL.h>
-#include <stdio.h>
-
-/*
-TODO:
-
-- to decouple the trackers from the sample, we need to supply them with
-  the scene rather than the app
-
-- in order to do that, we need a GetNextFreeZIndex function (or something 
-  along those lines) in the scene object
-
-*/
-
-boost::weak_ptr<OrthancStone::TrackerSampleApp> g_app;
-
-void TrackerSample_SetInfoDisplayMessage(std::string key, std::string value)
-{
-  boost::shared_ptr<OrthancStone::TrackerSampleApp> app = g_app.lock();
-  if (app)
-  {
-    app->SetInfoDisplayMessage(key, value);
-  }
-}
-
-/**
- * IMPORTANT: The full arguments to "main()" are needed for SDL on
- * Windows. Otherwise, one gets the linking error "undefined reference
- * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
- **/
-int main(int argc, char* argv[])
-{
-  using namespace OrthancStone;
-
-  StoneInitialize();
-  Orthanc::Logging::EnableInfoLevel(true);
-//  Orthanc::Logging::EnableTraceLevel(true);
-
-  try
-  {
-    MessageBroker broker;
-    boost::shared_ptr<TrackerSampleApp> app(new TrackerSampleApp(broker));
-    g_app = app;
-    app->PrepareScene();
-    app->Run();
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "EXCEPTION: " << e.What();
-  }
-
-  StoneFinalize();
-
-  return 0;
-}
-
-
--- a/Samples/Deprecated/Sdl/TrackerSampleApp.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,733 +0,0 @@
-/**
- * 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/>.
- **/
-
-#include "TrackerSampleApp.h"
-
-#include "../../Framework/OpenGL/SdlOpenGLContext.h"
-#include "../../Framework/Scene2D/CairoCompositor.h"
-#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/Scene2D/PanSceneTracker.h"
-#include "../../Framework/Scene2D/RotateSceneTracker.h"
-#include "../../Framework/Scene2D/Scene2D.h"
-#include "../../Framework/Scene2D/ZoomSceneTracker.h"
-#include "../../Framework/Scene2DViewport/UndoStack.h"
-#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h"
-#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h"
-#include "../../Framework/StoneInitialization.h"
-
-// From Orthanc framework
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Images/PngWriter.h>
-
-#include <boost/ref.hpp>
-#include <boost/make_shared.hpp>
-#include <SDL.h>
-
-#include <stdio.h>
-
-namespace OrthancStone
-{
-  const char* MeasureToolToString(size_t i)
-  {
-    static const char* descs[] = {
-      "GuiTool_Rotate",
-      "GuiTool_Pan",
-      "GuiTool_Zoom",
-      "GuiTool_LineMeasure",
-      "GuiTool_CircleMeasure",
-      "GuiTool_AngleMeasure",
-      "GuiTool_EllipseMeasure",
-      "GuiTool_LAST"
-    };
-    if (i >= GuiTool_LAST)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index");
-    }
-    return descs[i];
-  }
-
-  void TrackerSampleApp::SelectNextTool()
-  {
-    currentTool_ = static_cast<GuiTool>(currentTool_ + 1);
-    if (currentTool_ == GuiTool_LAST)
-      currentTool_ = static_cast<GuiTool>(0);;
-    printf("Current tool is now: %s\n", MeasureToolToString(currentTool_));
-  }
-
-  void TrackerSampleApp::DisplayInfoText()
-  {
-    // do not try to use stuff too early!
-    std::stringstream msg;
-	
-	for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin();
-		kv != infoTextMap_.end(); ++kv)
-    {
-      msg << kv->first << " : " << kv->second << std::endl;
-    }
-	std::string msgS = msg.str();
-
-    TextSceneLayer* layerP = NULL;
-    if (controller_->GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX))
-    {
-      TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>(
-        controller_->GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX));
-      layerP = &layer;
-    }
-    else
-    {
-      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-      layerP = layer.get();
-      layer->SetColor(0, 255, 0);
-      layer->SetFontIndex(1);
-      layer->SetBorder(20);
-      layer->SetAnchor(BitmapAnchor_TopLeft);
-      //layer->SetPosition(0,0);
-      controller_->GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release());
-    }
-    // position the fixed info text in the upper right corner
-    layerP->SetText(msgS.c_str());
-    double cX = GetCompositor().GetCanvasWidth() * (-0.5);
-    double cY = GetCompositor().GetCanvasHeight() * (-0.5);
-    controller_->GetScene().GetCanvasToSceneTransform().Apply(cX,cY);
-    layerP->SetPosition(cX, cY);
-  }
-
-  void TrackerSampleApp::DisplayFloatingCtrlInfoText(const PointerEvent& e)
-  {
-    ScenePoint2D p = e.GetMainPosition().Apply(controller_->GetScene().GetCanvasToSceneTransform());
-
-    char buf[128];
-    sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", 
-      p.GetX(), p.GetY(), 
-      e.GetMainPosition().GetX(), e.GetMainPosition().GetY());
-
-    if (controller_->GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX))
-    {
-      TextSceneLayer& layer =
-        dynamic_cast<TextSceneLayer&>(controller_->GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX));
-      layer.SetText(buf);
-      layer.SetPosition(p.GetX(), p.GetY());
-    }
-    else
-    {
-      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-      layer->SetColor(0, 255, 0);
-      layer->SetText(buf);
-      layer->SetBorder(20);
-      layer->SetAnchor(BitmapAnchor_BottomCenter);
-      layer->SetPosition(p.GetX(), p.GetY());
-      controller_->GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release());
-    }
-  }
-
-  void TrackerSampleApp::HideInfoText()
-  {
-    controller_->GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX);
-  }
-
-  ScenePoint2D TrackerSampleApp::GetRandomPointInScene() const
-  {
-    unsigned int w = GetCompositor().GetCanvasWidth();
-    LOG(TRACE) << "GetCompositor().GetCanvasWidth() = " << 
-      GetCompositor().GetCanvasWidth();
-    unsigned int h = GetCompositor().GetCanvasHeight();
-    LOG(TRACE) << "GetCompositor().GetCanvasHeight() = " << 
-      GetCompositor().GetCanvasHeight();
-
-    if ((w >= RAND_MAX) || (h >= RAND_MAX))
-      LOG(WARNING) << "Canvas is too big : tools will not be randomly placed";
-
-    int x = rand() % w;
-    int y = rand() % h;
-    LOG(TRACE) << "random x = " << x << "random y = " << y;
-
-    ScenePoint2D p = controller_->GetViewport().GetPixelCenterCoordinates(x, y);
-    LOG(TRACE) << "--> p.GetX() = " << p.GetX() << " p.GetY() = " << p.GetY();
-
-    ScenePoint2D r = p.Apply(controller_->GetScene().GetCanvasToSceneTransform());
-    LOG(TRACE) << "--> r.GetX() = " << r.GetX() << " r.GetY() = " << r.GetY();
-    return r;
-  }
-
-  void TrackerSampleApp::CreateRandomMeasureTool()
-  {
-    static bool srandCalled = false;
-    if (!srandCalled)
-    {
-      srand(42);
-      srandCalled = true;
-    }
-
-    int i = rand() % 2;
-    LOG(TRACE) << "random i = " << i;
-    switch (i)
-    {
-    case 0:
-      // line measure
-      {
-        boost::shared_ptr<CreateLineMeasureCommand> cmd = 
-          boost::make_shared<CreateLineMeasureCommand>(
-		  boost::ref(IObserver::GetBroker()),
-            controller_,
-            GetRandomPointInScene());
-        cmd->SetEnd(GetRandomPointInScene());
-        controller_->PushCommand(cmd);
-      }
-      break;
-    case 1:
-      // angle measure
-      {
-      boost::shared_ptr<CreateAngleMeasureCommand> cmd =
-        boost::make_shared<CreateAngleMeasureCommand>(
-          boost::ref(IObserver::GetBroker()),
-          controller_,
-          GetRandomPointInScene());
-        cmd->SetCenter(GetRandomPointInScene());
-        cmd->SetSide2End(GetRandomPointInScene());
-        controller_->PushCommand(cmd);
-      }
-      break;
-    }
-  }
-
-  void TrackerSampleApp::HandleApplicationEvent(
-    const SDL_Event & event)
-  {
-    DisplayInfoText();
-
-    if (event.type == SDL_MOUSEMOTION)
-    {
-      int scancodeCount = 0;
-      const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
-
-      if (activeTracker_.get() == NULL &&
-        SDL_SCANCODE_LALT < scancodeCount &&
-        keyboardState[SDL_SCANCODE_LALT])
-      {
-        // The "left-ctrl" key is down, while no tracker is present
-        // Let's display the info text
-        PointerEvent e;
-        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
-          event.button.x, event.button.y));
-
-        DisplayFloatingCtrlInfoText(e);
-      }
-      else if (activeTracker_.get() != NULL)
-      {
-        HideInfoText();
-        //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)";
-        if (activeTracker_.get() != NULL)
-        {
-          //LOG(TRACE) << "(activeTracker_.get() != NULL)";
-          PointerEvent e;
-          e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
-            event.button.x, event.button.y));
-          
-          //LOG(TRACE) << "event.button.x = " << event.button.x << "     " <<
-          //  "event.button.y = " << event.button.y;
-          LOG(TRACE) << "activeTracker_->PointerMove(e); " <<
-            e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY();
-          
-          activeTracker_->PointerMove(e);
-          if (!activeTracker_->IsAlive())
-            activeTracker_.reset();
-        }
-      }
-      else
-      {
-        HideInfoText();
-
-        PointerEvent e;
-        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
-
-        ScenePoint2D scenePos = e.GetMainPosition().Apply(
-          controller_->GetScene().GetCanvasToSceneTransform());
-        //auto measureTools = GetController()->HitTestMeasureTools(scenePos);
-        //LOG(TRACE) << "# of hit tests: " << measureTools.size();
-        
-        // this returns the collection of measuring tools where hit test is true
-        std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos);
-
-        // let's refresh the measuring tools highlighted state
-        // first let's tag them as "unhighlighted"
-        controller_->ResetMeasuringToolsHighlight();
-
-        // then immediately take the first one and ask it to highlight the 
-        // measuring tool UI part that is hot
-        if (measureTools.size() > 0)
-        {
-          measureTools[0]->Highlight(scenePos);
-        }
-      }
-    }
-    else if (event.type == SDL_MOUSEBUTTONUP)
-    {
-      if (activeTracker_)
-      {
-        PointerEvent e;
-        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
-        activeTracker_->PointerUp(e);
-        if (!activeTracker_->IsAlive())
-          activeTracker_.reset();
-      }
-    }
-    else if (event.type == SDL_MOUSEBUTTONDOWN)
-    {
-      PointerEvent e;
-      e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
-        event.button.x, event.button.y));
-      if (activeTracker_)
-      {
-        activeTracker_->PointerDown(e);
-        if (!activeTracker_->IsAlive())
-          activeTracker_.reset();
-      }
-      else
-      {
-        // we ATTEMPT to create a tracker if need be
-        activeTracker_ = CreateSuitableTracker(event, e);
-      }
-    }
-    else if (event.type == SDL_KEYDOWN &&
-      event.key.repeat == 0 /* Ignore key bounce */)
-    {
-      switch (event.key.keysym.sym)
-      {
-      case SDLK_ESCAPE:
-        if (activeTracker_)
-        {
-          activeTracker_->Cancel();
-          if (!activeTracker_->IsAlive())
-            activeTracker_.reset();
-        }
-        break;
-
-      case SDLK_t:
-        if (!activeTracker_)
-          SelectNextTool();
-        else
-        {
-          LOG(WARNING) << "You cannot change the active tool when an interaction"
-            " is taking place";
-        }
-        break;
-
-      case SDLK_m:
-        CreateRandomMeasureTool();
-        break;
-      case SDLK_s:
-        controller_->FitContent(GetCompositor().GetCanvasWidth(),
-          GetCompositor().GetCanvasHeight());
-        break;
-
-      case SDLK_z:
-        LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
-        if (event.key.keysym.mod & KMOD_CTRL)
-        {
-          if (controller_->CanUndo())
-          {
-            LOG(TRACE) << "Undoing...";
-            controller_->Undo();
-          }
-          else
-          {
-            LOG(WARNING) << "Nothing to undo!!!";
-          }
-        }
-        break;
-
-      case SDLK_y:
-        LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
-        if (event.key.keysym.mod & KMOD_CTRL)
-        {
-          if (controller_->CanRedo())
-          {
-            LOG(TRACE) << "Redoing...";
-            controller_->Redo();
-          }
-          else
-          {
-            LOG(WARNING) << "Nothing to redo!!!";
-          }
-        }
-        break;
-
-      case SDLK_c:
-        TakeScreenshot(
-          "screenshot.png",
-          GetCompositor().GetCanvasWidth(),
-          GetCompositor().GetCanvasHeight());
-        break;
-
-      default:
-        break;
-      }
-    }
-  }
-
-
-  void TrackerSampleApp::OnSceneTransformChanged(
-    const ViewportController::SceneTransformChanged& message)
-  {
-    DisplayInfoText();
-  }
-
-  boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::CreateSuitableTracker(
-    const SDL_Event & event,
-    const PointerEvent & e)
-  {
-    using namespace Orthanc;
-
-    switch (event.button.button)
-    {
-    case SDL_BUTTON_MIDDLE:
-      return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker
-        (controller_, e));
-
-    case SDL_BUTTON_RIGHT:
-      return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker
-        (controller_, e, GetCompositor().GetCanvasHeight()));
-
-    case SDL_BUTTON_LEFT:
-    {
-      //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:";
-      // TODO: we need to iterate on the set of measuring tool and perform
-      // a hit test to check if a tracker needs to be created for edition.
-      // Otherwise, depending upon the active tool, we might want to create
-      // a "measuring tool creation" tracker
-
-      // TODO: if there are conflicts, we should prefer a tracker that 
-      // pertains to the type of measuring tool currently selected (TBD?)
-      boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e);
-
-      if (hitTestTracker != NULL)
-      {
-        //LOG(TRACE) << "hitTestTracker != NULL";
-        return hitTestTracker;
-      }
-      else
-      {
-        switch (currentTool_)
-        {
-        case GuiTool_Rotate:
-          //LOG(TRACE) << "Creating RotateSceneTracker";
-          return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker(
-            controller_, e));
-        case GuiTool_Pan:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker(
-            controller_, e));
-        case GuiTool_Zoom:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker(
-            controller_, e, GetCompositor().GetCanvasHeight()));
-        //case GuiTool_AngleMeasure:
-        //  return new AngleMeasureTracker(GetScene(), e);
-        //case GuiTool_CircleMeasure:
-        //  return new CircleMeasureTracker(GetScene(), e);
-        //case GuiTool_EllipseMeasure:
-        //  return new EllipseMeasureTracker(GetScene(), e);
-        case GuiTool_LineMeasure:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker(
-            IObserver::GetBroker(), controller_, e));
-        case GuiTool_AngleMeasure:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker(
-            IObserver::GetBroker(), controller_, e));
-        case GuiTool_CircleMeasure:
-          LOG(ERROR) << "Not implemented yet!";
-          return boost::shared_ptr<IFlexiblePointerTracker>();
-        case GuiTool_EllipseMeasure:
-          LOG(ERROR) << "Not implemented yet!";
-          return boost::shared_ptr<IFlexiblePointerTracker>();
-        default:
-          throw OrthancException(ErrorCode_InternalError, "Wrong tool!");
-        }
-      }
-    }
-    default:
-      return boost::shared_ptr<IFlexiblePointerTracker>();
-    }
-  }
-
-
-  TrackerSampleApp::TrackerSampleApp(MessageBroker& broker) : IObserver(broker)
-    , currentTool_(GuiTool_Rotate)
-    , undoStack_(new UndoStack)
-    , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled
-  {
-    controller_ = boost::shared_ptr<ViewportController>(
-      new ViewportController(undoStack_, broker, viewport_));
-
-    controller_->RegisterObserverCallback(
-      new Callable<TrackerSampleApp, ViewportController::SceneTransformChanged>
-      (*this, &TrackerSampleApp::OnSceneTransformChanged));
-
-    TEXTURE_2x2_1_ZINDEX = 1;
-    TEXTURE_1x1_ZINDEX = 2;
-    TEXTURE_2x2_2_ZINDEX = 3;
-    LINESET_1_ZINDEX = 4;
-    LINESET_2_ZINDEX = 5;
-    FLOATING_INFOTEXT_LAYER_ZINDEX = 6;
-    FIXED_INFOTEXT_LAYER_ZINDEX = 7;
-  }
-
-  void TrackerSampleApp::PrepareScene()
-  {
-    // Texture of 2x2 size
-    {
-      Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false);
-
-      uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0));
-      p[0] = 255;
-      p[1] = 0;
-      p[2] = 0;
-
-      p[3] = 0;
-      p[4] = 255;
-      p[5] = 0;
-
-      p = reinterpret_cast<uint8_t*>(i.GetRow(1));
-      p[0] = 0;
-      p[1] = 0;
-      p[2] = 255;
-
-      p[3] = 255;
-      p[4] = 0;
-      p[5] = 0;
-
-      controller_->GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i));
-
-      std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
-      l->SetOrigin(-3, 2);
-      l->SetPixelSpacing(1.5, 1);
-      l->SetAngle(20.0 / 180.0 * M_PI);
-      controller_->GetScene().SetLayer(TEXTURE_2x2_2_ZINDEX, l.release());
-    }
-
-    // Texture of 1x1 size
-    {
-      Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false);
-
-      uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0));
-      p[0] = 255;
-      p[1] = 0;
-      p[2] = 0;
-
-      std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
-      l->SetOrigin(-2, 1);
-      l->SetAngle(20.0 / 180.0 * M_PI);
-      controller_->GetScene().SetLayer(TEXTURE_1x1_ZINDEX, l.release());
-    }
-
-    // Some lines
-    {
-      std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer);
-
-      layer->SetThickness(1);
-
-      PolylineSceneLayer::Chain chain;
-      chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5));
-      chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5));
-      chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5));
-      chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5));
-      layer->AddChain(chain, true, 255, 0, 0);
-
-      chain.clear();
-      chain.push_back(ScenePoint2D(-5, -5));
-      chain.push_back(ScenePoint2D(5, -5));
-      chain.push_back(ScenePoint2D(5, 5));
-      chain.push_back(ScenePoint2D(-5, 5));
-      layer->AddChain(chain, true, 0, 255, 0);
-
-      double dy = 1.01;
-      chain.clear();
-      chain.push_back(ScenePoint2D(-4, -4));
-      chain.push_back(ScenePoint2D(4, -4 + dy));
-      chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy));
-      chain.push_back(ScenePoint2D(4, 2));
-      layer->AddChain(chain, false, 0, 0, 255);
-
-      controller_->GetScene().SetLayer(LINESET_1_ZINDEX, layer.release());
-    }
-
-    // Some text
-    {
-      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-      layer->SetText("Hello");
-      controller_->GetScene().SetLayer(LINESET_2_ZINDEX, layer.release());
-    }
-  }
-
-
-  void TrackerSampleApp::DisableTracker()
-  {
-    if (activeTracker_)
-    {
-      activeTracker_->Cancel();
-      activeTracker_.reset();
-    }
-  }
-
-  void TrackerSampleApp::TakeScreenshot(const std::string& target,
-    unsigned int canvasWidth,
-    unsigned int canvasHeight)
-  {
-    CairoCompositor compositor(controller_->GetScene(), canvasWidth, canvasHeight);
-    compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1);
-    compositor.Refresh();
-
-    Orthanc::ImageAccessor canvas;
-    compositor.GetCanvas().GetReadOnlyAccessor(canvas);
-
-    Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
-    Orthanc::ImageProcessing::Convert(png, canvas);
-
-    Orthanc::PngWriter writer;
-    writer.WriteToFile(target, png);
-  }
-
-
-  boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::TrackerHitTest(const PointerEvent & e)
-  {
-    // std::vector<boost::shared_ptr<MeasureTool>> measureTools_;
-    ScenePoint2D scenePos = e.GetMainPosition().Apply(
-      controller_->GetScene().GetCanvasToSceneTransform());
-
-    std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos);
-
-    if (measureTools.size() > 0)
-    {
-      return measureTools[0]->CreateEditionTracker(e);
-    }
-    return boost::shared_ptr<IFlexiblePointerTracker>();
-  }
-
-  static void GLAPIENTRY
-    OpenGLMessageCallback(GLenum source,
-      GLenum type,
-      GLuint id,
-      GLenum severity,
-      GLsizei length,
-      const GLchar* message,
-      const void* userParam)
-  {
-    if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
-    {
-      fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
-        (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
-        type, severity, message);
-    }
-  }
-
-  static bool g_stopApplication = false;
-  
-  ICompositor& TrackerSampleApp::GetCompositor()
-  {
-    using namespace Orthanc;
-    try
-    {
-      SdlViewport& viewport = dynamic_cast<SdlViewport&>(viewport_);
-      return viewport.GetCompositor();
-    }
-    catch (std::bad_cast e)
-    {
-      throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!");
-     }
-  }
-
-  const ICompositor& TrackerSampleApp::GetCompositor() const
-  {
-    using namespace Orthanc;
-    try
-    {
-      SdlViewport& viewport = const_cast<SdlViewport&>(dynamic_cast<const SdlViewport&>(viewport_));
-      return viewport.GetCompositor();
-    }
-    catch (std::bad_cast e)
-    {
-      throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!");
-    }
-  }
-
-
-  void TrackerSampleApp::Run()
-  {
-    controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight());
-
-    glEnable(GL_DEBUG_OUTPUT);
-    glDebugMessageCallback(OpenGLMessageCallback, 0);
-
-    GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
-      FONT_SIZE_0, Orthanc::Encoding_Latin1);
-    GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
-      FONT_SIZE_1, Orthanc::Encoding_Latin1);
-
-    while (!g_stopApplication)
-    {
-      GetCompositor().Refresh();
-
-      SDL_Event event;
-      while (!g_stopApplication && SDL_PollEvent(&event))
-      {
-        if (event.type == SDL_QUIT)
-        {
-          g_stopApplication = true;
-          break;
-        }
-        else if (event.type == SDL_WINDOWEVENT &&
-          event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
-        {
-          DisableTracker(); // was: tracker.reset(NULL);
-        }
-        else if (event.type == SDL_KEYDOWN &&
-          event.key.repeat == 0 /* Ignore key bounce */)
-        {
-          switch (event.key.keysym.sym)
-          {
-          case SDLK_f:
-            viewport_.GetWindow().ToggleMaximize();
-            break;
-
-          case SDLK_q:
-            g_stopApplication = true;
-            break;
-          default:
-            break;
-          }
-        }
-        HandleApplicationEvent(event);
-      }
-      SDL_Delay(1);
-    }
-  }
-
-  void TrackerSampleApp::SetInfoDisplayMessage(
-    std::string key, std::string value)
-  {
-    if (value == "")
-      infoTextMap_.erase(key);
-    else
-      infoTextMap_[key] = value;
-    DisplayInfoText();
-  }
-
-}
--- a/Samples/Deprecated/Sdl/TrackerSampleApp.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "../../Framework/Messages/IObserver.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
-#include "../../Framework/Scene2DViewport/MeasureTool.h"
-#include "../../Framework/Scene2DViewport/PredeclaredTypes.h"
-#include "../../Framework/Scene2DViewport/ViewportController.h"
-#include "../../Framework/Viewport/SdlViewport.h"
-
-#include <SDL.h>
-
-#include <boost/make_shared.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/enable_shared_from_this.hpp>
-
-namespace OrthancStone
-{
-  enum GuiTool
-  {
-    GuiTool_Rotate = 0,
-    GuiTool_Pan,
-    GuiTool_Zoom,
-    GuiTool_LineMeasure,
-    GuiTool_CircleMeasure,
-    GuiTool_AngleMeasure,
-    GuiTool_EllipseMeasure,
-    GuiTool_LAST
-  };
-
-  const char* MeasureToolToString(size_t i);
-
-  static const unsigned int FONT_SIZE_0 = 32;
-  static const unsigned int FONT_SIZE_1 = 24;
-
-  class Scene2D;
-  class UndoStack;
-
-  class TrackerSampleApp : public IObserver
-    , public boost::enable_shared_from_this<TrackerSampleApp>
-  {
-  public:
-    // 12 because.
-    TrackerSampleApp(MessageBroker& broker);
-    void PrepareScene();
-    void Run();
-    void SetInfoDisplayMessage(std::string key, std::string value);
-    void DisableTracker();
-
-    void HandleApplicationEvent(const SDL_Event& event);
-
-    /**
-    This method is called when the scene transform changes. It allows to
-    recompute the visual elements whose content depend upon the scene transform
-    */
-    void OnSceneTransformChanged(
-      const ViewportController::SceneTransformChanged& message);
-
-  private:
-    void SelectNextTool();
-    void CreateRandomMeasureTool();
-
-
-    /**
-    In the case of this app, the viewport is an SDL viewport and it has 
-    a OpenGLCompositor& GetCompositor() method
-    */
-    ICompositor& GetCompositor();
-
-    /**
-    See the other overload
-    */
-    const ICompositor& GetCompositor() const;
-
-    /**
-    This returns a random point in the canvas part of the scene, but in
-    scene coordinates
-    */
-    ScenePoint2D GetRandomPointInScene() const;
-
-    boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e);
-
-    boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker(
-      const SDL_Event& event,
-      const PointerEvent& e);
-
-    void TakeScreenshot(
-      const std::string& target,
-      unsigned int canvasWidth,
-      unsigned int canvasHeight);
-
-    /**
-      This adds the command at the top of the undo stack
-    */
-    void Commit(boost::shared_ptr<TrackerCommand> cmd);
-    void Undo();
-    void Redo();
-
-  private:
-    void DisplayFloatingCtrlInfoText(const PointerEvent& e);
-    void DisplayInfoText();
-    void HideInfoText();
-
-  private:
-    /**
-    WARNING: the measuring tools do store a reference to the scene, and it 
-    paramount that the scene gets destroyed AFTER the measurement tools.
-    */
-    boost::shared_ptr<ViewportController> controller_;
-
-    std::map<std::string, std::string> infoTextMap_;
-    boost::shared_ptr<IFlexiblePointerTracker> activeTracker_;
-
-    //static const int LAYER_POSITION = 150;
-
-    int TEXTURE_2x2_1_ZINDEX;
-    int TEXTURE_1x1_ZINDEX;
-    int TEXTURE_2x2_2_ZINDEX;
-    int LINESET_1_ZINDEX;
-    int LINESET_2_ZINDEX;
-    int FLOATING_INFOTEXT_LAYER_ZINDEX;
-    int FIXED_INFOTEXT_LAYER_ZINDEX;
-
-    GuiTool currentTool_;
-    boost::shared_ptr<UndoStack> undoStack_;
-    SdlOpenGLViewport viewport_;
-  };
-
-}
--- a/Samples/Deprecated/Sdl/cpp.hint	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-#define ORTHANC_OVERRIDE
-#define ORTHANC_STONE_DEFINE_EMPTY_MESSAGE(FILE, LINE, NAME) class NAME : public ::OrthancStone::IMessage {};
--- a/Samples/Deprecated/WebAssembly/BasicMPR.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,427 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-
-#include "dev.h"
-
-#include <emscripten.h>
-
-#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
-#include "../../Framework/Oracle/SleepOracleCommand.h"
-#include "../../Framework/Oracle/WebAssemblyOracle.h"
-#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h"
-#include "../../Framework/StoneInitialization.h"
-#include "../../Framework/Volumes/VolumeSceneLayerSource.h"
-
-
-namespace OrthancStone
-{
-  class VolumeSlicerWidget : public IObserver
-  {
-  private:
-    OrthancStone::WebAssemblyViewport      viewport_;
-    std::unique_ptr<VolumeSceneLayerSource>  source_;
-    VolumeProjection                       projection_;
-    std::vector<CoordinateSystem3D>        planes_;
-    size_t                                 currentPlane_;
-
-    void Handle(const DicomVolumeImage::GeometryReadyMessage& message)
-    {
-      LOG(INFO) << "Geometry is available";
-
-      const VolumeImageGeometry& geometry = message.GetOrigin().GetGeometry();
-
-      const unsigned int depth = geometry.GetProjectionDepth(projection_);
-      currentPlane_ = depth / 2;
-      
-      planes_.resize(depth);
-
-      for (unsigned int z = 0; z < depth; z++)
-      {
-        planes_[z] = geometry.GetProjectionSlice(projection_, z);
-      }
-
-      Refresh();
-
-      viewport_.FitContent();
-    }
-    
-  public:
-    VolumeSlicerWidget(MessageBroker& broker,
-                         const std::string& canvas,
-                         VolumeProjection projection) :
-      IObserver(broker),
-      viewport_(broker, canvas),
-      projection_(projection),
-      currentPlane_(0)
-    {
-    }
-
-    void UpdateSize()
-    {
-      viewport_.UpdateSize();
-    }
-
-    void SetSlicer(int layerDepth,
-                   const boost::shared_ptr<IVolumeSlicer>& slicer,
-                   IObservable& loader,
-                   ILayerStyleConfigurator* configurator)
-    {
-      if (source_.get() != NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
-                                        "Only one slicer can be registered");
-      }
-      
-      loader.RegisterObserverCallback(
-        new Callable<VolumeSlicerWidget, DicomVolumeImage::GeometryReadyMessage>
-        (*this, &VolumeSlicerWidget::Handle));
-
-      source_.reset(new VolumeSceneLayerSource(viewport_.GetScene(), layerDepth, slicer));
-
-      if (configurator != NULL)
-      {
-        source_->SetConfigurator(configurator);
-      }
-    }    
-
-    void Refresh()
-    {
-      if (source_.get() != NULL &&
-          currentPlane_ < planes_.size())
-      {
-        source_->Update(planes_[currentPlane_]);
-        viewport_.Refresh();
-      }
-    }
-
-    size_t GetSlicesCount() const
-    {
-      return planes_.size();
-    }
-
-    void Scroll(int delta)
-    {
-      if (!planes_.empty())
-      {
-        int tmp = static_cast<int>(currentPlane_) + delta;
-        unsigned int next;
-
-        if (tmp < 0)
-        {
-          next = 0;
-        }
-        else if (tmp >= static_cast<int>(planes_.size()))
-        {
-          next = planes_.size() - 1;
-        }
-        else
-        {
-          next = static_cast<size_t>(tmp);
-        }
-
-        if (next != currentPlane_)
-        {
-          currentPlane_ = next;
-          Refresh();
-        }
-      }
-    }
-  };
-}
-
-
-
-
-boost::shared_ptr<OrthancStone::DicomVolumeImage>  ct_(new OrthancStone::DicomVolumeImage);
-
-boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader>  loader_;
-
-std::unique_ptr<OrthancStone::VolumeSlicerWidget>  widget1_;
-std::unique_ptr<OrthancStone::VolumeSlicerWidget>  widget2_;
-std::unique_ptr<OrthancStone::VolumeSlicerWidget>  widget3_;
-
-OrthancStone::MessageBroker  broker_;
-OrthancStone::WebAssemblyOracle  oracle_(broker_);
-
-
-EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
-{
-  try
-  {
-    if (widget1_.get() != NULL)
-    {
-      widget1_->UpdateSize();
-    }
-  
-    if (widget2_.get() != NULL)
-    {
-      widget2_->UpdateSize();
-    }
-  
-    if (widget3_.get() != NULL)
-    {
-      widget3_->UpdateSize();
-    }
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "Exception while updating canvas size: " << e.What();
-  }
-  
-  return true;
-}
-
-
-
-
-EM_BOOL OnAnimationFrame(double time, void *userData)
-{
-  try
-  {
-    if (widget1_.get() != NULL)
-    {
-      widget1_->Refresh();
-    }
-  
-    if (widget2_.get() != NULL)
-    {
-      widget2_->Refresh();
-    }
-  
-    if (widget3_.get() != NULL)
-    {
-      widget3_->Refresh();
-    }
-
-    return true;
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "Exception in the animation loop, stopping now: " << e.What();
-    return false;
-  }  
-}
-
-
-static bool ctrlDown_ = false;
-
-
-EM_BOOL OnMouseWheel(int eventType,
-                     const EmscriptenWheelEvent *wheelEvent,
-                     void *userData)
-{
-  try
-  {
-    if (userData != NULL)
-    {
-      int delta = 0;
-
-      if (wheelEvent->deltaY < 0)
-      {
-        delta = -1;
-      }
-           
-      if (wheelEvent->deltaY > 0)
-      {
-        delta = 1;
-      }
-
-      OrthancStone::VolumeSlicerWidget& widget =
-        *reinterpret_cast<OrthancStone::VolumeSlicerWidget*>(userData);
-      
-      if (ctrlDown_)
-      {
-        delta *= static_cast<int>(widget.GetSlicesCount() / 10);
-      }
-
-      widget.Scroll(delta);
-    }
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "Exception in the wheel event: " << e.What();
-  }
-  
-  return true;
-}
-
-
-EM_BOOL OnKeyDown(int eventType,
-                  const EmscriptenKeyboardEvent *keyEvent,
-                  void *userData)
-{
-  ctrlDown_ = keyEvent->ctrlKey;
-  return false;
-}
-
-
-EM_BOOL OnKeyUp(int eventType,
-                const EmscriptenKeyboardEvent *keyEvent,
-                void *userData)
-{
-  ctrlDown_ = false;
-  return false;
-}
-
-
-
-
-namespace OrthancStone
-{
-  class TestSleep : public IObserver
-  {
-  private:
-    WebAssemblyOracle&  oracle_;
-
-    void Schedule()
-    {
-      oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(2000));
-    }
-    
-    void Handle(const SleepOracleCommand::TimeoutMessage& message)
-    {
-      LOG(INFO) << "TIMEOUT";
-      Schedule();
-    }
-    
-  public:
-    TestSleep(MessageBroker& broker,
-              WebAssemblyOracle& oracle) :
-      IObserver(broker),
-      oracle_(oracle)
-    {
-      oracle.RegisterObserverCallback(
-        new Callable<TestSleep, SleepOracleCommand::TimeoutMessage>
-        (*this, &TestSleep::Handle));
-
-      LOG(INFO) << "STARTING";
-      Schedule();
-    }
-  };
-
-  //static TestSleep testSleep(broker_, oracle_);
-}
-
-
-
-static std::map<std::string, std::string> arguments_;
-
-static bool GetArgument(std::string& value,
-                        const std::string& key)
-{
-  std::map<std::string, std::string>::const_iterator found = arguments_.find(key);
-
-  if (found == arguments_.end())
-  {
-    return false;
-  }
-  else
-  {
-    value = found->second;
-    return true;
-  }
-}
-
-
-extern "C"
-{
-  int main(int argc, char const *argv[]) 
-  {
-    OrthancStone::StoneInitialize();
-    Orthanc::Logging::EnableInfoLevel(true);
-    // Orthanc::Logging::EnableTraceLevel(true);
-    EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded")););
-  }
-
-  EMSCRIPTEN_KEEPALIVE
-  void SetArgument(const char* key, const char* value)
-  {
-    // This is called for each GET argument (cf. "app.js")
-    LOG(INFO) << "Received GET argument: [" << key << "] = [" << value << "]";
-    arguments_[key] = value;
-  }
-
-  EMSCRIPTEN_KEEPALIVE
-  void Initialize()
-  {
-    try
-    {
-      oracle_.SetOrthancRoot("..");
-      
-      loader_.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct_, oracle_, oracle_));
-    
-      widget1_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas1", OrthancStone::VolumeProjection_Axial));
-      {
-        std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator);
-        style->SetLinearInterpolation(true);
-        style->SetWindowing(OrthancStone::ImageWindowing_Bone);
-        widget1_->SetSlicer(0, loader_, *loader_, style.release());
-      }
-      widget1_->UpdateSize();
-
-      widget2_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas2", OrthancStone::VolumeProjection_Coronal));
-      {
-        std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator);
-        style->SetLinearInterpolation(true);
-        style->SetWindowing(OrthancStone::ImageWindowing_Bone);
-        widget2_->SetSlicer(0, loader_, *loader_, style.release());
-      }
-      widget2_->UpdateSize();
-
-      widget3_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas3", OrthancStone::VolumeProjection_Sagittal));
-      {
-        std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator);
-        style->SetLinearInterpolation(true);
-        style->SetWindowing(OrthancStone::ImageWindowing_Bone);
-        widget3_->SetSlicer(0, loader_, *loader_, style.release());
-      }
-      widget3_->UpdateSize();
-
-      emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnWindowResize); // DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 !!
-
-      emscripten_set_wheel_callback("#mycanvas1", widget1_.get(), false, OnMouseWheel);
-      emscripten_set_wheel_callback("#mycanvas2", widget2_.get(), false, OnMouseWheel);
-      emscripten_set_wheel_callback("#mycanvas3", widget3_.get(), false, OnMouseWheel);
-
-      emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyDown);
-      emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, OnKeyUp);
-    
-      emscripten_request_animation_frame_loop(OnAnimationFrame, NULL);
-
-
-      std::string ct;
-      if (GetArgument(ct, "ct"))
-      {
-        //loader_->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");
-        loader_->LoadSeries(ct);
-      }
-      else
-      {
-        LOG(ERROR) << "No Orthanc identifier for the CT series was provided";
-      }
-    }
-    catch (Orthanc::OrthancException& e)
-    {
-      LOG(ERROR) << "Exception during Initialize(): " << e.What();
-    }
-  }
-}
--- a/Samples/Deprecated/WebAssembly/BasicMPR.html	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-<!doctype html>
-<html lang="en-us">
-  <head>
-    <meta charset="utf-8">
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
-    <!-- Disable pinch zoom on mobile devices -->
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
-    <meta name="HandheldFriendly" content="true" />
-    
-    
-    <title>Stone of Orthanc</title>
-
-    <style>
-      html, body {
-      width: 100%;
-      height: 100%;
-      margin: 0px;
-      border: 0;
-      overflow: hidden; /*  Disable scrollbars */
-      display: block;  /* No floating content on sides */
-      }
-
-      #mycanvas1 {
-      position:absolute;
-      left:0%;
-      top:0%;
-      background-color: red;
-      width: 50%;
-      height: 100%;
-      }
-
-      #mycanvas2 {
-      position:absolute;
-      left:50%;
-      top:0%;
-      background-color: green;
-      width: 50%;
-      height: 50%;
-      }
-
-      #mycanvas3 {
-      position:absolute;
-      left:50%;
-      top:50%;
-      background-color: blue;
-      width: 50%;
-      height: 50%;
-      }
-    </style>
-  </head>
-  <body>
-    <canvas id="mycanvas1" oncontextmenu="return false;"></canvas>
-    <canvas id="mycanvas2" oncontextmenu="return false;"></canvas>
-    <canvas id="mycanvas3" oncontextmenu="return false;"></canvas>
-
-    <script type="text/javascript" src="app.js"></script>
-    <script type="text/javascript" async src="BasicMPR.js"></script>
-  </body>
-</html>
--- a/Samples/Deprecated/WebAssembly/BasicScene.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,210 +0,0 @@
-/**
- * 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/>.
- **/
-
-
-#include "dev.h"
-
-#include <emscripten.h>
-#include <emscripten/html5.h>
-
-// From Stone
-#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
-#include "../../Framework/StoneInitialization.h"
-
-// From Orthanc framework
-#include <Core/Images/Image.h>
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-void PrepareScene(OrthancStone::Scene2D& scene)
-{
-  using namespace OrthancStone;
-
-  // Texture of 2x2 size
-  if (1)
-  {
-    Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false);
-    
-    uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0));
-    p[0] = 255;
-    p[1] = 0;
-    p[2] = 0;
-
-    p[3] = 0;
-    p[4] = 255;
-    p[5] = 0;
-
-    p = reinterpret_cast<uint8_t*>(i.GetRow(1));
-    p[0] = 0;
-    p[1] = 0;
-    p[2] = 255;
-
-    p[3] = 255;
-    p[4] = 0;
-    p[5] = 0;
-
-    scene.SetLayer(12, new ColorTextureSceneLayer(i));
-
-    std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
-    l->SetOrigin(-3, 2);
-    l->SetPixelSpacing(1.5, 1);
-    l->SetAngle(20.0 / 180.0 * M_PI);
-    scene.SetLayer(14, l.release());
-  }
-
-  // Texture of 1x1 size
-  if (1)
-  {
-    Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false);
-    
-    uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0));
-    p[0] = 255;
-    p[1] = 0;
-    p[2] = 0;
-
-    std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
-    l->SetOrigin(-2, 1);
-    l->SetAngle(20.0 / 180.0 * M_PI);
-    scene.SetLayer(13, l.release());
-  }
-
-  // Some lines
-  if (1)
-  {
-    std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer);
-
-    layer->SetThickness(1);
-
-    PolylineSceneLayer::Chain chain;
-    chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5));
-    chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5));
-    chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5));
-    chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5));
-    layer->AddChain(chain, true, 255, 0, 0);
-
-    chain.clear();
-    chain.push_back(ScenePoint2D(-5, -5));
-    chain.push_back(ScenePoint2D(5, -5));
-    chain.push_back(ScenePoint2D(5, 5));
-    chain.push_back(ScenePoint2D(-5, 5));
-    layer->AddChain(chain, true, 0, 255, 0);
-
-    double dy = 1.01;
-    chain.clear();
-    chain.push_back(ScenePoint2D(-4, -4));
-    chain.push_back(ScenePoint2D(4, -4 + dy));
-    chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy));
-    chain.push_back(ScenePoint2D(4, 2));
-    layer->AddChain(chain, false, 0, 0, 255);
-
-    scene.SetLayer(50, layer.release());
-  }
-
-  // Some text
-  if (1)
-  {
-    std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-    layer->SetText("Hello");
-    scene.SetLayer(100, layer.release());
-  }
-}
-
-
-std::unique_ptr<OrthancStone::WebAssemblyViewport>  viewport1_;
-std::unique_ptr<OrthancStone::WebAssemblyViewport>  viewport2_;
-std::unique_ptr<OrthancStone::WebAssemblyViewport>  viewport3_;
-boost::shared_ptr<OrthancStone::ViewportController>   controller1_;
-boost::shared_ptr<OrthancStone::ViewportController>   controller2_;
-boost::shared_ptr<OrthancStone::ViewportController>   controller3_;
-OrthancStone::MessageBroker broker_;
-
-
-EM_BOOL OnWindowResize(
-  int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
-{
-  if (viewport1_.get() != NULL)
-  {
-    viewport1_->UpdateSize();
-  }
-  
-  if (viewport2_.get() != NULL)
-  {
-    viewport2_->UpdateSize();
-  }
-  
-  if (viewport3_.get() != NULL)
-  {
-    viewport3_->UpdateSize();
-  }
-  
-  return true;
-}
-
-extern "C"
-{
-  int main(int argc, char const *argv[]) 
-  {
-    OrthancStone::StoneInitialize();
-    // Orthanc::Logging::EnableInfoLevel(true);
-    // Orthanc::Logging::EnableTraceLevel(true);
-    EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded")););
-  }
-
-  EMSCRIPTEN_KEEPALIVE
-  void Initialize()
-  {
-    viewport1_.reset(new OrthancStone::WebAssemblyViewport("mycanvas1"));
-    PrepareScene(viewport1_->GetScene());
-    viewport1_->UpdateSize();
-
-    viewport2_.reset(new OrthancStone::WebAssemblyViewport("mycanvas2"));
-    PrepareScene(viewport2_->GetScene());
-    viewport2_->UpdateSize();
-
-    viewport3_.reset(new OrthancStone::WebAssemblyViewport("mycanvas3"));
-    PrepareScene(viewport3_->GetScene());
-    viewport3_->UpdateSize();
-
-    viewport1_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
-                                        FONT_SIZE, Orthanc::Encoding_Latin1);
-    viewport2_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
-                                        FONT_SIZE, Orthanc::Encoding_Latin1);
-    viewport3_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
-                                        FONT_SIZE, Orthanc::Encoding_Latin1);
-
-    controller1_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport1_));
-    controller2_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport2_));
-    controller3_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport3_));
-
-    controller1_->FitContent(viewport1_->GetCanvasWidth(), viewport1_->GetCanvasHeight());
-    controller2_->FitContent(viewport2_->GetCanvasWidth(), viewport2_->GetCanvasHeight());
-    controller3_->FitContent(viewport3_->GetCanvasWidth(), viewport3_->GetCanvasHeight());
-
-    viewport1_->Refresh();
-    viewport2_->Refresh();
-    viewport3_->Refresh();
-
-    SetupEvents("mycanvas1", controller1_);
-    SetupEvents("mycanvas2", controller2_);
-    SetupEvents("mycanvas3", controller3_);
-
-    emscripten_set_resize_callback("#window", NULL, false, OnWindowResize);
-  }
-}
--- a/Samples/Deprecated/WebAssembly/BasicScene.html	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-<!doctype html>
-<html lang="en-us">
-  <head>
-    <meta charset="utf-8">
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
-    <!-- Disable pinch zoom on mobile devices -->
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
-    <meta name="HandheldFriendly" content="true" />
-    
-    
-    <title>Stone of Orthanc</title>
-
-    <style>
-      html, body {
-      width: 100%;
-      height: 100%;
-      margin: 0px;
-      border: 0;
-      overflow: hidden; /*  Disable scrollbars */
-      display: block;  /* No floating content on sides */
-      }
-
-      #mycanvas1 {
-      position:absolute;
-      left:0%;
-      top:0%;
-      background-color: red;
-      width: 50%;
-      height: 100%;
-      }
-
-      #mycanvas2 {
-      position:absolute;
-      left:50%;
-      top:0%;
-      background-color: green;
-      width: 50%;
-      height: 50%;
-      }
-
-      #mycanvas3 {
-      position:absolute;
-      left:50%;
-      top:50%;
-      background-color: blue;
-      width: 50%;
-      height: 50%;
-      }
-    </style>
-  </head>
-  <body>
-    <canvas id="mycanvas1" oncontextmenu="return false;"></canvas>
-    <canvas id="mycanvas2" oncontextmenu="return false;"></canvas>
-    <canvas id="mycanvas3" oncontextmenu="return false;"></canvas>
-
-    <script type="text/javascript">
-      if (!('WebAssembly' in window)) {
-      alert('Sorry, your browser does not support WebAssembly :(');
-      } else {
-      window.addEventListener('WebAssemblyLoaded', function() {
-      Module.ccall('Initialize', null, null, null);
-      });
-      }
-    </script>
-
-    <script type="text/javascript" async src="BasicScene.js"></script>
-  </body>
-</html>
--- a/Samples/Deprecated/WebAssembly/CMakeLists.txt	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-cmake_minimum_required(VERSION 2.8.3)
-
-
-#####################################################################
-## Configuration of the Emscripten compiler for WebAssembly target
-#####################################################################
-
-set(WASM_FLAGS "-s WASM=1 -s FETCH=1")
-
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}")
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1")
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0")
-#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXIT_RUNTIME=1")
-
-#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1")
-
-
-#####################################################################
-## Configuration of the Orthanc framework
-#####################################################################
-
-# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it
-# must be the first inclusion
-include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/Version.cmake)
-
-if (ORTHANC_STONE_VERSION STREQUAL "mainline")
-  set(ORTHANC_FRAMEWORK_VERSION "mainline")
-  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
-else()
-  set(ORTHANC_FRAMEWORK_VERSION "1.5.7")
-  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
-endif()
-
-set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")")
-set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"")
-set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
-
-
-#####################################################################
-## Configuration of the Stone framework
-#####################################################################
-
-include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake)
-include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
-
-DownloadPackage(
-  "a24b8136b8f3bb93f166baf97d9328de"
-  "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip"
-  "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83")
-
-set(ORTHANC_STONE_APPLICATION_RESOURCES
-  UBUNTU_FONT  ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf
-  )
-
-SET(ENABLE_GOOGLE_TEST OFF)
-SET(ENABLE_LOCALE ON)
-SET(ORTHANC_SANDBOXED ON)
-SET(ENABLE_WASM ON)
-
-include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake)
-
-add_definitions(
-  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
-  )
-
-
-#####################################################################
-## Build the samples
-#####################################################################
-
-add_library(OrthancStone STATIC
-  ${ORTHANC_STONE_SOURCES}
-  )
-
-
-if (ON)
-  add_executable(BasicScene
-    BasicScene.cpp
-    #${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.h
-    ${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.cpp
-    )
-
-  target_link_libraries(BasicScene OrthancStone)
-
-  install(
-    TARGETS BasicScene
-    RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}
-    )
-endif()
-
-
-if (ON)
-  add_executable(BasicMPR
-    BasicMPR.cpp
-    )
-
-  target_link_libraries(BasicMPR OrthancStone)
-
-  install(
-    TARGETS BasicMPR
-    RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}
-    )
-endif()
-  
-
-install(
-  FILES
-  ${CMAKE_CURRENT_BINARY_DIR}/BasicMPR.wasm
-  ${CMAKE_CURRENT_BINARY_DIR}/BasicScene.wasm
-  ${CMAKE_SOURCE_DIR}/BasicMPR.html
-  ${CMAKE_SOURCE_DIR}/BasicScene.html
-  ${CMAKE_SOURCE_DIR}/Configuration.json
-  ${CMAKE_SOURCE_DIR}/app.js
-  ${CMAKE_SOURCE_DIR}/index.html
-  DESTINATION ${CMAKE_INSTALL_PREFIX}
-  )
--- a/Samples/Deprecated/WebAssembly/Configuration.json	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-{
-  "Plugins": [
-    "/usr/local/share/orthanc/plugins/libOrthancWebViewer.so",
-    "/usr/local/share/orthanc/plugins/libServeFolders.so"
-  ],
-  "StorageDirectory" : "/var/lib/orthanc/db",
-  "IndexDirectory" : "/var/lib/orthanc/db",
-  "RemoteAccessAllowed" : true,
-  "AuthenticationEnabled" : false,
-  "ServeFolders" : {
-    "AllowCache" : false,
-    "GenerateETag" : true,
-    "Folders" : {
-      "/stone" : "/root/stone"
-    }
-  }
-}
--- a/Samples/Deprecated/WebAssembly/ConfigurationLocalSJO.json	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-{
-  "Plugins": [
-    "/home/jodogne/Subversion/orthanc-webviewer/r/libOrthancWebViewer.so",
-    "/home/jodogne/Subversion/orthanc/r/libServeFolders.so"
-  ],
-  "StorageDirectory" : "/tmp/orthanc-db",
-  "IndexDirectory" : "/tmp/orthanc-db",
-  "RemoteAccessAllowed" : true,
-  "AuthenticationEnabled" : false,
-  "ServeFolders" : {
-    "AllowCache" : false,
-    "GenerateETag" : true,
-    "Folders" : {
-      "/stone" : "/tmp/stone"
-    }
-  },
-  "WebViewer" : {
-    "CachePath" : "/tmp/orthanc-db/WebViewerCache"
-  }
-}
--- a/Samples/Deprecated/WebAssembly/NOTES.txt	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-Docker SJO
-==========
-
-$ source ~/Downloads/emsdk/emsdk_env.sh
-$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/tmp/stone
-$ ninja install
-$ docker run -p 4242:4242 -p 8042:8042 --rm -v /tmp/stone:/root/stone:ro -v /tmp/stone-db/:/var/lib/orthanc/db/ jodogne/orthanc-plugins:latest /root/stone/Configuration.json --verbose
-
-WARNING: This won't work using "orthanc-plugins:1.5.6", as support for
-PAM is mandatatory in "/instances/.../image-uint16".
-
-
-Docker BGO
-==========
-
-On Ubuntu WSL
--------------
-. ~/apps/emsdk/emsdk_env.sh
-cd /mnt/c/osi/dev/
-mkdir -p build_stone_newsamples_wasm_wsl
-mkdir -p build_install_stone_newsamples_wasm_wsl
-cd build_stone_newsamples_wasm_wsl
-cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl
-ninja install
-
-Then, on Windows
------------------
-docker run -p 4242:4242 -p 8042:8042 --rm -v "C:/osi/dev/build_install_stone_newsamples_wasm_wsl:/root/stone:ro" jodogne/orthanc-plugins:1.5.6 /root/stone/Configuration.json --verbose
-
-# WAIT A COUPLE OF SECS
-# if the archive has NOT already been unzipped, unzip it
-# upload dicom files to running orthanc
-
-cd C:\osi\dev\twiga-orthanc-viewer\demo\dicomfiles
-if (-not (test-path RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57)) { unzip RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57.zip}
-ImportDicomFiles.ps1 127.0.0.1 8042 .\RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57\
-
---> localhost:8042 --> Plugins --> serve-folders --> stone --> ...
-
-Local BGO
-==========
-
-. ~/apps/emsdk/emsdk_env.sh
-cd /mnt/c/osi/dev/
-mkdir -p build_stone_newsamples_wasm_wsl
-mkdir -p build_install_stone_newsamples_wasm_wsl
-cd build_stone_newsamples_wasm_wsl
-cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl
-
-
-
-TODO: Orthanc.exe 
-
-
-Local SJO
-==========
-
-$ source ~/Downloads/emsdk/emsdk_env.sh
-$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/tmp/stone
-$ ninja install
-
-$ make -C ~/Subversion/orthanc/r -j4
-$ make -C ~/Subversion/orthanc-webviewer/r -j4
-$ ~/Subversion/orthanc/r/Orthanc ../ConfigurationLocalSJO.json
-
-
-Local AM
-========
-
-. ~/apps/emsdk/emsdk_env.sh
-cd /mnt/c/o/
-mkdir -p build_stone_newsamples_wasm_wsl
-mkdir -p build_install_stone_newsamples_wasm_wsl
-cd build_stone_newsamples_wasm_wsl
-cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/fastcomp/emscripten/cmake/Modules/Platform/Emscripten.cmake -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=/mnt/c/o/orthanc/ -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/o/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/o/build_install_stone_newsamples_wasm_wsl
-ninja
--- a/Samples/Deprecated/WebAssembly/app.js	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/**
- * This is a generic bootstrap code that is shared by all the Stone
- * sample applications.
- **/
-
-// Check support for WebAssembly
-if (!('WebAssembly' in window)) {
-  alert('Sorry, your browser does not support WebAssembly :(');
-} else {
-
-  // Wait for the module to be loaded (the event "WebAssemblyLoaded"
-  // must be emitted by the "main" function)
-  window.addEventListener('WebAssemblyLoaded', function() {
-
-    // Loop over the GET arguments
-    var parameters = window.location.search.substr(1);
-    if (parameters != null && parameters != '') {
-      var tokens = parameters.split('&');
-      for (var i = 0; i < tokens.length; i++) {
-        var arg = tokens[i].split('=');
-        if (arg.length == 2) {
-
-          // Send each GET argument to WebAssembly
-          Module.ccall('SetArgument', null, [ 'string', 'string' ],
-                       [ arg[0], decodeURIComponent(arg[1]) ]);
-        }
-      }
-    }
-
-    // Inform the WebAssembly module that it can start
-    Module.ccall('Initialize', null, null, null);
-  });
-}
--- a/Samples/Deprecated/WebAssembly/dev.h	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,202 +0,0 @@
-/**
- * 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 "../../Framework/Viewport/WebAssemblyViewport.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/Scene2D/PanSceneTracker.h"
-#include "../../Framework/Scene2D/RotateSceneTracker.h"
-#include "../../Framework/Scene2D/ZoomSceneTracker.h"
-#include "../../Framework/Scene2DViewport/UndoStack.h"
-#include "../../Framework/Scene2DViewport/ViewportController.h"
-
-#include <Core/OrthancException.h>
-
-#include <emscripten/html5.h>
-#include <boost/make_shared.hpp>
-
-static const unsigned int FONT_SIZE = 32;
-
-namespace OrthancStone
-{
-  class ActiveTracker : public boost::noncopyable
-  {
-  private:
-    boost::shared_ptr<IFlexiblePointerTracker> tracker_;
-    std::string                             canvasIdentifier_;
-    bool                                    insideCanvas_;
-    
-  public:
-    ActiveTracker(const boost::shared_ptr<IFlexiblePointerTracker>& tracker,
-                  const std::string& canvasId) :
-      tracker_(tracker),
-      canvasIdentifier_(canvasId),
-      insideCanvas_(true)
-    {
-      if (tracker_.get() == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-    }
-
-    bool IsAlive() const
-    {
-      return tracker_->IsAlive();
-    }
-
-    void PointerMove(const PointerEvent& event)
-    {
-      tracker_->PointerMove(event);
-    }
-
-    void PointerUp(const PointerEvent& event)
-    {
-      tracker_->PointerUp(event);
-    }
-  };
-}
-
-static OrthancStone::PointerEvent* ConvertMouseEvent(
-  const EmscriptenMouseEvent&        source,
-  OrthancStone::IViewport& viewport)
-{
-  std::unique_ptr<OrthancStone::PointerEvent> target(
-    new OrthancStone::PointerEvent);
-
-  target->AddPosition(viewport.GetPixelCenterCoordinates(
-                        source.targetX, source.targetY));
-  target->SetAltModifier(source.altKey);
-  target->SetControlModifier(source.ctrlKey);
-  target->SetShiftModifier(source.shiftKey);
-
-  return target.release();
-}
-
-std::unique_ptr<OrthancStone::ActiveTracker> tracker_;
-
-EM_BOOL OnMouseEvent(int eventType, 
-                     const EmscriptenMouseEvent *mouseEvent, 
-                     void *userData)
-{
-  if (mouseEvent != NULL &&
-      userData != NULL)
-  {
-    boost::shared_ptr<OrthancStone::ViewportController>& controller = 
-      *reinterpret_cast<boost::shared_ptr<OrthancStone::ViewportController>*>(userData);
-
-    switch (eventType)
-    {
-      case EMSCRIPTEN_EVENT_CLICK:
-      {
-        static unsigned int count = 0;
-        char buf[64];
-        sprintf(buf, "click %d", count++);
-
-        std::unique_ptr<OrthancStone::TextSceneLayer> layer(new OrthancStone::TextSceneLayer);
-        layer->SetText(buf);
-        controller->GetViewport().GetScene().SetLayer(100, layer.release());
-        controller->GetViewport().Refresh();
-        break;
-      }
-
-      case EMSCRIPTEN_EVENT_MOUSEDOWN:
-      {
-        boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> t;
-
-        {
-          std::unique_ptr<OrthancStone::PointerEvent> event(
-            ConvertMouseEvent(*mouseEvent, controller->GetViewport()));
-
-          switch (mouseEvent->button)
-          {
-            case 0:  // Left button
-              emscripten_console_log("Creating RotateSceneTracker");
-              t.reset(new OrthancStone::RotateSceneTracker(
-                        controller, *event));
-              break;
-
-            case 1:  // Middle button
-              emscripten_console_log("Creating PanSceneTracker");
-              LOG(INFO) << "Creating PanSceneTracker" ;
-              t.reset(new OrthancStone::PanSceneTracker(
-                        controller, *event));
-              break;
-
-            case 2:  // Right button
-              emscripten_console_log("Creating ZoomSceneTracker");
-              t.reset(new OrthancStone::ZoomSceneTracker(
-                        controller, *event, controller->GetViewport().GetCanvasWidth()));
-              break;
-
-            default:
-              break;
-          }
-        }
-
-        if (t.get() != NULL)
-        {
-          tracker_.reset(
-            new OrthancStone::ActiveTracker(t, controller->GetViewport().GetCanvasIdentifier()));
-          controller->GetViewport().Refresh();
-        }
-
-        break;
-      }
-
-      case EMSCRIPTEN_EVENT_MOUSEMOVE:
-        if (tracker_.get() != NULL)
-        {
-          std::unique_ptr<OrthancStone::PointerEvent> event(
-            ConvertMouseEvent(*mouseEvent, controller->GetViewport()));
-          tracker_->PointerMove(*event);
-          controller->GetViewport().Refresh();
-        }
-        break;
-
-      case EMSCRIPTEN_EVENT_MOUSEUP:
-        if (tracker_.get() != NULL)
-        {
-          std::unique_ptr<OrthancStone::PointerEvent> event(
-            ConvertMouseEvent(*mouseEvent, controller->GetViewport()));
-          tracker_->PointerUp(*event);
-          controller->GetViewport().Refresh();
-          if (!tracker_->IsAlive())
-            tracker_.reset();
-        }
-        break;
-
-      default:
-        break;
-    }
-  }
-
-  return true;
-}
-
-
-void SetupEvents(const std::string& canvas,
-                 boost::shared_ptr<OrthancStone::ViewportController>& controller)
-{
-  emscripten_set_mousedown_callback(canvas.c_str(), &controller, false, OnMouseEvent);
-  emscripten_set_mousemove_callback(canvas.c_str(), &controller, false, OnMouseEvent);
-  emscripten_set_mouseup_callback(canvas.c_str(), &controller, false, OnMouseEvent);
-}
--- a/Samples/Deprecated/WebAssembly/index.html	Wed Apr 29 22:06:24 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-<!doctype html>
-<html lang="en-us">
-  <head>
-    <meta charset="utf-8">
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
-    <title>Stone of Orthanc</title>
-  </head>
-  <body>
-    <h1>Available samples</h1>
-    <ul>
-      <li><a href="BasicScene.html">Basic scene</a></li>
-      <li><a href="BasicMPR.html">Basic MPR display</a></li>
-    </ul>
-  </body>
-</html>
--- a/Samples/Sdl/RtViewer/CMakeLists.txt	Wed Apr 29 22:06:24 2020 +0200
+++ b/Samples/Sdl/RtViewer/CMakeLists.txt	Wed Apr 29 22:06:58 2020 +0200
@@ -11,7 +11,6 @@
 
 SET(ENABLE_GOOGLE_TEST OFF)
 SET(ENABLE_LOCALE ON)  # Necessary for text rendering
-SET(ENABLE_QT OFF)
 SET(ENABLE_SDL ON)
 SET(ENABLE_DCMTK ON)  # <==
 SET(ENABLE_OPENGL ON)  #  <==
--- a/Samples/Sdl/SingleFrameViewer/CMakeLists.txt	Wed Apr 29 22:06:24 2020 +0200
+++ b/Samples/Sdl/SingleFrameViewer/CMakeLists.txt	Wed Apr 29 22:06:58 2020 +0200
@@ -10,7 +10,6 @@
 
 SET(ENABLE_GOOGLE_TEST OFF)
 SET(ENABLE_LOCALE ON)  # Necessary for text rendering
-SET(ENABLE_QT OFF)
 SET(ENABLE_SDL ON)
 SET(ENABLE_DCMTK ON)  # <==
 SET(ENABLE_OPENGL ON)  #  <==
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/build-wasm-RtViewer.sh	Wed Apr 29 22:06:58 2020 +0200
@@ -0,0 +1,47 @@
+#!/bin/bash
+#
+# usage:
+# to build the sample in Debug:
+# ./build-wasm-SingleFrameViewer.sh
+#
+# to build the sample in Debug:
+# ./build-wasm-SingleFrameViewer.sh Release
+
+set -e
+
+if [ ! -d "WebAssembly" ]; then
+  echo "This script must be run from the Samples folder one level below orthanc-stone"
+  exit 1
+fi
+
+
+currentDir=$(pwd)
+samplesRootDir=$(pwd)
+devrootDir=$(pwd)/../../
+
+buildType=${1:-RelWithDebInfo}
+buildFolderName="$devrootDir/out/build-stone-wasm-RtViewer-$buildType"
+installFolderName="$devrootDir/out/install-stone-wasm-RtViewer-$buildType"
+
+mkdir -p $buildFolderName
+# change current folder to the build folder
+pushd $buildFolderName
+
+# configure the environment to use Emscripten
+source ~/apps/emsdk/emsdk_env.sh
+
+emcmake cmake -G "Ninja" \
+  -DCMAKE_BUILD_TYPE=$buildType \
+  -DCMAKE_INSTALL_PREFIX=$installFolderName \
+  -DSTATIC_BUILD=ON -DALLOW_DOWNLOADS=ON \
+  $samplesRootDir/WebAssembly/RtViewer
+
+# perform build + installation
+ninja install
+
+# restore the original working folder
+popd
+
+echo "If all went well, the output files can be found in $installFolderName:"
+
+ls $installFolderName
\ No newline at end of file
--- a/Samples/build-wasm-SingleFrameViewer.sh	Wed Apr 29 22:06:24 2020 +0200
+++ b/Samples/build-wasm-SingleFrameViewer.sh	Wed Apr 29 22:06:58 2020 +0200
@@ -33,7 +33,7 @@
 emcmake cmake -G "Ninja" \
   -DCMAKE_BUILD_TYPE=$buildType \
   -DCMAKE_INSTALL_PREFIX=$installFolderName \
-  -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DALLOW_DOWNLOADS=ON \
+  -DSTATIC_BUILD=ON -DALLOW_DOWNLOADS=ON \
   $samplesRootDir/WebAssembly/SingleFrameViewer
 
 # perform build + installation
--- a/UnitTestsSources/CMakeLists.txt	Wed Apr 29 22:06:24 2020 +0200
+++ b/UnitTestsSources/CMakeLists.txt	Wed Apr 29 22:06:58 2020 +0200
@@ -1,6 +1,10 @@
 cmake_minimum_required(VERSION 2.8.3)
 project(OrthancStone)
 
+set(ORTHANC_FRAMEWORK_SOURCE "path")
+set(ORTHANC_FRAMEWORK_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../orthanc)
+set(STONE_ROOT ${CMAKE_CURRENT_LIST_DIR}/../)
+
 include(../Resources/CMake/OrthancStoneParameters.cmake)
 
 set(ENABLE_STONE_DEPRECATED ON)  # Need deprecated classes for these samples
@@ -10,9 +14,8 @@
 endif()
 
 set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application")
-set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application")
 set(ENABLE_WASM OFF CACHE BOOL "Target WASM application")
-set(ENABLE_UNITTESTS ON 
+set(ENABLE_UNITTESTS ON BOOL "Enable unit tests")
 
 
 if (ENABLE_WASM)
@@ -20,15 +23,12 @@
   if (ENABLE_SDL)
     message("ENABLE_SDL is only supported in native (incompatible with ENABLE_WASM)")
   endif()
-  if (ENABLE_QT)
-    message("ENABLE_QT is only supported in native (incompatible with ENABLE_WASM)")
-  endif()
   set(ENABLE_NATIVE OFF)
   set(ORTHANC_SANDBOXED OFF)
   set(ENABLE_CRYPTO_OPTIONS ON)
   set(ENABLE_GOOGLE_TEST ON)
   set(ENABLE_WEB_CLIENT ON)
-elseif (ENABLE_QT OR ENABLE_SDL)
+elseif (ENABLE_SDL)
   set(ENABLE_NATIVE ON)
   set(ORTHANC_SANDBOXED OFF)
   set(ENABLE_CRYPTO_OPTIONS ON)
--- a/UnitTestsSources/GenericToolboxTests.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ b/UnitTestsSources/GenericToolboxTests.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -3846,7 +3846,7 @@
   }
 }
 
-static const size_t NUM_TIMINGS_CONVS = 2000;
+static const size_t NUM_TIMINGS_CONVS = 1; // set to 2000 if you want to measure perfs;
 
 
 //4444444444444444$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
--- a/UnitTestsSources/UnitTestsMain.cpp	Wed Apr 29 22:06:24 2020 +0200
+++ b/UnitTestsSources/UnitTestsMain.cpp	Wed Apr 29 22:06:58 2020 +0200
@@ -21,15 +21,10 @@
 
 #include "gtest/gtest.h"
 
-#include "../Framework/Deprecated/Layers/FrameRenderer.h"
-#include "../Framework/Deprecated/Toolbox/DownloadStack.h"
-#include "../Framework/Deprecated/Toolbox/MessagingToolbox.h"
-#include "../Framework/Deprecated/Toolbox/OrthancSlicesLoader.h"
 #include "../Framework/StoneInitialization.h"
 #include "../Framework/Toolbox/FiniteProjectiveCamera.h"
 #include "../Framework/Toolbox/GeometryToolbox.h"
 #include "../Framework/Volumes/ImageBuffer3D.h"
-#include "../Platforms/Generic/OracleWebService.h"
 
 #include <Core/HttpClient.h>
 #include <Core/Images/ImageProcessing.h>
@@ -633,14 +628,6 @@
   */
 }
 
-TEST(MessagingToolbox, ParseJson)
-{
-  Json::Value response;
-  std::string source = "{\"command\":\"panel:takeDarkImage\",\"commandType\":\"simple\",\"args\":{}}";
-  ASSERT_TRUE(Deprecated::MessagingToolbox::ParseJson(response, source.c_str(), source.size()));
-}
-
-
 
 static bool IsEqualVectorL1(OrthancStone::Vector a,
                             OrthancStone::Vector b)