changeset 1347:bfd77672d825 broker

Moved Application/Samples/* to Application/Samples/Deprecated/*
author Benjamin Golinvaux <bgo@osimis.io>
date Tue, 07 Apr 2020 14:29:01 +0200
parents df8bf351c23f
children eac254fb6791
files Applications/Samples/BasicPetCtFusionApplication.h Applications/Samples/CMakeLists.txt.old Applications/Samples/Deprecated/BasicPetCtFusionApplication.h 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/Samples/EmptyApplication.h Applications/Samples/LayoutPetCtFusionApplication.h Applications/Samples/Qt/SampleMainWindow.cpp Applications/Samples/Qt/SampleMainWindow.h Applications/Samples/Qt/SampleMainWindow.ui Applications/Samples/Qt/SampleMainWindowWithButtons.cpp Applications/Samples/Qt/SampleMainWindowWithButtons.h Applications/Samples/Qt/SampleMainWindowWithButtons.ui Applications/Samples/Qt/SampleQtApplicationRunner.h Applications/Samples/SampleApplicationBase.h Applications/Samples/SampleInteractor.h Applications/Samples/SampleList.h Applications/Samples/SampleMainNative.cpp Applications/Samples/SampleMainWasm.cpp Applications/Samples/Samples-status.md Applications/Samples/SimpleViewer/AppStatus.h Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp Applications/Samples/SimpleViewer/MainWidgetInteractor.h Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.cpp Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.h Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.ui Applications/Samples/SimpleViewer/Qt/mainQt.cpp Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp Applications/Samples/SimpleViewer/SimpleViewerApplication.h Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp Applications/Samples/SimpleViewer/ThumbnailInteractor.h Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h Applications/Samples/SimpleViewer/Wasm/mainWasm.cpp Applications/Samples/SimpleViewer/Wasm/simple-viewer.html Applications/Samples/SimpleViewer/Wasm/simple-viewer.ts Applications/Samples/SimpleViewer/Wasm/styles.css Applications/Samples/SimpleViewer/Wasm/tsconfig-simple-viewer.json Applications/Samples/SimpleViewerApplicationSingleFile.h Applications/Samples/SingleFrameApplication.h Applications/Samples/SingleFrameEditorApplication.h Applications/Samples/SingleVolumeApplication.h Applications/Samples/StoneSampleCommands.yml Applications/Samples/StoneSampleCommands_generate.py Applications/Samples/StoneSampleCommands_generated.hpp Applications/Samples/StoneSampleCommands_generated.ts Applications/Samples/SynchronizedSeriesApplication.h Applications/Samples/TestPatternApplication.h Applications/Samples/Web/index.html Applications/Samples/Web/samples-styles.css Applications/Samples/Web/simple-viewer-single-file.html Applications/Samples/Web/simple-viewer-single-file.ts Applications/Samples/Web/simple-viewer-single-file.tsconfig.json Applications/Samples/Web/single-frame-editor.html Applications/Samples/Web/single-frame-editor.ts Applications/Samples/Web/single-frame-editor.tsconfig.json Applications/Samples/Web/single-frame.html Applications/Samples/Web/single-frame.ts Applications/Samples/Web/single-frame.tsconfig.json Applications/Samples/Web/tsconfig-samples.json Applications/Samples/build-wasm.sh Applications/Samples/build-wasm.sh.old Applications/Samples/build-web-ext.sh Applications/Samples/build-web.sh Applications/Samples/get-requirements-windows.ps1 Applications/Samples/nginx.local.conf Applications/Samples/package-lock.json Applications/Samples/rt-viewer-demo/CMakeLists.txt Applications/Samples/rt-viewer-demo/build-sdl-msvc15.ps1 Applications/Samples/rt-viewer-demo/build-wasm.sh Applications/Samples/rt-viewer-demo/build-web.sh Applications/Samples/rt-viewer-demo/index.html Applications/Samples/rt-viewer-demo/main.cpp Applications/Samples/rt-viewer-demo/nginx.local.conf Applications/Samples/rt-viewer-demo/rt-viewer-demo.html Applications/Samples/rt-viewer-demo/rt-viewer-demo.ts Applications/Samples/rt-viewer-demo/rt-viewer-demo.tsconfig.json Applications/Samples/rt-viewer-demo/samples-styles.css Applications/Samples/rt-viewer-demo/start-serving-files.sh Applications/Samples/rt-viewer-demo/stop-serving-files.sh Applications/Samples/rt-viewer-demo/tsconfig-samples.json Applications/Samples/tsconfig-stone.json
diffstat 158 files changed, 7889 insertions(+), 7889 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Samples/BasicPetCtFusionApplication.h	Mon Apr 06 16:57:56 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/CMakeLists.txt.old	Mon Apr 06 16:57:56 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()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Deprecated/BasicPetCtFusionApplication.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/CMakeLists.txt.old	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/EmptyApplication.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/LayoutPetCtFusionApplication.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Qt/SampleMainWindow.cpp	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Qt/SampleMainWindow.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Qt/SampleMainWindow.ui	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.cpp	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Qt/SampleMainWindowWithButtons.ui	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Qt/SampleQtApplicationRunner.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SampleApplicationBase.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SampleInteractor.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SampleList.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SampleMainNative.cpp	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SampleMainWasm.cpp	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Samples-status.md	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/AppStatus.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.cpp	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/MainWidgetInteractor.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.cpp	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Qt/SimpleViewerMainWindow.ui	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Qt/mainQt.cpp	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.cpp	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/SimpleViewerApplication.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.cpp	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/ThumbnailInteractor.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Wasm/mainWasm.cpp	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.html	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Wasm/simple-viewer.ts	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Wasm/styles.css	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewer/Wasm/tsconfig-simple-viewer.json	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SimpleViewerApplicationSingleFile.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SingleFrameApplication.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SingleFrameEditorApplication.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SingleVolumeApplication.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/StoneSampleCommands.yml	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/StoneSampleCommands_generate.py	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/StoneSampleCommands_generated.hpp	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/StoneSampleCommands_generated.ts	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/SynchronizedSeriesApplication.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/TestPatternApplication.h	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/index.html	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/samples-styles.css	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/simple-viewer-single-file.html	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/simple-viewer-single-file.ts	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/simple-viewer-single-file.tsconfig.json	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/single-frame-editor.html	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/single-frame-editor.ts	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/single-frame-editor.tsconfig.json	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/single-frame.html	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/single-frame.ts	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/single-frame.tsconfig.json	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/Web/tsconfig-samples.json	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/build-wasm.sh	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/build-wasm.sh.old	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/build-web-ext.sh	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/build-web.sh	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/get-requirements-windows.ps1	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/nginx.local.conf	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/package-lock.json	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/CMakeLists.txt	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/build-sdl-msvc15.ps1	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/build-wasm.sh	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/build-web.sh	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/index.html	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/main.cpp	Tue Apr 07 14:29:01 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"); // IBA
+          //ct_->ScheduleLoadSeries(
+          //  "03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0"); // 0522c0001 TCIA
+        }
+
+        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");  // IBA 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"); // IBA
+          //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/Applications/Samples/Deprecated/rt-viewer-demo/nginx.local.conf	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.html	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.ts	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/rt-viewer-demo.tsconfig.json	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/samples-styles.css	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/start-serving-files.sh	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/rt-viewer-demo/stop-serving-files.sh	Tue Apr 07 14:29:01 2020 +0200
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+sudo nginx -s stop
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Deprecated/rt-viewer-demo/tsconfig-samples.json	Tue Apr 07 14:29:01 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/Applications/Samples/Deprecated/tsconfig-stone.json	Tue Apr 07 14:29:01 2020 +0200
@@ -0,0 +1,7 @@
+{
+    "include" : [
+        "../../Platforms/Wasm/stone-framework-loader.ts",
+        "../../Platforms/Wasm/wasm-application-runner.ts",
+        "../../Platforms/Wasm/wasm-viewport.ts"
+    ]
+}
--- a/Applications/Samples/EmptyApplication.h	Mon Apr 06 16:57:56 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/LayoutPetCtFusionApplication.h	Mon Apr 06 16:57:56 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/Qt/SampleMainWindow.cpp	Mon Apr 06 16:57:56 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/Qt/SampleMainWindow.h	Mon Apr 06 16:57:56 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/Qt/SampleMainWindow.ui	Mon Apr 06 16:57:56 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/Qt/SampleMainWindowWithButtons.cpp	Mon Apr 06 16:57:56 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/Qt/SampleMainWindowWithButtons.h	Mon Apr 06 16:57:56 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/Qt/SampleMainWindowWithButtons.ui	Mon Apr 06 16:57:56 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/Qt/SampleQtApplicationRunner.h	Mon Apr 06 16:57:56 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/SampleApplicationBase.h	Mon Apr 06 16:57:56 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/SampleInteractor.h	Mon Apr 06 16:57:56 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/SampleList.h	Mon Apr 06 16:57:56 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/SampleMainNative.cpp	Mon Apr 06 16:57:56 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/SampleMainWasm.cpp	Mon Apr 06 16:57:56 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/Samples-status.md	Mon Apr 06 16:57:56 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/SimpleViewer/AppStatus.h	Mon Apr 06 16:57:56 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/SimpleViewer/MainWidgetInteractor.cpp	Mon Apr 06 16:57:56 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/SimpleViewer/MainWidgetInteractor.h	Mon Apr 06 16:57:56 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/SimpleViewer/Qt/SimpleViewerMainWindow.cpp	Mon Apr 06 16:57:56 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/SimpleViewer/Qt/SimpleViewerMainWindow.h	Mon Apr 06 16:57:56 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/SimpleViewer/Qt/SimpleViewerMainWindow.ui	Mon Apr 06 16:57:56 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/SimpleViewer/Qt/mainQt.cpp	Mon Apr 06 16:57:56 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/SimpleViewer/SimpleViewerApplication.cpp	Mon Apr 06 16:57:56 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/SimpleViewer/SimpleViewerApplication.h	Mon Apr 06 16:57:56 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/SimpleViewer/ThumbnailInteractor.cpp	Mon Apr 06 16:57:56 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/SimpleViewer/ThumbnailInteractor.h	Mon Apr 06 16:57:56 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/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp	Mon Apr 06 16:57:56 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/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h	Mon Apr 06 16:57:56 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/SimpleViewer/Wasm/mainWasm.cpp	Mon Apr 06 16:57:56 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/SimpleViewer/Wasm/simple-viewer.html	Mon Apr 06 16:57:56 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/SimpleViewer/Wasm/simple-viewer.ts	Mon Apr 06 16:57:56 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/SimpleViewer/Wasm/styles.css	Mon Apr 06 16:57:56 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/SimpleViewer/Wasm/tsconfig-simple-viewer.json	Mon Apr 06 16:57:56 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/SimpleViewerApplicationSingleFile.h	Mon Apr 06 16:57:56 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/SingleFrameApplication.h	Mon Apr 06 16:57:56 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/SingleFrameEditorApplication.h	Mon Apr 06 16:57:56 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/SingleVolumeApplication.h	Mon Apr 06 16:57:56 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/StoneSampleCommands.yml	Mon Apr 06 16:57:56 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/StoneSampleCommands_generate.py	Mon Apr 06 16:57:56 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/StoneSampleCommands_generated.hpp	Mon Apr 06 16:57:56 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/StoneSampleCommands_generated.ts	Mon Apr 06 16:57:56 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/SynchronizedSeriesApplication.h	Mon Apr 06 16:57:56 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/TestPatternApplication.h	Mon Apr 06 16:57:56 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/Web/index.html	Mon Apr 06 16:57:56 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/Web/samples-styles.css	Mon Apr 06 16:57:56 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/Web/simple-viewer-single-file.html	Mon Apr 06 16:57:56 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/Web/simple-viewer-single-file.ts	Mon Apr 06 16:57:56 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/Web/simple-viewer-single-file.tsconfig.json	Mon Apr 06 16:57:56 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/Web/single-frame-editor.html	Mon Apr 06 16:57:56 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/Web/single-frame-editor.ts	Mon Apr 06 16:57:56 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/Web/single-frame-editor.tsconfig.json	Mon Apr 06 16:57:56 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/Web/single-frame.html	Mon Apr 06 16:57:56 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/Web/single-frame.ts	Mon Apr 06 16:57:56 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/Web/single-frame.tsconfig.json	Mon Apr 06 16:57:56 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/Web/tsconfig-samples.json	Mon Apr 06 16:57:56 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/build-wasm.sh	Mon Apr 06 16:57:56 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/build-wasm.sh.old	Mon Apr 06 16:57:56 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/build-web-ext.sh	Mon Apr 06 16:57:56 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/build-web.sh	Mon Apr 06 16:57:56 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/get-requirements-windows.ps1	Mon Apr 06 16:57:56 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/nginx.local.conf	Mon Apr 06 16:57:56 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/package-lock.json	Mon Apr 06 16:57:56 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/rt-viewer-demo/CMakeLists.txt	Mon Apr 06 16:57:56 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/rt-viewer-demo/build-sdl-msvc15.ps1	Mon Apr 06 16:57:56 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/rt-viewer-demo/build-wasm.sh	Mon Apr 06 16:57:56 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/rt-viewer-demo/build-web.sh	Mon Apr 06 16:57:56 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/rt-viewer-demo/index.html	Mon Apr 06 16:57:56 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/rt-viewer-demo/main.cpp	Mon Apr 06 16:57:56 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"); // IBA
-          //ct_->ScheduleLoadSeries(
-          //  "03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0"); // 0522c0001 TCIA
-        }
-
-        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");  // IBA 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"); // IBA
-          //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/rt-viewer-demo/nginx.local.conf	Mon Apr 06 16:57:56 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/rt-viewer-demo/rt-viewer-demo.html	Mon Apr 06 16:57:56 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/rt-viewer-demo/rt-viewer-demo.ts	Mon Apr 06 16:57:56 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/rt-viewer-demo/rt-viewer-demo.tsconfig.json	Mon Apr 06 16:57:56 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/rt-viewer-demo/samples-styles.css	Mon Apr 06 16:57:56 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/rt-viewer-demo/start-serving-files.sh	Mon Apr 06 16:57:56 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/rt-viewer-demo/stop-serving-files.sh	Mon Apr 06 16:57:56 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/rt-viewer-demo/tsconfig-samples.json	Mon Apr 06 16:57:56 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/tsconfig-stone.json	Mon Apr 06 16:57:56 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"
-    ]
-}