changeset 530:64782d018fc4 bgo-commands-codegen

Merged am-touch-events
author Benjamin Golinvaux <bgo@osimis.io>
date Sun, 17 Mar 2019 20:16:21 +0100
parents 7b29a8599318 (diff) 2549363fadc3 (current diff)
children 32dc5af8ab99
files
diffstat 22 files changed, 1314 insertions(+), 388 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Commands/BaseCommandBuilder.cpp	Tue Mar 12 17:29:12 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You 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 "BaseCommandBuilder.h"
-#include "Core/OrthancException.h"
-#include <iostream>
-#include "Framework/StoneException.h"
-
-namespace OrthancStone
-{
-  ICommand* BaseCommandBuilder::CreateFromJson(const Json::Value& commandJson)
-  {
-    if (!commandJson.isObject() || !commandJson["command"].isString())
-    {
-      throw StoneException(ErrorCode_CommandJsonInvalidFormat);
-    }
-
-    if (commandJson["commandType"].isString() && commandJson["commandType"].asString() == "generic-no-arg-command")
-    {
-      return new GenericNoArgCommand(commandJson["command"].asString().c_str());
-    }
-    else if (commandJson["commandType"].isString() && commandJson["commandType"].asString() == "generic-one-string-arg-command")
-    {
-      // TODO: we should create a command with a string arg !
-      return new GenericNoArgCommand(commandJson["command"].asString().c_str());
-    }
-
-    return NULL;
-  }
-
-}
--- a/Applications/Commands/BaseCommandBuilder.h	Tue Mar 12 17:29:12 2019 +0000
+++ /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-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You 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 <map>
-#include <memory>
-
-#include "ICommand.h"
-#include "../../Applications/Commands/ICommandBuilder.h"
-
-// TODO: must be reworked completely (check trello)
-
-namespace OrthancStone
-{
-  class BaseCommandBuilder : public ICommandBuilder
-  {
-  public:
-    virtual ICommand* CreateFromJson(const Json::Value& commandJson);
-  };
-}
--- a/Applications/Commands/ICommand.h	Tue Mar 12 17:29:12 2019 +0000
+++ /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-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You 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 <json/json.h>
-
-// TODO: must be reworked completely (check trello)
-
-namespace OrthancStone
-{
-  class ICommand  // TODO noncopyable
-  {
-  protected:
-    std::string name_;
-    ICommand(const std::string& name)
-      : name_(name)
-    {}
-  public:
-    virtual ~ICommand() 
-    {}
-    virtual void Execute() = 0;
-//    virtual void Configure(const Json::Value& arguments) = 0;
-    const std::string& GetName() const
-    {
-      return name_;
-    }
-  };
-
-
-  template <typename TCommand>
-  class BaseCommand : public ICommand
-  {
-  protected:
-    BaseCommand(const std::string& name)
-      : ICommand(name)
-    {}
-
-  public:
-    static ICommand* Create() {
-      return new TCommand();
-    }
-
-    virtual void Configure(const Json::Value& arguments) {
-    }
-  };
-
-  class NoopCommand : public BaseCommand<NoopCommand>
-  {
-  public:
-    NoopCommand()
-      : BaseCommand("noop")
-    {}
-    virtual void Execute() {}
-  };
-
-  class GenericNoArgCommand : public BaseCommand<GenericNoArgCommand>
-  {
-  public:
-    GenericNoArgCommand(const std::string& name)
-      : BaseCommand(name)
-    {}
-    virtual void Execute() {} // TODO currently not used but this is not nice at all !
-  };
-
-  class GenericOneStringArgCommand : public BaseCommand<GenericOneStringArgCommand>
-  {
-    std::string argument_;
-  public:
-    GenericOneStringArgCommand(const std::string& name, const std::string& argument)
-      : BaseCommand(name)
-    {}
-
-    const std::string& GetArgument() const {return argument_;}
-    virtual void Execute() {} // TODO currently not used but this is not nice at all !
-  };
-
-}
--- a/Applications/Commands/ICommandBuilder.h	Tue Mar 12 17:29:12 2019 +0000
+++ /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-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include <boost/noncopyable.hpp>
-#include <json/json.h>
-
-#include "ICommand.h"
-
-namespace OrthancStone
-{
-
-  class ICommandBuilder : public boost::noncopyable
-  {
-  public:
-    virtual ICommand* CreateFromJson(const Json::Value& commandJson) = 0;
-  };
-}
--- a/Applications/Commands/ICommandExecutor.h	Tue Mar 12 17:29:12 2019 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include <boost/noncopyable.hpp>
-
-namespace OrthancStone
-{
-  class ICommandExecutor : public boost::noncopyable
-  {
-
-  };
-}
--- a/Applications/IStoneApplication.h	Tue Mar 12 17:29:12 2019 +0000
+++ b/Applications/IStoneApplication.h	Sun Mar 17 20:16:21 2019 +0100
@@ -25,9 +25,6 @@
 #include <boost/program_options.hpp>
 #include "../Framework/Viewport/WidgetViewport.h"
 #include "json/json.h"
