# HG changeset patch # User Sebastien Jodogne # Date 1510836363 -3600 # Node ID 1982d6c1d2fffd4fd532f09fb10705d6404d1b1b # Parent a823122db53d06a79b7e5f42d3be8c622aba9211 IVolumeLoader diff -r a823122db53d -r 1982d6c1d2ff Applications/BasicApplicationContext.cpp --- a/Applications/BasicApplicationContext.cpp Thu Nov 16 12:50:22 2017 +0100 +++ b/Applications/BasicApplicationContext.cpp Thu Nov 16 13:46:03 2017 +0100 @@ -56,13 +56,13 @@ delete *it; } - for (Volumes::iterator it = volumes_.begin(); it != volumes_.end(); ++it) + for (SlicedVolumes::iterator it = slicedVolumes_.begin(); it != slicedVolumes_.end(); ++it) { assert(*it != NULL); delete *it; } - for (StructureSets::iterator it = structureSets_.begin(); it != structureSets_.end(); ++it) + for (VolumeLoaders::iterator it = volumeLoaders_.begin(); it != volumeLoaders_.end(); ++it) { assert(*it != NULL); delete *it; @@ -77,28 +77,31 @@ } - ISlicedVolume& BasicApplicationContext::AddVolume(ISlicedVolume* volume) + ISlicedVolume& BasicApplicationContext::AddSlicedVolume(ISlicedVolume* volume) { if (volume == NULL) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); } - - volumes_.push_back(volume); - return *volume; + else + { + slicedVolumes_.push_back(volume); + return *volume; + } } - DicomStructureSet& BasicApplicationContext::AddStructureSet(const std::string& instance) + + IVolumeLoader& BasicApplicationContext::AddVolumeLoader(IVolumeLoader* loader) { - /*std::auto_ptr structureSet - (new DicomStructureSet(GetWebService().GetConnection(), instance)); - - DicomStructureSet& result = *structureSet; - structureSets_.push_back(structureSet.release()); - - return result;*/ - - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + if (loader == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + else + { + volumeLoaders_.push_back(loader); + return *loader; + } } diff -r a823122db53d -r 1982d6c1d2ff Applications/BasicApplicationContext.h --- a/Applications/BasicApplicationContext.h Thu Nov 16 12:50:22 2017 +0100 +++ b/Applications/BasicApplicationContext.h Thu Nov 16 13:46:03 2017 +0100 @@ -21,10 +21,10 @@ #pragma once +#include "../../Framework/Viewport/WidgetViewport.h" #include "../../Framework/Volumes/ISlicedVolume.h" -#include "../../Framework/Viewport/WidgetViewport.h" +#include "../../Framework/Volumes/IVolumeLoader.h" #include "../../Framework/Widgets/IWorldSceneInteractor.h" -#include "../../Framework/Toolbox/DicomStructureSet.h" #include "../../Platforms/Generic/OracleWebService.h" #include @@ -35,9 +35,9 @@ class BasicApplicationContext : public boost::noncopyable { private: - typedef std::list Volumes; + typedef std::list SlicedVolumes; + typedef std::list VolumeLoaders; typedef std::list Interactors; - typedef std::list StructureSets; static void UpdateThread(BasicApplicationContext* that); @@ -45,9 +45,9 @@ OracleWebService webService_; boost::mutex viewportMutex_; WidgetViewport viewport_; - Volumes volumes_; + SlicedVolumes slicedVolumes_; + VolumeLoaders volumeLoaders_; Interactors interactors_; - StructureSets structureSets_; boost::thread updateThread_; bool stopped_; unsigned int updateDelay_; @@ -84,9 +84,9 @@ return webService_; } - ISlicedVolume& AddVolume(ISlicedVolume* volume); + ISlicedVolume& AddSlicedVolume(ISlicedVolume* volume); - DicomStructureSet& AddStructureSet(const std::string& instance); + IVolumeLoader& AddVolumeLoader(IVolumeLoader* loader); IWorldSceneInteractor& AddInteractor(IWorldSceneInteractor* interactor); diff -r a823122db53d -r 1982d6c1d2ff Applications/Samples/SingleVolumeApplication.h --- a/Applications/Samples/SingleVolumeApplication.h Thu Nov 16 12:50:22 2017 +0100 +++ b/Applications/Samples/SingleVolumeApplication.h Thu Nov 16 13:46:03 2017 +0100 @@ -188,7 +188,7 @@ widget->AddLayer(new VolumeImageSource(*volume)); context.AddInteractor(new Interactor(*volume, *widget, projection, 0)); - context.AddVolume(volume.release()); + context.AddSlicedVolume(volume.release()); { RenderStyle s; @@ -211,19 +211,20 @@ //pet->ScheduleLoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // IBA 3 //pet->ScheduleLoadInstance("269f26f4-0c83eeeb-2e67abbd-5467a40f-f1bec90c"); // 0522c0001 TCIA - std::auto_ptr rtStruct(new DicomStructureSetRendererFactory(context.GetWebService())); + std::auto_ptr rtStruct(new StructureSetLoader(context.GetWebService())); rtStruct->ScheduleLoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // IBA //rtStruct->ScheduleLoadInstance("17cd032b-ad92a438-ca05f06a-f9e96668-7e3e9e20"); // 0522c0001 TCIA widget->AddLayer(new VolumeImageSource(*ct)); widget->AddLayer(new VolumeImageSource(*pet)); - widget->AddLayer(rtStruct.release()); + widget->AddLayer(new DicomStructureSetRendererFactory(*rtStruct)); context.AddInteractor(new Interactor(*pet, *widget, projection, 1)); //context.AddInteractor(new VolumeImageInteractor(*ct, *widget, projection)); - context.AddVolume(ct.release()); - context.AddVolume(pet.release()); + context.AddSlicedVolume(ct.release()); + context.AddSlicedVolume(pet.release()); + context.AddVolumeLoader(rtStruct.release()); { RenderStyle s; diff -r a823122db53d -r 1982d6c1d2ff Framework/Layers/DicomStructureSetRendererFactory.cpp --- a/Framework/Layers/DicomStructureSetRendererFactory.cpp Thu Nov 16 12:50:22 2017 +0100 +++ b/Framework/Layers/DicomStructureSetRendererFactory.cpp Thu Nov 16 13:46:03 2017 +0100 @@ -21,10 +21,6 @@ #include "DicomStructureSetRendererFactory.h" -#include "../Toolbox/MessagingToolbox.h" - -#include - namespace OrthancStone { class DicomStructureSetRendererFactory::Renderer : public ILayerRenderer @@ -103,147 +99,11 @@ }; - class DicomStructureSetRendererFactory::Operation : public Orthanc::IDynamicObject - { - public: - enum Type - { - Type_LoadStructureSet, - Type_LookupSopInstanceUid, - Type_LoadReferencedSlice - }; - - private: - Type type_; - std::string value_; - - public: - Operation(Type type, - const std::string& value) : - type_(type), - value_(value) - { - } - - Type GetType() const - { - return type_; - } - - const std::string& GetIdentifier() const - { - return value_; - } - }; - - - void DicomStructureSetRendererFactory::NotifyError(const std::string& uri, - Orthanc::IDynamicObject* payload) - { - // TODO - } - - - void DicomStructureSetRendererFactory::NotifySuccess(const std::string& uri, - const void* answer, - size_t answerSize, - Orthanc::IDynamicObject* payload) - { - std::auto_ptr op(dynamic_cast(payload)); - - switch (op->GetType()) - { - case Operation::Type_LoadStructureSet: - { - OrthancPlugins::FullOrthancDataset dataset(answer, answerSize); - structureSet_.reset(new DicomStructureSet(dataset)); - - std::set instances; - structureSet_->GetReferencedInstances(instances); - - for (std::set::const_iterator it = instances.begin(); - it != instances.end(); ++it) - { - orthanc_.SchedulePostRequest(*this, "/tools/lookup", *it, - new Operation(Operation::Type_LookupSopInstanceUid, *it)); - } - - break; - } - - case Operation::Type_LookupSopInstanceUid: - { - Json::Value lookup; - - if (MessagingToolbox::ParseJson(lookup, answer, answerSize)) - { - if (lookup.type() != Json::arrayValue || - lookup.size() != 1 || - !lookup[0].isMember("Type") || - !lookup[0].isMember("Path") || - lookup[0]["Type"].type() != Json::stringValue || - lookup[0]["ID"].type() != Json::stringValue || - lookup[0]["Type"].asString() != "Instance") - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); - } - - const std::string& instance = lookup[0]["ID"].asString(); - orthanc_.ScheduleGetRequest(*this, "/instances/" + instance + "/tags", - new Operation(Operation::Type_LoadReferencedSlice, instance)); - } - else - { - // TODO - } - - break; - } - - case Operation::Type_LoadReferencedSlice: - { - OrthancPlugins::FullOrthancDataset dataset(answer, answerSize); - - Orthanc::DicomMap slice; - MessagingToolbox::ConvertDataset(slice, dataset); - structureSet_->AddReferencedSlice(slice); - - LayerSourceBase::NotifyContentChange(); - - break; - } - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - - DicomStructureSetRendererFactory::DicomStructureSetRendererFactory(IWebService& orthanc) : - orthanc_(orthanc) - { - } - - - void DicomStructureSetRendererFactory::ScheduleLoadInstance(const std::string& instance) - { - if (structureSet_.get() != NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - const std::string uri = "/instances/" + instance + "/tags?ignore-length=3006-0050"; - orthanc_.ScheduleGetRequest(*this, uri, new Operation(Operation::Type_LoadStructureSet, instance)); - } - } - - void DicomStructureSetRendererFactory::ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) { - if (structureSet_.get() != NULL) + if (loader_.HasStructureSet()) { - NotifyLayerReady(new Renderer(*structureSet_, viewportSlice), viewportSlice, false); + NotifyLayerReady(new Renderer(loader_.GetStructureSet(), viewportSlice), viewportSlice, false); } } } diff -r a823122db53d -r 1982d6c1d2ff Framework/Layers/DicomStructureSetRendererFactory.h --- a/Framework/Layers/DicomStructureSetRendererFactory.h Thu Nov 16 12:50:22 2017 +0100 +++ b/Framework/Layers/DicomStructureSetRendererFactory.h Thu Nov 16 13:46:03 2017 +0100 @@ -21,35 +21,41 @@ #pragma once -#include "../Toolbox/DicomStructureSet.h" -#include "../Toolbox/IWebService.h" #include "LayerSourceBase.h" +#include "../Volumes/StructureSetLoader.h" namespace OrthancStone { class DicomStructureSetRendererFactory : public LayerSourceBase, - private IWebService::ICallback + private IVolumeLoader::IObserver { private: class Renderer; - class Operation; + + virtual void NotifyGeometryReady(const IVolumeLoader& loader) + { + LayerSourceBase::NotifyGeometryReady(); + } + + virtual void NotifyGeometryError(const IVolumeLoader& loader) + { + LayerSourceBase::NotifyGeometryError(); + } + + virtual void NotifyContentChange(const IVolumeLoader& loader) + { + LayerSourceBase::NotifyContentChange(); + } - virtual void NotifyError(const std::string& uri, - Orthanc::IDynamicObject* payload); - - virtual void NotifySuccess(const std::string& uri, - const void* answer, - size_t answerSize, - Orthanc::IDynamicObject* payload); - - IWebService& orthanc_; - std::auto_ptr structureSet_; + StructureSetLoader& loader_; public: - DicomStructureSetRendererFactory(IWebService& orthanc); - - void ScheduleLoadInstance(const std::string& instance); + DicomStructureSetRendererFactory(StructureSetLoader& loader) : + loader_(loader) + { + loader_.Register(*this); + } virtual bool GetExtent(std::vector& points, const CoordinateSystem3D& viewportSlice) diff -r a823122db53d -r 1982d6c1d2ff Framework/Volumes/IVolumeLoader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/IVolumeLoader.h Thu Nov 16 13:46:03 2017 +0100 @@ -0,0 +1,53 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include + +namespace OrthancStone +{ + class IVolumeLoader : public boost::noncopyable + { + public: + class IObserver : public boost::noncopyable + { + public: + virtual ~IObserver() + { + } + + virtual void NotifyGeometryReady(const IVolumeLoader& loader) = 0; + + virtual void NotifyGeometryError(const IVolumeLoader& loader) = 0; + + // Triggered if the content of several slices in the loader has + // changed + virtual void NotifyContentChange(const IVolumeLoader& loader) = 0; + }; + + virtual ~IVolumeLoader() + { + } + + virtual void Register(IObserver& observer) = 0; + }; +} diff -r a823122db53d -r 1982d6c1d2ff Framework/Volumes/StructureSetLoader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/StructureSetLoader.cpp Thu Nov 16 13:46:03 2017 +0100 @@ -0,0 +1,179 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public 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 "StructureSetLoader.h" + +#include "../Toolbox/MessagingToolbox.h" + +#include + +namespace OrthancStone +{ + class StructureSetLoader::Operation : public Orthanc::IDynamicObject + { + public: + enum Type + { + Type_LoadStructureSet, + Type_LookupSopInstanceUid, + Type_LoadReferencedSlice + }; + + private: + Type type_; + std::string value_; + + public: + Operation(Type type, + const std::string& value) : + type_(type), + value_(value) + { + } + + Type GetType() const + { + return type_; + } + + const std::string& GetIdentifier() const + { + return value_; + } + }; + + + void StructureSetLoader::NotifyError(const std::string& uri, + Orthanc::IDynamicObject* payload) + { + // TODO + } + + + void StructureSetLoader::NotifySuccess(const std::string& uri, + const void* answer, + size_t answerSize, + Orthanc::IDynamicObject* payload) + { + std::auto_ptr op(dynamic_cast(payload)); + + switch (op->GetType()) + { + case Operation::Type_LoadStructureSet: + { + OrthancPlugins::FullOrthancDataset dataset(answer, answerSize); + structureSet_.reset(new DicomStructureSet(dataset)); + + std::set instances; + structureSet_->GetReferencedInstances(instances); + + for (std::set::const_iterator it = instances.begin(); + it != instances.end(); ++it) + { + orthanc_.SchedulePostRequest(*this, "/tools/lookup", *it, + new Operation(Operation::Type_LookupSopInstanceUid, *it)); + } + + VolumeLoaderBase::NotifyGeometryReady(); + + break; + } + + case Operation::Type_LookupSopInstanceUid: + { + Json::Value lookup; + + if (MessagingToolbox::ParseJson(lookup, answer, answerSize)) + { + if (lookup.type() != Json::arrayValue || + lookup.size() != 1 || + !lookup[0].isMember("Type") || + !lookup[0].isMember("Path") || + lookup[0]["Type"].type() != Json::stringValue || + lookup[0]["ID"].type() != Json::stringValue || + lookup[0]["Type"].asString() != "Instance") + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + const std::string& instance = lookup[0]["ID"].asString(); + orthanc_.ScheduleGetRequest(*this, "/instances/" + instance + "/tags", + new Operation(Operation::Type_LoadReferencedSlice, instance)); + } + else + { + // TODO + } + + break; + } + + case Operation::Type_LoadReferencedSlice: + { + OrthancPlugins::FullOrthancDataset dataset(answer, answerSize); + + Orthanc::DicomMap slice; + MessagingToolbox::ConvertDataset(slice, dataset); + structureSet_->AddReferencedSlice(slice); + + VolumeLoaderBase::NotifyContentChange(); + + break; + } + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + + StructureSetLoader::StructureSetLoader(IWebService& orthanc) : + orthanc_(orthanc) + { + } + + + void StructureSetLoader::ScheduleLoadInstance(const std::string& instance) + { + if (structureSet_.get() != NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + const std::string uri = "/instances/" + instance + "/tags?ignore-length=3006-0050"; + orthanc_.ScheduleGetRequest(*this, uri, new Operation(Operation::Type_LoadStructureSet, instance)); + } + } + + + DicomStructureSet& StructureSetLoader::GetStructureSet() + { + if (structureSet_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return *structureSet_; + } + } +} diff -r a823122db53d -r 1982d6c1d2ff Framework/Volumes/StructureSetLoader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/StructureSetLoader.h Thu Nov 16 13:46:03 2017 +0100 @@ -0,0 +1,60 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "../Toolbox/DicomStructureSet.h" +#include "../Toolbox/IWebService.h" +#include "VolumeLoaderBase.h" + +namespace OrthancStone +{ + class StructureSetLoader : + public VolumeLoaderBase, + private IWebService::ICallback + { + private: + class Operation; + + virtual void NotifyError(const std::string& uri, + Orthanc::IDynamicObject* payload); + + virtual void NotifySuccess(const std::string& uri, + const void* answer, + size_t answerSize, + Orthanc::IDynamicObject* payload); + + IWebService& orthanc_; + std::auto_ptr structureSet_; + + public: + StructureSetLoader(IWebService& orthanc); + + void ScheduleLoadInstance(const std::string& instance); + + bool HasStructureSet() const + { + return structureSet_.get() != NULL; + } + + DicomStructureSet& GetStructureSet(); + }; +} diff -r a823122db53d -r 1982d6c1d2ff Framework/Volumes/VolumeLoaderBase.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/VolumeLoaderBase.cpp Thu Nov 16 13:46:03 2017 +0100 @@ -0,0 +1,40 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public 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 "VolumeLoaderBase.h" + +namespace OrthancStone +{ + void VolumeLoaderBase::NotifyGeometryReady() + { + observers_.Apply(*this, &IObserver::NotifyGeometryReady); + } + + void VolumeLoaderBase::NotifyGeometryError() + { + observers_.Apply(*this, &IObserver::NotifyGeometryError); + } + + void VolumeLoaderBase::NotifyContentChange() + { + observers_.Apply(*this, &IObserver::NotifyContentChange); + } +} diff -r a823122db53d -r 1982d6c1d2ff Framework/Volumes/VolumeLoaderBase.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/VolumeLoaderBase.h Thu Nov 16 13:46:03 2017 +0100 @@ -0,0 +1,49 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "IVolumeLoader.h" +#include "../Toolbox/ObserversRegistry.h" + +namespace OrthancStone +{ + class VolumeLoaderBase : public IVolumeLoader + { + private: + typedef ObserversRegistry Observers; + + Observers observers_; + + protected: + virtual void NotifyGeometryReady(); + + virtual void NotifyGeometryError(); + + virtual void NotifyContentChange(); + + public: + virtual void Register(IObserver& observer) + { + observers_.Register(observer); + } + }; +} diff -r a823122db53d -r 1982d6c1d2ff Resources/CMake/OrthancStoneConfiguration.cmake --- a/Resources/CMake/OrthancStoneConfiguration.cmake Thu Nov 16 12:50:22 2017 +0100 +++ b/Resources/CMake/OrthancStoneConfiguration.cmake Thu Nov 16 13:46:03 2017 +0100 @@ -194,6 +194,8 @@ ${ORTHANC_STONE_DIR}/Framework/Viewport/WidgetViewport.cpp ${ORTHANC_STONE_DIR}/Framework/Volumes/ImageBuffer3D.cpp ${ORTHANC_STONE_DIR}/Framework/Volumes/SlicedVolumeBase.cpp + ${ORTHANC_STONE_DIR}/Framework/Volumes/VolumeLoaderBase.cpp + ${ORTHANC_STONE_DIR}/Framework/Volumes/StructureSetLoader.cpp ${ORTHANC_STONE_DIR}/Framework/Widgets/CairoWidget.cpp ${ORTHANC_STONE_DIR}/Framework/Widgets/EmptyWidget.cpp ${ORTHANC_STONE_DIR}/Framework/Widgets/LayerWidget.cpp