# HG changeset patch # User Alain Mazy # Date 1562665603 -7200 # Node ID 31319fe867b9d764e9db15358f780647ea4fe833 # Parent a8cd3755db21cd943c5613ea378b5f65a88cb935# Parent 80829436ce0cee3eebbf56312a5a7b4da8aa93a6 Merge diff -r a8cd3755db21 -r 31319fe867b9 Framework/Scene2D/CairoCompositor.h --- a/Framework/Scene2D/CairoCompositor.h Tue Jul 09 11:41:16 2019 +0200 +++ b/Framework/Scene2D/CairoCompositor.h Tue Jul 09 11:46:43 2019 +0200 @@ -29,6 +29,7 @@ namespace OrthancStone { class CairoCompositor : + public ICompositor, private Internals::CompositorHelper::IRendererFactory, private Internals::ICairoContextProvider { @@ -44,16 +45,6 @@ virtual cairo_t* GetCairoContext(); - virtual unsigned int GetCairoWidth() - { - return canvas_.GetWidth(); - } - - virtual unsigned int GetCairoHeight() - { - return canvas_.GetHeight(); - } - virtual Internals::CompositorHelper::ILayerRenderer* Create(const ISceneLayer& layer); public: @@ -63,6 +54,16 @@ ~CairoCompositor(); + virtual unsigned int GetWidth() const + { + return canvas_.GetWidth(); + } + + virtual unsigned int GetHeight() const + { + return canvas_.GetHeight(); + } + const CairoSurface& GetCanvas() const { return canvas_; @@ -78,7 +79,7 @@ Orthanc::Encoding codepage); #endif - void Refresh(); + virtual void Refresh(); Orthanc::ImageAccessor* RenderText(size_t fontIndex, const std::string& utf8) const; diff -r a8cd3755db21 -r 31319fe867b9 Framework/Scene2D/Internals/CairoInfoPanelRenderer.cpp --- a/Framework/Scene2D/Internals/CairoInfoPanelRenderer.cpp Tue Jul 09 11:41:16 2019 +0200 +++ b/Framework/Scene2D/Internals/CairoInfoPanelRenderer.cpp Tue Jul 09 11:46:43 2019 +0200 @@ -42,7 +42,7 @@ int dx, dy; InfoPanelSceneLayer::ComputeAnchorLocation( dx, dy, anchor_, texture_.GetWidth(), texture_.GetHeight(), - target_.GetCairoWidth(), target_.GetCairoHeight()); + target_.GetWidth(), target_.GetHeight()); cairo_t* cr = target_.GetCairoContext(); diff -r a8cd3755db21 -r 31319fe867b9 Framework/Scene2D/Internals/CompositorHelper.h --- a/Framework/Scene2D/Internals/CompositorHelper.h Tue Jul 09 11:41:16 2019 +0200 +++ b/Framework/Scene2D/Internals/CompositorHelper.h Tue Jul 09 11:46:43 2019 +0200 @@ -22,13 +22,29 @@ #pragma once #include "../Scene2D.h" - +#include "../ScenePoint2D.h" #include #include namespace OrthancStone { + class ICompositor + { + public: + virtual unsigned int GetWidth() const = 0; + virtual unsigned int GetHeight() const = 0; + virtual void Refresh() = 0; + + ScenePoint2D GetPixelCenterCoordinates(int x, int y) const + { + return ScenePoint2D( + static_cast(x) + 0.5 - static_cast(GetWidth()) / 2.0, + static_cast(y) + 0.5 - static_cast(GetHeight()) / 2.0); + } + + }; + namespace Internals { class CompositorHelper : protected Scene2D::IVisitor diff -r a8cd3755db21 -r 31319fe867b9 Framework/Scene2D/Internals/ICairoContextProvider.h --- a/Framework/Scene2D/Internals/ICairoContextProvider.h Tue Jul 09 11:41:16 2019 +0200 +++ b/Framework/Scene2D/Internals/ICairoContextProvider.h Tue Jul 09 11:46:43 2019 +0200 @@ -38,9 +38,9 @@ virtual cairo_t* GetCairoContext() = 0; - virtual unsigned int GetCairoWidth() = 0; + virtual unsigned int GetWidth() const = 0; - virtual unsigned int GetCairoHeight() = 0; + virtual unsigned int GetHeight() const = 0; }; } } diff -r a8cd3755db21 -r 31319fe867b9 Framework/Scene2D/OpenGLCompositor.cpp --- a/Framework/Scene2D/OpenGLCompositor.cpp Tue Jul 09 11:41:16 2019 +0200 +++ b/Framework/Scene2D/OpenGLCompositor.cpp Tue Jul 09 11:46:43 2019 +0200 @@ -207,12 +207,4 @@ SetFont(index, dict); } #endif - - - ScenePoint2D OpenGLCompositor::GetPixelCenterCoordinates(int x, int y) const - { - return ScenePoint2D( - static_cast(x) + 0.5 - static_cast(canvasWidth_) / 2.0, - static_cast(y) + 0.5 - static_cast(canvasHeight_) / 2.0); - } } diff -r a8cd3755db21 -r 31319fe867b9 Framework/Scene2D/OpenGLCompositor.h --- a/Framework/Scene2D/OpenGLCompositor.h Tue Jul 09 11:41:16 2019 +0200 +++ b/Framework/Scene2D/OpenGLCompositor.h Tue Jul 09 11:46:43 2019 +0200 @@ -29,7 +29,9 @@ namespace OrthancStone { - class OpenGLCompositor : private Internals::CompositorHelper::IRendererFactory + class OpenGLCompositor : + public ICompositor, + private Internals::CompositorHelper::IRendererFactory { private: class Font; @@ -58,7 +60,7 @@ void UpdateSize(); - void Refresh(); + virtual void Refresh(); void SetFont(size_t index, const GlyphBitmapAlphabet& dict); @@ -70,16 +72,15 @@ Orthanc::Encoding codepage); #endif - unsigned int GetCanvasWidth() const + virtual unsigned int GetWidth() const { return canvasWidth_; } - unsigned int GetCanvasHeight() const + unsigned int GetHeight() const { return canvasHeight_; } - ScenePoint2D GetPixelCenterCoordinates(int x, int y) const; }; } diff -r a8cd3755db21 -r 31319fe867b9 Samples/Sdl/BasicScene.cpp --- a/Samples/Sdl/BasicScene.cpp Tue Jul 09 11:41:16 2019 +0200 +++ b/Samples/Sdl/BasicScene.cpp Tue Jul 09 11:46:43 2019 +0200 @@ -225,7 +225,7 @@ case SDL_BUTTON_RIGHT: activeTracker = boost::make_shared(controller, - e, compositor.GetCanvasHeight()); + e, compositor.GetHeight()); break; case SDL_BUTTON_LEFT: @@ -242,14 +242,14 @@ switch (event.key.keysym.sym) { case SDLK_s: - controller->FitContent(compositor.GetCanvasWidth(), - compositor.GetCanvasHeight()); + controller->FitContent(compositor.GetWidth(), + compositor.GetHeight()); break; case SDLK_c: TakeScreenshot("screenshot.png", scene, - compositor.GetCanvasWidth(), - compositor.GetCanvasHeight()); + compositor.GetWidth(), + compositor.GetHeight()); break; default: diff -r a8cd3755db21 -r 31319fe867b9 Samples/Sdl/CMakeLists.txt --- a/Samples/Sdl/CMakeLists.txt Tue Jul 09 11:41:16 2019 +0200 +++ b/Samples/Sdl/CMakeLists.txt Tue Jul 09 11:46:43 2019 +0200 @@ -71,7 +71,7 @@ target_link_libraries(BasicScene OrthancStone) # -# BasicScene +# TrackerSample # LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSample.cpp") @@ -108,3 +108,21 @@ ) target_link_libraries(FusionMprSdl OrthancStone) + +# +# RadiographyEditor +# + +LIST(APPEND RADIOGRAPHY_EDITOR_SOURCE "../Shared/RadiographyEditorApp.cpp") +LIST(APPEND RADIOGRAPHY_EDITOR_SOURCE "../Shared/RadiographyEditorApp.h") +LIST(APPEND RADIOGRAPHY_EDITOR_SOURCE "RadiographyEditor.cpp") + +if (MSVC AND MSVC_VERSION GREATER 1700) + LIST(APPEND RADIOGRAPHY_EDITOR_SOURCE "cpp.hint") +endif() + +add_executable(RadiographyEditor + ${RADIOGRAPHY_EDITOR_SOURCE} + ) + +target_link_libraries(RadiographyEditor OrthancStone) diff -r a8cd3755db21 -r 31319fe867b9 Samples/Sdl/FusionMprSdl.cpp --- a/Samples/Sdl/FusionMprSdl.cpp Tue Jul 09 11:41:16 2019 +0200 +++ b/Samples/Sdl/FusionMprSdl.cpp Tue Jul 09 11:46:43 2019 +0200 @@ -135,8 +135,8 @@ } // position the fixed info text in the upper right corner layerP->SetText(msgS.c_str()); - double cX = compositor_->GetCanvasWidth() * (-0.5); - double cY = compositor_->GetCanvasHeight() * (-0.5); + double cX = compositor_->GetWidth() * (-0.5); + double cY = compositor_->GetHeight() * (-0.5); GetScene()->GetCanvasToSceneTransform().Apply(cX,cY); layerP->SetPosition(cX, cY); } @@ -270,8 +270,8 @@ } break; case SDLK_s: - controller_->FitContent(compositor_->GetCanvasWidth(), - compositor_->GetCanvasHeight()); + controller_->FitContent(compositor_->GetWidth(), + compositor_->GetHeight()); break; case SDLK_z: @@ -309,8 +309,8 @@ case SDLK_c: TakeScreenshot( "screenshot.png", - compositor_->GetCanvasWidth(), - compositor_->GetCanvasHeight()); + compositor_->GetWidth(), + compositor_->GetHeight()); break; default: @@ -340,7 +340,7 @@ case SDL_BUTTON_RIGHT: return boost::shared_ptr(new ZoomSceneTracker - (controller_, e, compositor_->GetCanvasHeight())); + (controller_, e, compositor_->GetHeight())); case SDL_BUTTON_LEFT: { @@ -372,7 +372,7 @@ controller_, e)); case FusionMprGuiTool_Zoom: return boost::shared_ptr(new ZoomSceneTracker( - controller_, e, compositor_->GetCanvasHeight())); + controller_, e, compositor_->GetHeight())); //case GuiTool_AngleMeasure: // return new AngleMeasureTracker(GetScene(), e); //case GuiTool_CircleMeasure: diff -r a8cd3755db21 -r 31319fe867b9 Samples/Sdl/RadiographyEditor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Sdl/RadiographyEditor.cpp Tue Jul 09 11:46:43 2019 +0200 @@ -0,0 +1,267 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-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 . + **/ + +#include "../Shared/RadiographyEditorApp.h" + +// From Stone +#include "../../Framework/Oracle/SleepOracleCommand.h" +#include "../../Framework/Oracle/ThreadedOracle.h" +#include "../../Applications/Sdl/SdlOpenGLWindow.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/StoneInitialization.h" + +#include +#include + + +#include +#include + +#include +#include + +using namespace OrthancStone; + +namespace OrthancStone +{ + class NativeApplicationContext : public IMessageEmitter + { + private: + boost::shared_mutex mutex_; + MessageBroker broker_; + IObservable oracleObservable_; + + public: + NativeApplicationContext() : + oracleObservable_(broker_) + { + } + + + virtual void EmitMessage(const IObserver& observer, + const IMessage& message) ORTHANC_OVERRIDE + { + try + { + boost::unique_lock lock(mutex_); + oracleObservable_.EmitMessage(observer, message); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception while emitting a message: " << e.What(); + } + } + + + class ReaderLock : public boost::noncopyable + { + private: + NativeApplicationContext& that_; + boost::shared_lock lock_; + + public: + ReaderLock(NativeApplicationContext& that) : + that_(that), + lock_(that.mutex_) + { + } + }; + + + class WriterLock : public boost::noncopyable + { + private: + NativeApplicationContext& that_; + boost::unique_lock lock_; + + public: + WriterLock(NativeApplicationContext& that) : + that_(that), + lock_(that.mutex_) + { + } + + MessageBroker& GetBroker() + { + return that_.broker_; + } + + IObservable& GetOracleObservable() + { + return that_.oracleObservable_; + } + }; + }; +} + +class OpenGlSdlCompositorFactory : public ICompositorFactory +{ + OpenGL::IOpenGLContext& openGlContext_; + +public: + OpenGlSdlCompositorFactory(OpenGL::IOpenGLContext& openGlContext) : + openGlContext_(openGlContext) + {} + + ICompositor* GetCompositor(const Scene2D& scene) + { + + OpenGLCompositor* compositor = new OpenGLCompositor(openGlContext_, scene); + compositor->SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE_0, Orthanc::Encoding_Latin1); + compositor->SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE_1, Orthanc::Encoding_Latin1); + return compositor; + } +}; + +static void GLAPIENTRY +OpenGLMessageCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) +{ + if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) + { + fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", + (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), + type, severity, message); + } +} + + +/** + * IMPORTANT: The full arguments to "main()" are needed for SDL on + * Windows. Otherwise, one gets the linking error "undefined reference + * to `SDL_main'". https://wiki.libsdl.org/FAQWindows + **/ +int main(int argc, char* argv[]) +{ + using namespace OrthancStone; + + StoneInitialize(); + Orthanc::Logging::EnableInfoLevel(true); + // Orthanc::Logging::EnableTraceLevel(true); + + try + { + OrthancStone::NativeApplicationContext context; + OrthancStone::NativeApplicationContext::WriterLock lock(context); + OrthancStone::ThreadedOracle oracle(context); + + // False means we do NOT let Windows treat this as a legacy application + // that needs to be scaled + SdlOpenGLWindow window("Hello", 1024, 1024, false); + + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(OpenGLMessageCallback, 0); + + std::auto_ptr compositorFactory(new OpenGlSdlCompositorFactory(window)); + boost::shared_ptr app(new RadiographyEditorApp(oracle, lock.GetOracleObservable(), compositorFactory.release())); + app->PrepareScene(); + app->FitContent(window.GetCanvasWidth(), window.GetCanvasHeight()); + + bool stopApplication = false; + + while (!stopApplication) + { + app->Refresh(); + + SDL_Event event; + while (!stopApplication && SDL_PollEvent(&event)) + { + OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None; + if (event.key.keysym.mod & KMOD_CTRL) + modifiers = static_cast(static_cast(modifiers) | static_cast(OrthancStone::KeyboardModifiers_Control)); + if (event.key.keysym.mod & KMOD_ALT) + modifiers = static_cast(static_cast(modifiers) | static_cast(OrthancStone::KeyboardModifiers_Alt)); + if (event.key.keysym.mod & KMOD_SHIFT) + modifiers = static_cast(static_cast(modifiers) | static_cast(OrthancStone::KeyboardModifiers_Shift)); + + OrthancStone::MouseButton button; + if (event.button.button == SDL_BUTTON_LEFT) + button = OrthancStone::MouseButton_Left; + else if (event.button.button == SDL_BUTTON_MIDDLE) + button = OrthancStone::MouseButton_Middle; + else if (event.button.button == SDL_BUTTON_RIGHT) + button = OrthancStone::MouseButton_Right; + + if (event.type == SDL_QUIT) + { + stopApplication = true; + break; + } + else if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + { + app->DisableTracker(); // was: tracker.reset(NULL); + app->UpdateSize(); + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_f: + window.GetWindow().ToggleMaximize(); + break; + + case SDLK_q: + stopApplication = true; + break; + default: + { + app->OnKeyPressed(event.key.keysym.sym, modifiers); + } + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + app->OnMouseDown(event.button.x, event.button.y, modifiers, button); + } + else if (event.type == SDL_MOUSEMOTION) + { + app->OnMouseMove(event.button.x, event.button.y, modifiers); + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + app->OnMouseUp(event.button.x, event.button.y, modifiers, button); + } + } + SDL_Delay(1); + } + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + StoneFinalize(); + + return 0; +} + + diff -r a8cd3755db21 -r 31319fe867b9 Samples/Sdl/TrackerSampleApp.cpp --- a/Samples/Sdl/TrackerSampleApp.cpp Tue Jul 09 11:41:16 2019 +0200 +++ b/Samples/Sdl/TrackerSampleApp.cpp Tue Jul 09 11:46:43 2019 +0200 @@ -121,8 +121,8 @@ } // position the fixed info text in the upper right corner layerP->SetText(msgS.c_str()); - double cX = compositor_->GetCanvasWidth() * (-0.5); - double cY = compositor_->GetCanvasHeight() * (-0.5); + double cX = compositor_->GetWidth() * (-0.5); + double cY = compositor_->GetHeight() * (-0.5); GetScene()->GetCanvasToSceneTransform().Apply(cX,cY); layerP->SetPosition(cX, cY); } @@ -162,12 +162,12 @@ ScenePoint2D TrackerSampleApp::GetRandomPointInScene() const { - unsigned int w = compositor_->GetCanvasWidth(); - LOG(TRACE) << "compositor_->GetCanvasWidth() = " << - compositor_->GetCanvasWidth(); - unsigned int h = compositor_->GetCanvasHeight(); - LOG(TRACE) << "compositor_->GetCanvasHeight() = " << - compositor_->GetCanvasHeight(); + unsigned int w = compositor_->GetWidth(); + LOG(TRACE) << "compositor_->GetWidth() = " << + compositor_->GetWidth(); + unsigned int h = compositor_->GetHeight(); + LOG(TRACE) << "compositor_->GetHeight() = " << + compositor_->GetHeight(); if ((w >= RAND_MAX) || (h >= RAND_MAX)) LOG(WARNING) << "Canvas is too big : tools will not be randomly placed"; @@ -351,8 +351,8 @@ CreateRandomMeasureTool(); break; case SDLK_s: - controller_->FitContent(compositor_->GetCanvasWidth(), - compositor_->GetCanvasHeight()); + controller_->FitContent(compositor_->GetWidth(), + compositor_->GetHeight()); break; case SDLK_z: @@ -390,8 +390,8 @@ case SDLK_c: TakeScreenshot( "screenshot.png", - compositor_->GetCanvasWidth(), - compositor_->GetCanvasHeight()); + compositor_->GetWidth(), + compositor_->GetHeight()); break; default: @@ -421,7 +421,7 @@ case SDL_BUTTON_RIGHT: return boost::shared_ptr(new ZoomSceneTracker - (controller_, e, compositor_->GetCanvasHeight())); + (controller_, e, compositor_->GetHeight())); case SDL_BUTTON_LEFT: { @@ -453,7 +453,7 @@ controller_, e)); case GuiTool_Zoom: return boost::shared_ptr(new ZoomSceneTracker( - controller_, e, compositor_->GetCanvasHeight())); + controller_, e, compositor_->GetHeight())); //case GuiTool_AngleMeasure: // return new AngleMeasureTracker(GetScene(), e); //case GuiTool_CircleMeasure: diff -r a8cd3755db21 -r 31319fe867b9 Samples/Shared/RadiographyEditorApp.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Shared/RadiographyEditorApp.cpp Tue Jul 09 11:46:43 2019 +0200 @@ -0,0 +1,784 @@ +/** + * 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 . + **/ + +#include "RadiographyEditorApp.h" + +#include "../../Applications/Sdl/SdlOpenGLWindow.h" + +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" +#include "../../Framework/Scene2D/Scene2D.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h" +#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h" +#include "../../Framework/StoneInitialization.h" + +// From Orthanc framework +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace OrthancStone +{ + const char* MeasureToolToString(size_t i) + { + static const char* descs[] = { + "GuiTool_Rotate", + "GuiTool_Pan", + "GuiTool_Zoom", + "GuiTool_LineMeasure", + "GuiTool_CircleMeasure", + "GuiTool_AngleMeasure", + "GuiTool_EllipseMeasure", + "GuiTool_LAST" + }; + if (i >= GuiTool_LAST) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index"); + } + return descs[i]; + } + + boost::shared_ptr RadiographyEditorApp::GetScene() + { + return controller_->GetScene(); + } + + boost::shared_ptr RadiographyEditorApp::GetScene() const + { + return controller_->GetScene(); + } + + void RadiographyEditorApp::SelectNextTool() + { + currentTool_ = static_cast(currentTool_ + 1); + if (currentTool_ == GuiTool_LAST) + currentTool_ = static_cast(0);; + printf("Current tool is now: %s\n", MeasureToolToString(currentTool_)); + } + + void RadiographyEditorApp::DisplayInfoText() + { + // do not try to use stuff too early! + if (compositor_.get() == NULL) + return; + + std::stringstream msg; + + for (std::map::const_iterator kv = infoTextMap_.begin(); + kv != infoTextMap_.end(); ++kv) + { + msg << kv->first << " : " << kv->second << std::endl; + } + std::string msgS = msg.str(); + + TextSceneLayer* layerP = NULL; + if (GetScene()->HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX)) + { + TextSceneLayer& layer = dynamic_cast( + GetScene()->GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX)); + layerP = &layer; + } + else + { + std::auto_ptr layer(new TextSceneLayer); + layerP = layer.get(); + layer->SetColor(0, 255, 0); + layer->SetFontIndex(1); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_TopLeft); + //layer->SetPosition(0,0); + GetScene()->SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release()); + } + // position the fixed info text in the upper right corner + layerP->SetText(msgS.c_str()); + double cX = compositor_->GetWidth() * (-0.5); + double cY = compositor_->GetHeight() * (-0.5); + GetScene()->GetCanvasToSceneTransform().Apply(cX,cY); + layerP->SetPosition(cX, cY); + } + + void RadiographyEditorApp::DisplayFloatingCtrlInfoText(const PointerEvent& e) + { + ScenePoint2D p = e.GetMainPosition().Apply(GetScene()->GetCanvasToSceneTransform()); + + char buf[128]; + sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", + p.GetX(), p.GetY(), + e.GetMainPosition().GetX(), e.GetMainPosition().GetY()); + + if (GetScene()->HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)) + { + TextSceneLayer& layer = + dynamic_cast(GetScene()->GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX)); + layer.SetText(buf); + layer.SetPosition(p.GetX(), p.GetY()); + } + else + { + std::auto_ptr layer(new TextSceneLayer); + layer->SetColor(0, 255, 0); + layer->SetText(buf); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_BottomCenter); + layer->SetPosition(p.GetX(), p.GetY()); + GetScene()->SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release()); + } + } + + void RadiographyEditorApp::HideInfoText() + { + GetScene()->DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX); + } + + ScenePoint2D RadiographyEditorApp::GetRandomPointInScene() const + { + unsigned int w = compositor_->GetWidth(); + LOG(TRACE) << "compositor_->GetWidth() = " << + compositor_->GetWidth(); + unsigned int h = compositor_->GetHeight(); + LOG(TRACE) << "compositor_->GetHeight() = " << + compositor_->GetHeight(); + + if ((w >= RAND_MAX) || (h >= RAND_MAX)) + LOG(WARNING) << "Canvas is too big : tools will not be randomly placed"; + + int x = rand() % w; + int y = rand() % h; + LOG(TRACE) << "random x = " << x << "random y = " << y; + + ScenePoint2D p = compositor_->GetPixelCenterCoordinates(x, y); + LOG(TRACE) << "--> p.GetX() = " << p.GetX() << " p.GetY() = " << p.GetY(); + + ScenePoint2D r = p.Apply(GetScene()->GetCanvasToSceneTransform()); + LOG(TRACE) << "--> r.GetX() = " << r.GetX() << " r.GetY() = " << r.GetY(); + return r; + } + + void RadiographyEditorApp::CreateRandomMeasureTool() + { + static bool srandCalled = false; + if (!srandCalled) + { + srand(42); + srandCalled = true; + } + + int i = rand() % 2; + LOG(TRACE) << "random i = " << i; + switch (i) + { + case 0: + // line measure + { + boost::shared_ptr cmd = + boost::make_shared( + boost::ref(IObserver::GetBroker()), + controller_, + GetRandomPointInScene()); + cmd->SetEnd(GetRandomPointInScene()); + controller_->PushCommand(cmd); + } + break; + case 1: + // angle measure + { + boost::shared_ptr cmd = + boost::make_shared( + boost::ref(IObserver::GetBroker()), + controller_, + GetRandomPointInScene()); + cmd->SetCenter(GetRandomPointInScene()); + cmd->SetSide2End(GetRandomPointInScene()); + controller_->PushCommand(cmd); + } + break; + } + } + + void RadiographyEditorApp::OnMouseMove(int x, int y, OrthancStone::KeyboardModifiers modifiers) + { + DisplayInfoText(); + if (activeTracker_.get() == NULL && (modifiers & OrthancStone::KeyboardModifiers_Alt)) + { + // The "left-ctrl" key is down, while no tracker is present + // Let's display the info text + PointerEvent e; + e.AddPosition(compositor_->GetPixelCenterCoordinates(x, y)); + + DisplayFloatingCtrlInfoText(e); + } + else { + HideInfoText(); + //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)"; + if (activeTracker_.get() != NULL) + { + //LOG(TRACE) << "(activeTracker_.get() != NULL)"; + PointerEvent e; + e.AddPosition(compositor_->GetPixelCenterCoordinates(x, y)); + + //LOG(TRACE) << "event.button.x = " << event.button.x << " " << + // "event.button.y = " << event.button.y; + LOG(TRACE) << "activeTracker_->PointerMove(e); " << + e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY(); + + activeTracker_->PointerMove(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + } + } + + void RadiographyEditorApp::OnKeyPressed(char keyChar, OrthancStone::KeyboardModifiers modifiers) + { + DisplayInfoText(); + + switch (keyChar) + { + case '\033': // escape + { + if (activeTracker_) + { + activeTracker_->Cancel(); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + };break; + case 't': + { + if (!activeTracker_) + SelectNextTool(); + else + { + LOG(WARNING) << "You cannot change the active tool when an interaction" + " is taking place"; + } + };break; + case 'm': + CreateRandomMeasureTool(); + break; + case 's': + controller_->FitContent(compositor_->GetWidth(), + compositor_->GetHeight()); + break; + case 'z': + LOG(TRACE) << "z has been pressed. modifier = " << modifiers; + if (modifiers & OrthancStone::KeyboardModifiers_Control) + { + if (controller_->CanUndo()) + { + LOG(TRACE) << "Undoing..."; + controller_->Undo(); + } + else + { + LOG(WARNING) << "Nothing to undo!!!"; + } + } + break; + + case 'y': + LOG(TRACE) << "y has been pressed. modifier = " << modifiers; + if (modifiers & OrthancStone::KeyboardModifiers_Control) + { + if (controller_->CanRedo()) + { + LOG(TRACE) << "Redoing..."; + controller_->Redo(); + } + else + { + LOG(WARNING) << "Nothing to redo!!!"; + } + } + break; + + case 'c': + TakeScreenshot( + "screenshot.png", + compositor_->GetWidth(), + compositor_->GetHeight()); + break; + + } + } + + void RadiographyEditorApp::OnMouseDown(int x, int y, OrthancStone::KeyboardModifiers modifiers, OrthancStone::MouseButton button) + { + DisplayInfoText(); + PointerEvent e; + e.AddPosition(compositor_->GetPixelCenterCoordinates(x, y)); + // TODO: set modifiers in e + + if (activeTracker_) + { + activeTracker_->PointerDown(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + else + { + // we ATTEMPT to create a tracker if need be + activeTracker_ = CreateSuitableTracker(button, e); + } + } + + void RadiographyEditorApp::OnMouseUp(int x, int y, OrthancStone::KeyboardModifiers modifiers, OrthancStone::MouseButton button) + { + DisplayInfoText(); + if (activeTracker_) + { + PointerEvent e; + e.AddPosition(compositor_->GetPixelCenterCoordinates(x, y)); + // TODO: set modifiers in e + + activeTracker_->PointerUp(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + } + + void RadiographyEditorApp::HandleApplicationEvent( + const SDL_Event & event) + { + DisplayInfoText(); + + if (event.type == SDL_MOUSEMOTION) + { + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + + if (activeTracker_.get() == NULL && + SDL_SCANCODE_LALT < scancodeCount && + keyboardState[SDL_SCANCODE_LALT]) + { + // The "left-ctrl" key is down, while no tracker is present + // Let's display the info text + PointerEvent e; + e.AddPosition(compositor_->GetPixelCenterCoordinates( + event.button.x, event.button.y)); + // TODO: set modifiers in e + + DisplayFloatingCtrlInfoText(e); + } + else + { + HideInfoText(); + //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)"; + if (activeTracker_.get() != NULL) + { + //LOG(TRACE) << "(activeTracker_.get() != NULL)"; + PointerEvent e; + e.AddPosition(compositor_->GetPixelCenterCoordinates( + event.button.x, event.button.y)); + // TODO: set modifiers in e + + //LOG(TRACE) << "event.button.x = " << event.button.x << " " << + // "event.button.y = " << event.button.y; + LOG(TRACE) << "activeTracker_->PointerMove(e); " << + e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY(); + + activeTracker_->PointerMove(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + } + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + if (activeTracker_) + { + PointerEvent e; + e.AddPosition(compositor_->GetPixelCenterCoordinates(event.button.x, event.button.y)); + // TODO: set modifiers in e + activeTracker_->PointerUp(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + PointerEvent e; + e.AddPosition(compositor_->GetPixelCenterCoordinates( + event.button.x, event.button.y)); + // TODO: set modifiers in e + if (activeTracker_) + { + activeTracker_->PointerDown(e); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + else + { + // we ATTEMPT to create a tracker if need be +// activeTracker_ = CreateSuitableTracker(event, e); + } + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_ESCAPE: + if (activeTracker_) + { + activeTracker_->Cancel(); + if (!activeTracker_->IsAlive()) + activeTracker_.reset(); + } + break; + + case SDLK_t: + if (!activeTracker_) + SelectNextTool(); + else + { + LOG(WARNING) << "You cannot change the active tool when an interaction" + " is taking place"; + } + break; + + case SDLK_m: + CreateRandomMeasureTool(); + break; + case SDLK_s: + controller_->FitContent(compositor_->GetWidth(), + compositor_->GetHeight()); + break; + + case SDLK_z: + LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; + if (event.key.keysym.mod & KMOD_CTRL) + { + if (controller_->CanUndo()) + { + LOG(TRACE) << "Undoing..."; + controller_->Undo(); + } + else + { + LOG(WARNING) << "Nothing to undo!!!"; + } + } + break; + + case SDLK_y: + LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod; + if (event.key.keysym.mod & KMOD_CTRL) + { + if (controller_->CanRedo()) + { + LOG(TRACE) << "Redoing..."; + controller_->Redo(); + } + else + { + LOG(WARNING) << "Nothing to redo!!!"; + } + } + break; + + case SDLK_c: + TakeScreenshot( + "screenshot.png", + compositor_->GetWidth(), + compositor_->GetHeight()); + break; + + default: + break; + } + } + } + + + void RadiographyEditorApp::OnSceneTransformChanged( + const ViewportController::SceneTransformChanged& message) + { + DisplayInfoText(); + } + + boost::shared_ptr RadiographyEditorApp::CreateSuitableTracker( + OrthancStone::MouseButton button, + const PointerEvent & e) + { + using namespace Orthanc; + + switch (button) + { + case OrthancStone::MouseButton_Middle: + return boost::shared_ptr(new PanSceneTracker + (controller_, e)); + + case OrthancStone::MouseButton_Right: + return boost::shared_ptr(new ZoomSceneTracker + (controller_, e, compositor_->GetHeight())); + + case OrthancStone::MouseButton_Left: + { + //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:"; + // TODO: we need to iterate on the set of measuring tool and perform + // a hit test to check if a tracker needs to be created for edition. + // Otherwise, depending upon the active tool, we might want to create + // a "measuring tool creation" tracker + + // TODO: if there are conflicts, we should prefer a tracker that + // pertains to the type of measuring tool currently selected (TBD?) + boost::shared_ptr hitTestTracker = TrackerHitTest(e); + + if (hitTestTracker != NULL) + { + //LOG(TRACE) << "hitTestTracker != NULL"; + return hitTestTracker; + } + else + { + switch (currentTool_) + { + case GuiTool_Rotate: + //LOG(TRACE) << "Creating RotateSceneTracker"; + return boost::shared_ptr(new RotateSceneTracker( + controller_, e)); + case GuiTool_Pan: + return boost::shared_ptr(new PanSceneTracker( + controller_, e)); + case GuiTool_Zoom: + return boost::shared_ptr(new ZoomSceneTracker( + controller_, e, compositor_->GetHeight())); + //case GuiTool_AngleMeasure: + // return new AngleMeasureTracker(GetScene(), e); + //case GuiTool_CircleMeasure: + // return new CircleMeasureTracker(GetScene(), e); + //case GuiTool_EllipseMeasure: + // return new EllipseMeasureTracker(GetScene(), e); + case GuiTool_LineMeasure: + return boost::shared_ptr(new CreateLineMeasureTracker( + IObserver::GetBroker(), controller_, e)); + case GuiTool_AngleMeasure: + return boost::shared_ptr(new CreateAngleMeasureTracker( + IObserver::GetBroker(), controller_, e)); + case GuiTool_CircleMeasure: + LOG(ERROR) << "Not implemented yet!"; + return boost::shared_ptr(); + case GuiTool_EllipseMeasure: + LOG(ERROR) << "Not implemented yet!"; + return boost::shared_ptr(); + default: + throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); + } + } + } + default: + return boost::shared_ptr(); + } + } + + + RadiographyEditorApp::RadiographyEditorApp(OrthancStone::IOracle& oracle, + IObservable& oracleObservable, + ICompositorFactory* compositorFactory) : + IObserver(oracleObservable.GetBroker()), + oracle_(oracle), + compositorFactory_(compositorFactory), + currentTool_(GuiTool_Rotate) + { + controller_ = boost::shared_ptr(new ViewportController(IObserver::GetBroker())); + + controller_->RegisterObserverCallback( + new Callable + (*this, &RadiographyEditorApp::OnSceneTransformChanged)); + + TEXTURE_2x2_1_ZINDEX = 1; + TEXTURE_1x1_ZINDEX = 2; + TEXTURE_2x2_2_ZINDEX = 3; + LINESET_1_ZINDEX = 4; + LINESET_2_ZINDEX = 5; + FLOATING_INFOTEXT_LAYER_ZINDEX = 6; + FIXED_INFOTEXT_LAYER_ZINDEX = 7; + } + + void RadiographyEditorApp::PrepareScene() + { + // Texture of 2x2 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); + + uint8_t* p = reinterpret_cast(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + p[3] = 0; + p[4] = 255; + p[5] = 0; + + p = reinterpret_cast(i.GetRow(1)); + p[0] = 0; + p[1] = 0; + p[2] = 255; + + p[3] = 255; + p[4] = 0; + p[5] = 0; + + GetScene()->SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i)); + + std::auto_ptr l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-3, 2); + l->SetPixelSpacing(1.5, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + GetScene()->SetLayer(TEXTURE_2x2_2_ZINDEX, l.release()); + } + + // Texture of 1x1 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); + + uint8_t* p = reinterpret_cast(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + std::auto_ptr l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-2, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + GetScene()->SetLayer(TEXTURE_1x1_ZINDEX, l.release()); + } + + // Some lines + { + std::auto_ptr layer(new PolylineSceneLayer); + + layer->SetThickness(1); + + PolylineSceneLayer::Chain chain; + chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); + chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); + layer->AddChain(chain, true, 255, 0, 0); + + chain.clear(); + chain.push_back(ScenePoint2D(-5, -5)); + chain.push_back(ScenePoint2D(5, -5)); + chain.push_back(ScenePoint2D(5, 5)); + chain.push_back(ScenePoint2D(-5, 5)); + layer->AddChain(chain, true, 0, 255, 0); + + double dy = 1.01; + chain.clear(); + chain.push_back(ScenePoint2D(-4, -4)); + chain.push_back(ScenePoint2D(4, -4 + dy)); + chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); + chain.push_back(ScenePoint2D(4, 2)); + layer->AddChain(chain, false, 0, 0, 255); + + GetScene()->SetLayer(LINESET_1_ZINDEX, layer.release()); + } + + // Some text + { + std::auto_ptr layer(new TextSceneLayer); + layer->SetText("Hello"); + GetScene()->SetLayer(LINESET_2_ZINDEX, layer.release()); + } + } + + + void RadiographyEditorApp::DisableTracker() + { + if (activeTracker_) + { + activeTracker_->Cancel(); + activeTracker_.reset(); + } + } + + void RadiographyEditorApp::TakeScreenshot(const std::string& target, + unsigned int canvasWidth, + unsigned int canvasHeight) + { + CairoCompositor compositor(*GetScene(), canvasWidth, canvasHeight); + compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1); + compositor.Refresh(); + + Orthanc::ImageAccessor canvas; + compositor.GetCanvas().GetReadOnlyAccessor(canvas); + + Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); + Orthanc::ImageProcessing::Convert(png, canvas); + + Orthanc::PngWriter writer; + writer.WriteToFile(target, png); + } + + + boost::shared_ptr RadiographyEditorApp::TrackerHitTest(const PointerEvent & e) + { + // std::vector> measureTools_; + return boost::shared_ptr(); + } + + + void RadiographyEditorApp::FitContent(unsigned int width, unsigned int height) + { + controller_->FitContent(width, height); + } + + void RadiographyEditorApp::UpdateSize() + { + if (dynamic_cast(compositor_.get()) != NULL) + { + dynamic_cast(compositor_.get())->UpdateSize(); + } + } + + void RadiographyEditorApp::Refresh() + { + compositor_.reset(compositorFactory_->GetCompositor(*GetScene())); + compositor_->Refresh(); + + // the following is paramount because the compositor holds a reference + // to the scene and we do not want this reference to become dangling + // TODO ???? compositor_.reset(NULL); + } + + void RadiographyEditorApp::SetInfoDisplayMessage( + std::string key, std::string value) + { + if (value == "") + infoTextMap_.erase(key); + else + infoTextMap_[key] = value; + DisplayInfoText(); + } + +} diff -r a8cd3755db21 -r 31319fe867b9 Samples/Shared/RadiographyEditorApp.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Shared/RadiographyEditorApp.h Tue Jul 09 11:46:43 2019 +0200 @@ -0,0 +1,168 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-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 . + **/ + + +#include "../../Framework/Messages/IObserver.h" +#include "../../Framework/Oracle/IOracle.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/Internals/CompositorHelper.h" +#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h" +#include "../../Framework/Scene2DViewport/MeasureTool.h" +#include "../../Framework/Scene2DViewport/PredeclaredTypes.h" +#include "../../Framework/Scene2DViewport/ViewportController.h" + +#include + +#include +#include +#include + + +namespace OrthancStone +{ + class ICompositorFactory + { + public: + virtual OrthancStone::ICompositor* GetCompositor(const OrthancStone::Scene2D& scene) = 0; + }; + + class IInteractor + { + public: + virtual void OnMouseDown(int x, int y, OrthancStone::KeyboardModifiers modifiers, OrthancStone::MouseButton button) = 0; + virtual void OnMouseMove(int x, int y, OrthancStone::KeyboardModifiers modifiers) = 0; + virtual void OnMouseUp(int x, int y, OrthancStone::KeyboardModifiers modifiers, OrthancStone::MouseButton button) = 0; + virtual void OnKeyPressed(char keyChar, OrthancStone::KeyboardModifiers modifiers) = 0; + }; + + + + + enum GuiTool + { + GuiTool_Rotate = 0, + GuiTool_Pan, + GuiTool_Zoom, + GuiTool_LineMeasure, + GuiTool_CircleMeasure, + GuiTool_AngleMeasure, + GuiTool_EllipseMeasure, + GuiTool_LAST + }; + + const char* MeasureToolToString(size_t i); + + static const unsigned int FONT_SIZE_0 = 32; + static const unsigned int FONT_SIZE_1 = 24; + + class Scene2D; + + class RadiographyEditorApp : public IObserver + , public IInteractor, public boost::enable_shared_from_this + { + OrthancStone::IOracle& oracle_; + std::auto_ptr compositorFactory_; + std::auto_ptr compositor_; + + public: + // 12 because. + RadiographyEditorApp(OrthancStone::IOracle& oracle, IObservable& oracleObservable, ICompositorFactory* compositorFactory); + + void PrepareScene(); + void FitContent(unsigned int width, unsigned int height); + void Refresh(); + void UpdateSize(); + void SetInfoDisplayMessage(std::string key, std::string value); + void DisableTracker(); + + virtual void OnMouseMove(int x, int y, OrthancStone::KeyboardModifiers modifiers); + virtual void OnKeyPressed(char keyChar, OrthancStone::KeyboardModifiers modifiers); + virtual void OnMouseDown(int x, int y, OrthancStone::KeyboardModifiers modifiers, OrthancStone::MouseButton button); + virtual void OnMouseUp(int x, int y, OrthancStone::KeyboardModifiers modifiers, OrthancStone::MouseButton button); + + boost::shared_ptr GetScene(); + boost::shared_ptr GetScene() const; + + void HandleApplicationEvent(const SDL_Event& event); + + /** + This method is called when the scene transform changes. It allows to + recompute the visual elements whose content depend upon the scene transform + */ + void OnSceneTransformChanged( + const ViewportController::SceneTransformChanged& message); + + private: + void SelectNextTool(); + void CreateRandomMeasureTool(); + + /** + This returns a random point in the canvas part of the scene, but in + scene coordinates + */ + ScenePoint2D GetRandomPointInScene() const; + + boost::shared_ptr TrackerHitTest(const PointerEvent& e); + + boost::shared_ptr CreateSuitableTracker( + OrthancStone::MouseButton button, + const PointerEvent& e); + + void TakeScreenshot( + const std::string& target, + unsigned int canvasWidth, + unsigned int canvasHeight); + + /** + This adds the command at the top of the undo stack + */ + void Commit(boost::shared_ptr cmd); + void Undo(); + void Redo(); + + private: + void DisplayFloatingCtrlInfoText(const PointerEvent& e); + void DisplayInfoText(); + void HideInfoText(); + + private: + /** + WARNING: the measuring tools do store a reference to the scene, and it + paramount that the scene gets destroyed AFTER the measurement tools. + */ + boost::shared_ptr controller_; + + std::map infoTextMap_; + boost::shared_ptr activeTracker_; + + //static const int LAYER_POSITION = 150; + + int TEXTURE_2x2_1_ZINDEX; + int TEXTURE_1x1_ZINDEX; + int TEXTURE_2x2_2_ZINDEX; + int LINESET_1_ZINDEX; + int LINESET_2_ZINDEX; + int FLOATING_INFOTEXT_LAYER_ZINDEX; + int FIXED_INFOTEXT_LAYER_ZINDEX; + + GuiTool currentTool_; + }; + +}