-#include "Commands/ICommand.h"
-#include "Commands/BaseCommandBuilder.h"
-
 
 namespace OrthancStone
 {
@@ -61,16 +58,6 @@
 
     virtual std::string GetTitle() const = 0;
     virtual IWidget* GetCentralWidget() = 0;
-
     virtual void Finalize() = 0;
-
-    virtual BaseCommandBuilder& GetCommandBuilder() = 0;
-
-    virtual void HandleSerializedMessage(const char* data) {};
-    
-    virtual void ExecuteCommand(ICommand& command)
-    {
-    }
   };
-
 }
--- a/Applications/Samples/CMakeLists.txt	Tue Mar 12 17:29:12 2019 +0000
+++ b/Applications/Samples/CMakeLists.txt	Sun Mar 17 20:16:21 2019 +0100
@@ -8,6 +8,14 @@
 
 include(../../Resources/CMake/OrthancStoneParameters.cmake)
 
+if (OPENSSL_NO_CAPIENG)
+add_definitions(-DOPENSSL_NO_CAPIENG=1)
+endif()
+
+if (BGO_USE_NEW_COMMANDS)
+add_definitions(-DBGO_USE_NEW_COMMANDS=1)
+endif()
+
 #set(ENABLE_DCMTK ON)
 
 set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application")
@@ -38,7 +46,7 @@
   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}/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")
@@ -181,16 +189,20 @@
         list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES
             ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/mainWasm.cpp
             ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp
+            ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h
             ${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/AppStatus.h
       ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/AppStatus.h
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.h
       ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Messages.h
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.h
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.h
       ${SIMPLE_VIEWER_APPLICATION_SOURCES}
       )
     target_link_libraries(OrthancStoneSimpleViewer OrthancStone)
--- a/Applications/Samples/SampleApplicationBase.h	Tue Mar 12 17:29:12 2019 +0000
+++ b/Applications/Samples/SampleApplicationBase.h	Sun Mar 17 20:16:21 2019 +0100
@@ -41,7 +41,6 @@
     class SampleApplicationBase : public IStoneApplication
     {
     protected:
-      BaseCommandBuilder commandBuilder_;
       WorldSceneWidget*  mainWidget_;   // ownership is transfered to the application context
 
     public:
@@ -56,8 +55,6 @@
         return "Stone of Orthanc - Sample";
       }
 
-      virtual BaseCommandBuilder& GetCommandBuilder() {return commandBuilder_;}
-
       virtual void Finalize() {}
       virtual IWidget* GetCentralWidget() {return mainWidget_;}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Samples-status.md	Sun Mar 17 20:16:21 2019 +0100
@@ -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
--- a/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp	Tue Mar 12 17:29:12 2019 +0000
+++ b/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp	Sun Mar 17 20:16:21 2019 +0100
@@ -37,33 +37,32 @@
   {
     if (button == MouseButton_Left)
     {
-      if (application_.GetCurrentTool() == SimpleViewerApplication::Tools_LineMeasure)
+      if (application_.GetCurrentTool() == Tool_LineMeasure)
       {
         return new LineMeasureTracker(statusBar, dynamic_cast<SliceViewerWidget&>(widget).GetSlice(),
                                       x, y, 255, 0, 0, application_.GetFont());
       }
-      else if (application_.GetCurrentTool() == SimpleViewerApplication::Tools_CircleMeasure)
+      else if (application_.GetCurrentTool() == Tool_CircleMeasure)
       {
         return new CircleMeasureTracker(statusBar, dynamic_cast<SliceViewerWidget&>(widget).GetSlice(),
                                         x, y, 255, 0, 0, application_.GetFont());
       }
-      else if (application_.GetCurrentTool() == SimpleViewerApplication::Tools_Crop)
+      else if (application_.GetCurrentTool() == Tool_Crop)
       {
         // TODO
       }
-      else if (application_.GetCurrentTool() == SimpleViewerApplication::Tools_Windowing)
+      else if (application_.GetCurrentTool() == Tool_Windowing)
       {
         // TODO
       }
-      else if (application_.GetCurrentTool() == SimpleViewerApplication::Tools_Zoom)
+      else if (application_.GetCurrentTool() == Tool_Zoom)
       {
         // TODO
       }
-      else if (application_.GetCurrentTool() == SimpleViewerApplication::Tools_Pan)
+      else if (application_.GetCurrentTool() == Tool_Pan)
       {
         // TODO
       }
-
     }
     return NULL;
   }
--- a/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.cpp	Tue Mar 12 17:29:12 2019 +0000
+++ b/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.cpp	Sun Mar 17 20:16:21 2019 +0100
@@ -27,8 +27,15 @@
 #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,
@@ -67,43 +74,36 @@
 
   void SimpleViewerMainWindow::cropClicked()
   {
-    GenericNoArgCommand command("selectTool:crop");
-    stoneApplication_.ExecuteCommand(command);
+    stoneApplication_.ExecuteCommand(SelectTool(Tool_Crop));
   }
 
   void SimpleViewerMainWindow::undoCropClicked()
   {
-    GenericNoArgCommand command("action:undo-crop");
-    stoneApplication_.ExecuteCommand(command);
+    stoneApplication_.ExecuteCommand(Action(ActionType_UndoCrop));
   }
 
   void SimpleViewerMainWindow::lineClicked()
   {
-    GenericNoArgCommand command("selectTool:line-measure");
-    stoneApplication_.ExecuteCommand(command);
+    stoneApplication_.ExecuteCommand(SelectTool(Tool_LineMeasure));
   }
 
   void SimpleViewerMainWindow::circleClicked()
   {
-    GenericNoArgCommand command("selectTool:circle-measure");
-    stoneApplication_.ExecuteCommand(command);
+    stoneApplication_.ExecuteCommand(SelectTool(Tool_CircleMeasure));
   }
 
   void SimpleViewerMainWindow::windowingClicked()
   {
-    GenericNoArgCommand command("selectTool:windowing");
-    stoneApplication_.ExecuteCommand(command);
+    stoneApplication_.ExecuteCommand(SelectTool(Tool_Windowing));
   }
 
   void SimpleViewerMainWindow::rotateClicked()
   {
-    GenericNoArgCommand command("action:rotate");
-    stoneApplication_.ExecuteCommand(command);
+    stoneApplication_.ExecuteCommand(Action(ActionType_Rotate));
   }
 
   void SimpleViewerMainWindow::invertClicked()
   {
-    GenericNoArgCommand command("action:invert");
-    stoneApplication_.ExecuteCommand(command);
+    stoneApplication_.ExecuteCommand(Action(ActionType_Invert));
   }
 }
--- a/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp	Tue Mar 12 17:29:12 2019 +0000
+++ b/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp	Sun Mar 17 20:16:21 2019 +0100
@@ -153,13 +153,16 @@
   void SimpleViewerApplication::LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId)
   {
     LOG(INFO) << "Loading thumbnail for series " << seriesId;
+    
     SliceViewerWidget* thumbnailWidget = 
       new SliceViewerWidget(IObserver::GetBroker(), "thumbnail-series-" + seriesId);
     thumbnails_.push_back(thumbnailWidget);
     thumbnailsLayout_->AddWidget(thumbnailWidget);
+    
     thumbnailWidget->RegisterObserverCallback(
       new Callable<SimpleViewerApplication, SliceViewerWidget::GeometryChangedMessage>
       (*this, &SimpleViewerApplication::OnWidgetGeometryChanged));
+    
     smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0);
     thumbnailWidget->SetInteractor(*thumbnailInteractor_);
   }
@@ -180,53 +183,29 @@
     smartLoader_->SetFrameInWidget(*mainWidget_, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
   }
 
-
-
-  void SimpleViewerApplication::ExecuteCommand(ICommand& command)
+  bool SimpleViewerApplication::Handle(const StoneSampleCommands::SelectTool& value) ORTHANC_OVERRIDE
   {
-    statusBar_->SetMessage("received command: " + std::string(command.GetName()));
-    if (command.GetName() == "selectTool:circle-measure")
-    {
-      SelectTool(Tools_CircleMeasure);
-    }
-    else if (command.GetName() == "selectTool:line-measure")
-    {
-      SelectTool(Tools_LineMeasure);
-    }
-    else if (command.GetName() == "selectTool:crop")
-    {
-      SelectTool(Tools_Crop);
-    }
-    else if (command.GetName() == "selectTool:windowing")
-    {
-      SelectTool(Tools_Windowing);
-    }
-    else if (command.GetName() == "action:rotate")
-    {
-      ExecuteAction(Actions_Rotate);
-    }
-    else if (command.GetName() == "action:undo-crop")
-    {
-      ExecuteAction(Actions_UndoCrop);
-    }
-    else if (command.GetName() == "action:invert")
-    {
-      ExecuteAction(Actions_Invert);
-    }
-    else
-    {
-      command.Execute();
-    }
+    currentTool_ = value.tool;
+    return true;
   }
 
-  void SimpleViewerApplication::ExecuteAction(SimpleViewerApplication::Actions action)
+  bool SimpleViewerApplication::Handle(const StoneSampleCommands::Action& value) ORTHANC_OVERRIDE
   {
-    // TODO
-  }
-
-  void SimpleViewerApplication::SelectTool(SimpleViewerApplication::Tools tool)
-  {
-    currentTool_ = tool;
+    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
--- a/Applications/Samples/SimpleViewer/SimpleViewerApplication.h	Tue Mar 12 17:29:12 2019 +0000
+++ b/Applications/Samples/SimpleViewer/SimpleViewerApplication.h	Sun Mar 17 20:16:21 2019 +0100
@@ -21,6 +21,12 @@
 
 #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/Layers/CircleMeasureTracker.h"
@@ -53,10 +59,11 @@
 namespace SimpleViewer
 {
 
-  class SimpleViewerApplication :
-      public IStoneApplication,
-      public IObserver,
-      public IObservable
+  class SimpleViewerApplication
+    : public IStoneApplication
+    , public IObserver
+    , public IObservable
+    , public StoneSampleCommands::IHandler
   {
   public:
 
@@ -71,23 +78,9 @@
       }
     };
 
-    enum Tools {
-      Tools_LineMeasure,
-      Tools_CircleMeasure,
-      Tools_Crop,
-      Tools_Windowing,
-      Tools_Zoom,
-      Tools_Pan
-    };
+  private:
+    Tool                                currentTool_;
 
-    enum Actions {
-      Actions_Rotate,
-      Actions_Invert,
-      Actions_UndoCrop
-    };
-
-  private:
-    Tools                               currentTool_;
     std::auto_ptr<MainWidgetInteractor> mainWidgetInteractor_;
     std::auto_ptr<ThumbnailInteractor>  thumbnailInteractor_;
     LayoutWidget*                       mainLayout_;
@@ -96,8 +89,6 @@
     std::vector<SliceViewerWidget*>     thumbnails_;
     std::map<std::string, std::vector<std::string> > instancesIdsPerSeriesId_;
     std::map<std::string, Json::Value>  seriesTags_;
-    BaseCommandBuilder                  commandBuilder_;
-
     unsigned int                        currentInstanceIndex_;
     OrthancStone::WidgetViewport*       wasmViewport1_;
     OrthancStone::WidgetViewport*       wasmViewport2_;
@@ -111,7 +102,7 @@
     SimpleViewerApplication(MessageBroker& broker) :
       IObserver(broker),
       IObservable(broker),
-      currentTool_(Tools_LineMeasure),
+      currentTool_(StoneSampleCommands::Tool_LineMeasure),
       mainLayout_(NULL),
       currentInstanceIndex_(0),
       wasmViewport1_(NULL),
@@ -142,9 +133,8 @@
 
     void SelectSeriesInMainViewport(const std::string& seriesId);
 
-    void SelectTool(Tools tool);
-    
-    Tools GetCurrentTool() const
+
+    Tool GetCurrentTool() const
     {
       return currentTool_;
     }
@@ -154,12 +144,18 @@
       return font_;
     }
 
-    void ExecuteAction(Actions action);
+    // 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 std::string GetTitle() const {return "SimpleViewer";}
-    virtual void ExecuteCommand(ICommand& command);
-    virtual BaseCommandBuilder& GetCommandBuilder() {return commandBuilder_;}
-
 
 #if ORTHANC_ENABLE_WASM==1
     virtual void InitializeWasm();
--- a/Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp	Tue Mar 12 17:29:12 2019 +0000
+++ b/Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp	Sun Mar 17 20:16:21 2019 +0100
@@ -45,7 +45,7 @@
 
     writer->write(event, &outputStr);
 
-    NotifyStatusUpdateFromCppToWeb(outputStr.str());
+    NotifyStatusUpdateFromCppToWebWithString(outputStr.str());
   }
 
 } // namespace SimpleViewer
\ No newline at end of file
--- a/Applications/Samples/SimpleViewerApplicationSingleFile.h	Tue Mar 12 17:29:12 2019 +0000
+++ b/Applications/Samples/SimpleViewerApplicationSingleFile.h	Sun Mar 17 20:16:21 2019 +0100
@@ -127,12 +127,12 @@
         {
           if (button == MouseButton_Left)
           {
-            if (application_.currentTool_ == Tools_LineMeasure)
+            if (application_.currentTool_ == Tool_LineMeasure)
             {
               return new LineMeasureTracker(statusBar, dynamic_cast<SliceViewerWidget&>(widget).GetSlice(),
                                             x, y, 255, 0, 0, application_.GetFont());
             }
-            else if (application_.currentTool_ == Tools_CircleMeasure)
+            else if (application_.currentTool_ == Tool_CircleMeasure)
             {
               return new CircleMeasureTracker(statusBar, dynamic_cast<SliceViewerWidget&>(widget).GetSlice(),
                                               x, y, 255, 0, 0, application_.GetFont());
@@ -179,11 +179,11 @@
               break;
 
             case 'l':
-              application_.currentTool_ = Tools_LineMeasure;
+              application_.currentTool_ = Tool_LineMeasure;
               break;
 
             case 'c':
-              application_.currentTool_ = Tools_CircleMeasure;
+              application_.currentTool_ = Tool_CircleMeasure;
               break;
 
             default:
@@ -214,13 +214,13 @@
         {
           if (input == "select-tool:line-measure")
           {
-            viewerApplication_.currentTool_ = Tools_LineMeasure;
-            NotifyStatusUpdateFromCppToWeb("currentTool=line-measure");
+            viewerApplication_.currentTool_ = Tool_LineMeasure;
+            NotifyStatusUpdateFromCppToWebWithString("currentTool=line-measure");
           }
           else if (input == "select-tool:circle-measure")
           {
-            viewerApplication_.currentTool_ = Tools_CircleMeasure;
-            NotifyStatusUpdateFromCppToWeb("currentTool=circle-measure");
+            viewerApplication_.currentTool_ = Tool_CircleMeasure;
+            NotifyStatusUpdateFromCppToWebWithString("currentTool=circle-measure");
           }
 
           output = "ok";
@@ -231,19 +231,19 @@
           UpdateStoneApplicationStatusFromCppWithSerializedMessage(statusUpdateMessage.c_str());
         }
 
-        virtual void NotifyStatusUpdateFromCppToWeb(const std::string& statusUpdateMessage) 
+        virtual void NotifyStatusUpdateFromCppToWebWithString(const std::string& statusUpdateMessage) 
         {
           UpdateStoneApplicationStatusFromCppWithString(statusUpdateMessage.c_str());
         }
 
       };
 #endif
-      enum Tools {
-        Tools_LineMeasure,
-        Tools_CircleMeasure
+      enum Tool {
+        Tool_LineMeasure,
+        Tool_CircleMeasure
       };
 
-      Tools                                currentTool_;
+      Tool                                 currentTool_;
       std::auto_ptr<MainWidgetInteractor>  mainWidgetInteractor_;
       std::auto_ptr<ThumbnailInteractor>   thumbnailInteractor_;
       LayoutWidget*                        mainLayout_;
@@ -265,7 +265,7 @@
     public:
       SimpleViewerApplication(MessageBroker& broker) :
         IObserver(broker),
-        currentTool_(Tools_LineMeasure),
+        currentTool_(Tool_LineMeasure),
         mainLayout_(NULL),
         currentInstanceIndex_(0),
         wasmViewport1_(NULL),
@@ -438,8 +438,8 @@
       
       virtual void OnPushButton1Clicked() {}
       virtual void OnPushButton2Clicked() {}
-      virtual void OnTool1Clicked() { currentTool_ = Tools_LineMeasure;}
-      virtual void OnTool2Clicked() { currentTool_ = Tools_CircleMeasure;}
+      virtual void OnTool1Clicked() { currentTool_ = Tool_LineMeasure;}
+      virtual void OnTool2Clicked() { currentTool_ = Tool_CircleMeasure;}
 
       virtual void GetButtonNames(std::string& pushButton1,
                                   std::string& pushButton2,
--- a/Applications/Samples/SingleFrameApplication.h	Tue Mar 12 17:29:12 2019 +0000
+++ b/Applications/Samples/SingleFrameApplication.h	Sun Mar 17 20:16:21 2019 +0100
@@ -138,7 +138,7 @@
 
           if (slice >= static_cast<int>(source_->GetSliceCount()))
           {
-            slice = source_->GetSliceCount() - 1;
+            slice = static_cast<int>(source_->GetSliceCount()) - 1;
           }
 
           if (slice != static_cast<int>(slice_)) 
@@ -160,7 +160,7 @@
         if (source_ != NULL &&
             index < source_->GetSliceCount())
         {
-          slice_ = index;
+          slice_ = static_cast<unsigned int>(index);
           
 #if 1
           GetMainWidget().SetSlice(source_->GetSlice(slice_).GetGeometry());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/StoneSampleCommands.yml	Sun Mar 17 20:16:21 2019 +0100
@@ -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/StoneSampleCommands_generate.py	Sun Mar 17 20:16:21 2019 +0100
@@ -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/StoneSampleCommands_generated.hpp	Sun Mar 17 20:16:21 2019 +0100
@@ -0,0 +1,696 @@
+/*
+         1         2         3         4         5         6         7
+12345678901234567890123456789012345678901234567890123456789012345678901234567890
+
+Generated on 2019-03-15 10:00:48.763392 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::auto_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(int 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, int indent)
+  {
+    out << MakeIndent(indent) << value;
+    return out;
+  }
+
+  // string dumper
+  inline std::ostream& StoneDumpValue(std::ostream& out, const std::string& value, int 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, int 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, int 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, int 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, int 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, int 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, int 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;
+  };
+
+  /** 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
+    {
+      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/StoneSampleCommands_generated.ts	Sun Mar 17 20:16:21 2019 +0100
@@ -0,0 +1,333 @@
+/*
+         1         2         3         4         5         6         7
+12345678901234567890123456789012345678901234567890123456789012345678901234567890
+
+Generated on 2019-03-15 10:00:48.763392 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/Framework/Widgets/LayoutWidget.cpp	Tue Mar 12 17:29:12 2019 +0000
+++ b/Framework/Widgets/LayoutWidget.cpp	Sun Mar 17 20:16:21 2019 +0100
@@ -74,7 +74,7 @@
       std::vector<Touch> relativeTouches;
       for (size_t t = 0; t < displayTouches.size(); t++)
       {
-        relativeTouches.push_back(Touch((int)displayTouches[t].x - left_, (int)displayTouches[t].y - top_));
+        relativeTouches.push_back(Touch(displayTouches[t].x - left_, displayTouches[t].y - top_));
       }
 
       tracker_->MouseMove(x - left_, y - top_, relativeTouches);
@@ -226,7 +226,7 @@
     }
     else if (isHorizontal_)
     {
-      unsigned int padding = paddingLeft_ + paddingRight_ + (children_.size() - 1) * paddingInternal_;
+      unsigned int padding = paddingLeft_ + paddingRight_ + (static_cast<unsigned int>(children_.size()) - 1) * paddingInternal_;
       float childWidth = ((static_cast<float>(width_) - static_cast<float>(padding)) / 
                           static_cast<float>(children_.size()));
         
@@ -250,7 +250,7 @@
     }
     else
     {
-      unsigned int padding = paddingTop_ + paddingBottom_ + (children_.size() - 1) * paddingInternal_;
+      unsigned int padding = paddingTop_ + paddingBottom_ + (static_cast<unsigned int>(children_.size()) - 1) * paddingInternal_;
       float childHeight = ((static_cast<float>(height_) - static_cast<float>(padding)) / 
                            static_cast<float>(children_.size()));
         
--- a/README.md	Tue Mar 12 17:29:12 2019 +0000
+++ b/README.md	Sun Mar 17 20:16:21 2019 +0100
@@ -125,7 +125,10 @@
 source code in an `orthanc` folder next to the Stone of Orthanc
 repository, please enter the following:
 
+**Simple make generator with dynamic build**
+
 ```
+# Please set $currentDir to the current folder
 mkdir -p ~/builds/orthanc-stone-build
 cd ~/builds/orthanc-stone-build
 cmake -DORTHANC_FRAMEWORK_SOURCE=path \
@@ -134,6 +137,16 @@
     ~/orthanc-stone/Applications/Samples/
 ```
 
+**Ninja generator with static build (typical under Windows)**
+
+```
+# Please yourself one level above the orthanc-stone and orthanc folders
+mkdir -p stone_build_sdl
+cd stone_build_sdl
+cmake -G Ninja -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples/
+```
+
+
 If you are working on Windows, add the correct generator option to
 cmake to, for instance, generate msbuild files for Visual Studio.
 
@@ -146,9 +159,14 @@
 
 Building the Qt native samples (SimpleViewer only) under Windows:
 ------------------------------------------------------------------
+
+**MSVC 2017 generator with static build (typical under Windows)**
+
 For instance, if Qt is installed in `C:\Qt\5.12.0\msvc2017_64`
 
-`cmake -DSTATIC_BUILD=ON -DCMAKE_PREFIX_PATH=C:\Qt\5.12.0\msvc2017_64 -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_QT=ON -G "Visual Studio 15 2017 Win64" ../orthanc-stone/Applications/Samples/`
+```
+cmake -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DCMAKE_PREFIX_PATH=C:\Qt\5.12.0\msvc2017_64 -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_QT=ON -G "Visual Studio 15 2017 Win64" ../orthanc-stone/Applications/Samples/
+```
 
 Note: replace `$($pwd)` with the current directory when not using Powershell
 
@@ -196,3 +214,9 @@
   url="https://doi.org/10.1007/s10278-018-0082-y"
 }
 
+Various notes to be deleted
+---------------------------
+class BaseCommand : public ICommand
+
+RadiographySceneCommand
+GenericNoArgCommand