diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index 2950c63e78..815b75450a 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,316 +1,321 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkCoreActivator.cpp mitkCoreObjectFactoryBase.cpp mitkCoreObjectFactory.cpp mitkCoreServices.cpp mitkException.cpp Algorithms/mitkBaseDataSource.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkCompareImageDataFilter.cpp Algorithms/mitkCompositePixelValueToString.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkExtractSliceFilter2.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkAnatomicalStructureColorPresets.cpp DataManagement/mitkArbitraryTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataStorage.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGeometryTransformHolder.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkGenericIDRelationRule.cpp DataManagement/mitkIdentifiable.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageReadAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageVtkReadAccessor.cpp DataManagement/mitkImageVtkWriteAccessor.cpp DataManagement/mitkImageWriteAccessor.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkIPersistenceService.cpp DataManagement/mitkIPropertyAliases.cpp DataManagement/mitkIPropertyDescriptions.cpp DataManagement/mitkIPropertyExtensions.cpp DataManagement/mitkIPropertyFilters.cpp DataManagement/mitkIPropertyOwner.cpp DataManagement/mitkIPropertyPersistence.cpp DataManagement/mitkIPropertyProvider.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLine.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTableProperty.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMaterial.cpp DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModifiedLock.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDataUID.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateFunction.cpp DataManagement/mitkNodePredicateGeometry.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateDataProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkNodePredicateUID.cpp DataManagement/mitkNumericConstants.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneGeometryData.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkPropertyKeyPath.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyListReplacedObserver.cpp DataManagement/mitkPropertyNameHelper.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkPropertyPersistence.cpp DataManagement/mitkPropertyPersistenceInfo.cpp DataManagement/mitkPropertyRelationRuleBase.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkScaleOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTemporoSpatialStringProperty.cpp DataManagement/mitkUIDManipulator.cpp DataManagement/mitkVector.cpp DataManagement/mitkVectorProperty.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkIPropertyRelations.cpp DataManagement/mitkPropertyRelations.cpp Interactions/mitkAction.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCrosshairPositionEvent.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp + Interactions/mitkDisplayActionEventBroadcast.cpp + Interactions/mitkDisplayActionEventFunctions.cpp + Interactions/mitkDisplayActionEventHandler.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventFactory.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkInteractionEventObserver.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkInteractionPositionEvent.cpp + Interactions/mitkInteractionSchemeSwitcher.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseModeSwitcher.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkSinglePointDataInteractor.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp + Interactions/mitkStdDisplayActionEventHandler.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkXML2EventParser.cpp IO/mitkAbstractFileIO.cpp IO/mitkAbstractFileReader.cpp IO/mitkAbstractFileWriter.cpp IO/mitkCustomMimeType.cpp IO/mitkFileReader.cpp IO/mitkFileReaderRegistry.cpp IO/mitkFileReaderSelector.cpp IO/mitkFileReaderWriterBase.cpp IO/mitkFileWriter.cpp IO/mitkFileWriterRegistry.cpp IO/mitkFileWriterSelector.cpp IO/mitkGeometry3DToXML.cpp IO/mitkIFileIO.cpp IO/mitkIFileReader.cpp IO/mitkIFileWriter.cpp IO/mitkGeometryDataReaderService.cpp IO/mitkGeometryDataWriterService.cpp IO/mitkImageGenerator.cpp IO/mitkImageVtkLegacyIO.cpp IO/mitkImageVtkXmlIO.cpp IO/mitkIMimeTypeProvider.cpp IO/mitkIOConstants.cpp IO/mitkIOMimeTypes.cpp IO/mitkIOUtil.cpp IO/mitkItkImageIO.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkLegacyFileReaderService.cpp IO/mitkLegacyFileWriterService.cpp IO/mitkLocaleSwitch.cpp IO/mitkLog.cpp IO/mitkMimeType.cpp IO/mitkMimeTypeProvider.cpp IO/mitkOperation.cpp IO/mitkPixelType.cpp IO/mitkPointSetReaderService.cpp IO/mitkPointSetWriterService.cpp IO/mitkProportionalTimeGeometryToXML.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSurfaceStlIO.cpp IO/mitkSurfaceVtkIO.cpp IO/mitkSurfaceVtkLegacyIO.cpp IO/mitkSurfaceVtkXmlIO.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkPreferenceListReaderOptionsFunctor.cpp Rendering/mitkAbstractAnnotationRenderer.cpp Rendering/mitkAnnotationUtils.cpp Rendering/mitkBaseRenderer.cpp #Rendering/mitkGLMapper.cpp Moved to deprecated LegacyGL Module Rendering/mitkGradientBackground.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/mitkMapper.cpp Rendering/mitkAnnotation.cpp Rendering/mitkPlaneGeometryDataMapper2D.cpp Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowFrame.cpp #Rendering/mitkSurfaceGLMapper2D.cpp Moved to deprecated LegacyGL Module Rendering/mitkSurfaceVtkMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfig.xml Interactions/DisplayConfigPACS.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigMITK.xml Interactions/DisplayConfigMITKNoCrosshair.xml Interactions/DisplayConfigMITKRotation.xml Interactions/DisplayConfigMITKRotationUnCoupled.xml Interactions/DisplayConfigMITKSwivel.xml Interactions/DisplayConfigMITKLimited.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml mitkAnatomicalStructureColorPresets.xml ) diff --git a/Modules/Core/include/mitkDataStorage.h b/Modules/Core/include/mitkDataStorage.h index 80eaf630c2..307476fa51 100644 --- a/Modules/Core/include/mitkDataStorage.h +++ b/Modules/Core/include/mitkDataStorage.h @@ -1,448 +1,456 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKDATASTORAGE_H #define MITKDATASTORAGE_H #include "itkObject.h" #include "itkSimpleFastMutexLock.h" #include "itkVectorContainer.h" #include "mitkDataNode.h" #include "mitkGeometry3D.h" #include "mitkMessage.h" #include #include namespace mitk { class NodePredicateBase; class DataNode; class BaseRenderer; //##Documentation //## @brief Data management class that handles 'was created by' relations //## //## The DataStorage provides data storage and management functionality. //## It handles a 'was created by' relation by associating each data object with a //## set of source objects, that this object was created from. //## Thus, nodes are stored in a noncyclical directed graph data structure. //## If a new node is added to the DataStorage, AddNodeEvent is emitted. //## If a node is removed, RemoveNodeEvent is emitted. //## //## //## \ingroup DataStorage class MITKCORE_EXPORT DataStorage : public itk::Object { public: mitkClassMacroItkParent(DataStorage, itk::Object); //##Documentation //## @brief A Container of objects that is used as a result set of GetSubset() query operations (Set of //SmartPointers // to DataNodes). typedef itk::VectorContainer SetOfObjects; //##Documentation //## @brief Adds a DataNode containing a data object to its internal storage //## //## This Method adds a new data object to the DataStorage. The new object is //## passed in the first parameter. The second parameter is a set //## of source objects, that were used to create this object. The new object will have //## a 'was created from' relation to its source objects. //## the addition of a new object will fire the notification mechanism. //## If the node parameter is nullptr or if the DataNode has already been added, //## an exception will be thrown. virtual void Add(DataNode *node, const DataStorage::SetOfObjects *parents = nullptr) = 0; //##Documentation //## @brief Convenience method to add a node that has one parent //## void Add(DataNode *node, DataNode *parent); //##Documentation //## @brief Removes node from the DataStorage //## virtual void Remove(const DataNode *node) = 0; //##Documentation //## @brief Checks if a node exists in the DataStorage //## virtual bool Exists(const DataNode *node) const = 0; //##Documentation //## @brief Removes a set of nodes from the DataStorage //## void Remove(const DataStorage::SetOfObjects *nodes); //##Documentation //## @brief returns a set of data objects that meet the given condition(s) //## //## GetSubset returns a set of objects with a specific data type that meet the condition(s) //## specified in the condition parameter. Conditions can be //## - data type of the data object //## - is source object of specific object (e.g. all source objects of node x) //## - has property with specific value (e.g. OrganType is Liver) //## - negation of any condition //## - conjunction of a set of conditions //## - disjunction of a set of conditions //## Conditions are implemented as predicates using the Composite Design Pattern //## (see definition of NodePredicateBase for details). //## The method returns a set of SmartPointers to the DataNodes that fulfill the //## conditions. A set of all objects can be retrieved with the GetAll() method; SetOfObjects::ConstPointer GetSubset(const NodePredicateBase *condition) const; + //##Documentation + //## @brief returns the topmost node of the set of nodes, meeting special property conditions + //## + //## GetTopLayerNode returns a node that is visible and has the highest layer of a set of given nodes. + //## The property list, which is used to find the visibility- and layer-property is is specified by the + //## given base renderer. + mitk::DataNode::Pointer GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes, const mitk::Point3D worldPosition, const mitk::BaseRenderer* renderer); + //##Documentation //## @brief returns a set of source objects for a given node that meet the given condition(s). //## virtual SetOfObjects::ConstPointer GetSources(const DataNode *node, const NodePredicateBase *condition = nullptr, bool onlyDirectSources = true) const = 0; //##Documentation //## @brief returns a set of derived objects for a given node. //## //## GetDerivations() returns a set of objects that are derived from the DataNode node. //## This means, that node was used to create the returned objects. If the parameter //## onlyDirectDerivations is set to true (default value), only objects that directly have //## node as one of their source objects will be returned. Otherwise, objects that are //## derived from derivations of node are returned too. //## The derived objects can be filtered with a predicate object as described in the GetSubset() //## method by providing a predicate as the condition parameter. virtual SetOfObjects::ConstPointer GetDerivations(const DataNode *node, const NodePredicateBase *condition = nullptr, bool onlyDirectDerivations = true) const = 0; //##Documentation //## @brief returns a set of all data objects that are stored in the data storage //## virtual SetOfObjects::ConstPointer GetAll() const = 0; //##Documentation //## @brief Convenience method to get the first node that matches the predicate condition //## DataNode *GetNode(const NodePredicateBase *condition = nullptr) const; //##Documentation //## @brief Convenience method to get the first node with a given name //## DataNode *GetNamedNode(const char *name) const; //##Documentation //## @brief Convenience method to get the first node with a given name //## DataNode *GetNamedNode(const std::string name) const { return this->GetNamedNode(name.c_str()); } //##Documentation //## @brief Convenience method to get the first node with a given name that is derived from sourceNode //## DataNode *GetNamedDerivedNode(const char *name, const DataNode *sourceNode, bool onlyDirectDerivations = true) const; //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name //## template DataType *GetNamedObject(const char *name) const { if (name == nullptr) return nullptr; DataNode *n = this->GetNamedNode(name); if (n == nullptr) return nullptr; else return dynamic_cast(n->GetData()); } //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name //## template DataType *GetNamedObject(const std::string name) const { return this->GetNamedObject(name.c_str()); } //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name that is derived // from a specific node //## template DataType *GetNamedDerivedObject(const char *name, const DataNode *sourceNode, bool onlyDirectDerivations = true) const { if (name == nullptr) return nullptr; DataNode *n = this->GetNamedDerivedNode(name, sourceNode, onlyDirectDerivations); if (n == nullptr) return nullptr; else return dynamic_cast(n->GetData()); } //##Documentation //## @brief Returns a list of used grouptags //## const DataNode::GroupTagList GetGroupTags() const; /*ITK Mutex */ mutable itk::SimpleFastMutexLock m_MutexOne; /* Public Events */ typedef Message1 DataStorageEvent; //##Documentation //## @brief AddEvent is emitted whenever a new node has been added to the DataStorage. //## //## Observers should register to this event by calling myDataStorage->AddNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been added to the DataStorage. //## Observers should unregister by calling myDataStorage->AddNodeEvent.RemoveListener(myObject, //MyObject::MyMethod). //## Note: AddEvents are _not_ emitted if a node is added to DataStorage by adding it to the the underlying //DataTree! // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent AddNodeEvent; //##Documentation //## @brief RemoveEvent is emitted directly before a node is removed from the DataStorage. //## //## Observers should register to this event by calling myDataStorage->RemoveNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been added to the DataStorage. //## Observers should unregister by calling myDataStorage->RemoveNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Note: RemoveEvents are also emitted if a node was removed from the DataStorage by deleting it from the //underlying // DataTree // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent RemoveNodeEvent; //##Documentation //## @brief ChangedEvent is emitted directly after a node was changed. //## //## Observers should register to this event by calling myDataStorage->ChangedNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been changed. //## Observers should unregister by calling myDataStorage->ChangedNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Internally the DataStorage listens to itk::ModifiedEvents on the nodes and forwards them //## to the listeners of this event. // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent ChangedNodeEvent; //##Documentation //## @brief DeleteNodeEvent is emitted directly before a node is deleted. //## //## Observers should register to this event by calling myDataStorage->DeleteNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called when a node is deleted. //## Observers should unregister by calling myDataStorage->DeleteNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Internally the DataStorage listens to itk::DeleteEvents on the nodes and forwards them //## to the listeners of this event. // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent DeleteNodeEvent; DataStorageEvent InteractorChangedNodeEvent; //##Documentation //## @brief Compute the axis-parallel bounding geometry of the input objects //## //## Throws std::invalid_argument exception if input is nullptr //## @param input set of objects of the DataStorage to be included in the bounding geometry //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey TimeGeometry::ConstPointer ComputeBoundingGeometry3D(const SetOfObjects *input, const char *boolPropertyKey = nullptr, const BaseRenderer *renderer = nullptr, const char *boolPropertyKey2 = nullptr) const; //##Documentation //## @brief Compute the axis-parallel bounding geometry of the data tree //## (bounding box, minimal spacing of the considered nodes, live-span) //## //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey TimeGeometry::ConstPointer ComputeBoundingGeometry3D(const char *boolPropertyKey = nullptr, const BaseRenderer *renderer = nullptr, const char *boolPropertyKey2 = nullptr) const; //##Documentation //## @brief Compute the axis-parallel bounding geometry of all visible parts of the //## data tree bounding box, minimal spacing of the considered nodes, live-span) //## //## Simply calls ComputeBoundingGeometry3D(it, "visible", renderer, boolPropertyKey). //## it -> an iterator of a data tree structure //## @param renderer the reference to the renderer //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. TimeGeometry::ConstPointer ComputeVisibleBoundingGeometry3D(const BaseRenderer *renderer = nullptr, const char *boolPropertyKey = nullptr); //##Documentation //## @brief Compute the bounding box of data tree structure //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey BoundingBox::Pointer ComputeBoundingBox(const char *boolPropertyKey = nullptr, const BaseRenderer *renderer = nullptr, const char *boolPropertyKey2 = nullptr); //##Documentation //## \brief Compute the bounding box of all visible parts of the data tree structure, for general //## rendering or renderer specific visibility property checking //## //## Simply calls ComputeBoundingBox(it, "visible", renderer, boolPropertyKey). //## it -> an iterator of a data tree structure //## @param renderer the reference to the renderer //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. BoundingBox::Pointer ComputeVisibleBoundingBox(const BaseRenderer *renderer = nullptr, const char *boolPropertyKey = nullptr) { return ComputeBoundingBox("visible", renderer, boolPropertyKey); } //##Documentation //## @brief Compute the time-bounds of the contents of a data tree structure //## //## The methods returns only [-infinity, +infinity], if all data-objects have an infinite live-span. Otherwise, //## all data-objects with infinite live-span are ignored. //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the time-bounds calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey TimeBounds ComputeTimeBounds(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2); //##Documentation //## @brief Compute the time-bounds of all visible parts of the data tree structure, for general //## rendering or renderer specific visibility property checking //## //## The methods returns only [-infinity, +infinity], if all data-objects have an infinite live-span. Otherwise, //## all data-objects with infinite live-span are ignored. //## Simply calls ComputeTimeBounds(it, "visible", renderer, boolPropertyKey). //## @param it an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the time-bounds calculation. //## @param renderer see @a boolPropertyKey TimeBounds ComputeTimeBounds(const BaseRenderer *renderer, const char *boolPropertyKey) { return ComputeTimeBounds("visible", renderer, boolPropertyKey); } //##Documentation //## @brief Defines whether or not NodeChangedEvent is invoked . //## //## This method can be used to set m_BlockNodeModifiedEvents. //## //## If this flag is true, NodeChangedEvent is not invoked when a //## DataNode is modified. This might be undesired when setting //## many properties on a datanode and you do not want anyone to //## react. void BlockNodeModifiedEvents(bool block); protected: //##Documentation //## @brief EmitAddNodeEvent emits the AddNodeEvent //## //## This method should be called by subclasses to emit the AddNodeEvent void EmitAddNodeEvent(const DataNode *node); //##Documentation //## @brief EmitRemoveNodeEvent emits the RemoveNodeEvent //## //## This method should be called by subclasses to emit the RemoveNodeEvent void EmitRemoveNodeEvent(const DataNode *node); void OnNodeInteractorChanged(itk::Object *caller, const itk::EventObject &event); //##Documentation //## @brief OnNodeModified listens to modified events of DataNodes. //## //## The node is hidden behind the caller parameter, which has to be casted first. //## If the cast succeeds the ChangedNodeEvent is emitted with this node. void OnNodeModifiedOrDeleted(const itk::Object *caller, const itk::EventObject &event); //##Documentation //## @brief Adds a Modified-Listener to the given Node. void AddListeners(const DataNode *_Node); //##Documentation //## @brief Removes a Modified-Listener from the given Node. void RemoveListeners(const DataNode *_Node); //##Documentation //## @brief Saves Modified-Observer Tags for each node in order to remove the event listeners again. std::map m_NodeModifiedObserverTags; std::map m_NodeInteractorChangedObserverTags; //##Documentation //## @brief Saves Delete-Observer Tags for each node in order to remove the event listeners again. std::map m_NodeDeleteObserverTags; //##Documentation //## @brief If this class changes nodes itself, set this to TRUE in order //## to suppress NodeChangedEvent to be emitted. bool m_BlockNodeModifiedEvents; //##Documentation //## @brief Standard Constructor for ::New() instantiation DataStorage(); //##Documentation //## @brief Standard Destructor ~DataStorage() override; //##Documentation //## @brief Filters a SetOfObjects by the condition. If no condition is provided, the original set is returned SetOfObjects::ConstPointer FilterSetOfObjects(const SetOfObjects *set, const NodePredicateBase *condition) const; //##Documentation //## @brief Prints the contents of the DataStorage to os. Do not call directly, call ->Print() instead void PrintSelf(std::ostream &os, itk::Indent indent) const override; }; //##Documentation //## @brief returns the topmost visible node of a given list of nodes. //## MITKCORE_EXPORT DataNode::Pointer FindTopmostVisibleNode(const DataStorage::SetOfObjects* nodes, const Point3D worldposition, const TimePointType timePoint, const BaseRenderer* baseRender); } // namespace mitk #endif // MITKDATASTORAGE_H diff --git a/Modules/Core/include/mitkDisplayActionEventBroadcast.h b/Modules/Core/include/mitkDisplayActionEventBroadcast.h new file mode 100644 index 0000000000..421421f0de --- /dev/null +++ b/Modules/Core/include/mitkDisplayActionEventBroadcast.h @@ -0,0 +1,217 @@ +/*=================================================================== + + The Medical Imaging Interaction Toolkit (MITK) + + Copyright (c) German Cancer Research Center, + Division of Medical Image Computing. + All rights reserved. + + This software is distributed WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. + + See LICENSE.txt or http://www.mitk.org for details. + + ===================================================================*/ + +#ifndef MITKDISPLAYACTIONEVENTBROADCAST_H +#define MITKDISPLAYACTIONEVENTBROADCAST_H + +#include "mitkInteractionEventObserver.h" +#include + +namespace mitk +{ + /** + * @brief This class serves as an event state machine while simultaneously observing interaction events. + * It connects the actions from the event state machine .xml-file with concrete functions of this class. + */ + class MITKCORE_EXPORT DisplayActionEventBroadcast : public EventStateMachine, public InteractionEventObserver + { + public: + mitkClassMacro(DisplayActionEventBroadcast, EventStateMachine) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + /** + * By this function this observer is notified about about every 'InteractionEvent'. + * The interaction event is passed to the state machine in order to use its infrastructure. + * For more information see @see InteractionEventObserver. + * + * @par interactionEvent The event that was observed and triggered this notification. + * @par isHandled Flag that indicates if a 'DataInteractor' has already handled the event. + */ + virtual void Notify(InteractionEvent* interactionEvent, bool isHandled) override; + + protected: + + DisplayActionEventBroadcast(); + virtual ~DisplayActionEventBroadcast() override; + + /** + * @brief Connects the action names used in the state machine pattern with functions implemented within this InteractionEventObserver. + */ + void ConnectActionsAndFunctions() override; + /** + * @brief This function is executed when a config object is set / changed (via 'SetEventConfig' or 'AddEventConfig' in 'InteractionEventObserver'). + * It is used to read out the parameters set in the configuration file and to set the member variables accordingly. + */ + virtual void ConfigurationChanged() override; + /** + * @brief Filters the event resp. the sender of the event. + * + * @par interactionEvent The event whose sender has to be checked + * @par data node The data node is ignored in this specific implementation. + * + * @return True, if the sender of the event is a valid sender and the sending renderer is a 2D-renderer. False, if not. + */ + virtual bool FilterEvents(InteractionEvent* interactionEvent, DataNode* dataNode) override; + + ////////////////////////////////////////////////////////////////////////// + // Functions to react to interaction events (actions) + ////////////////////////////////////////////////////////////////////////// + /** + * @brief Check if the given interaction event is actually an 'InteractionPositionEvent'. + * + * @par interactionEvent The interaction event that is checked. + * + * @return True, if the given event can be dynamically cast to an 'InteractionPositionEvent'. False, if not. + */ + bool CheckPositionEvent(const InteractionEvent* interactionEvent); + + bool CheckRotationPossible(const InteractionEvent* interactionEvent); + + bool CheckSwivelPossible(const InteractionEvent* interactionEvent); + + void Init(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); + + void Move(StateMachineAction* stateMachineAction , InteractionEvent* interactionEvent); + + void SetCrosshair(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); + + void Zoom(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); + + void Scroll(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); + + void ScrollOneUp(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); + + void ScrollOneDown(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); + + void AdjustLevelWindow(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); + + void StartRotation(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); + + void EndRotation(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); + + void Rotate(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); + + void Swivel(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); + + private: + + void UpdateStatusbar(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent); + + bool GetBoolProperty(mitk::PropertyList::Pointer propertyList, const char* propertyName, bool defaultValue); + + /** + * @brief Reference to the service registration of the observer. + * This is needed to unregister the observer on unload. + */ + us::ServiceRegistration m_ServiceRegistration; + + /** + * @brief Determines if this broadcast class reacts to events that already have been processed by a DataInteractor. + * The default value is false. + */ + bool m_AlwaysReact; + + /** + * @brief Coordinate of the mouse pointer at beginning of an interaction (translated to mm unit). + */ + mitk::Point2D m_StartCoordinateInMM; + /** + * @brief Coordinate of the mouse pointer in the last step within an interaction. + */ + mitk::Point2D m_LastDisplayCoordinate; + /** + * @brief Coordinate of the mouse pointer in the last step within an interaction (translated to mm unit). + */ + mitk::Point2D m_LastCoordinateInMM; + /** + * \brief Current coordinates of the pointer. + */ + mitk::Point2D m_CurrentDisplayCoordinate; + + /** + * @brief Defines the behavior at the end of a data set. + * If set to true, it will restart at end of data set from the beginning. + */ + bool m_AutoRepeat; + /** + * @brief Defines how many slices are scrolled per pixel that the mouse pointer was moved. + * By default the modifier is 4. This means that when the user moves the cursor by 4 pixels in Y-direction, + * the scene is scrolled by one slice. If the user has moved the the cursor by 20 pixels, the scene is + * scrolled by 5 slices. + * + * If the cursor has moved less than m_IndexToSliceModifier pixels, the scene is scrolled by one slice. + */ + int m_IndexToSliceModifier; + /** + * @brief Defines the scroll behavior. + * Default is up/down movement of pointer performs scrolling + */ + std::string m_ScrollDirection; + /** + * @brief Defines how the axis of interaction influences scroll behavior. + */ + bool m_InvertScrollDirection; + /** + * @brief Defines the zoom behavior. + * Default is up/down movement of pointer performs zooming + */ + std::string m_ZoomDirection; + /** + * @brief Defines how the axis of interaction influences zoom behavior. + */ + bool m_InvertZoomDirection; + /** + * @brief Factor to adjust zooming speed. + */ + float m_ZoomFactor; + /** + * @brief Defines how the axis of interaction influences move behavior. + */ + bool m_InvertMoveDirection; + /** + * @brief Defines the level-window behavior. + * Default is left/right movement of pointer modifies the level. + */ + std::string m_LevelDirection; + /** + * @brief Defines how the axis of interaction influences level-window behavior. + */ + bool m_InvertLevelWindowDirection; + /** + * @brief Determines if the angle between crosshair remains fixed when rotating. + */ + bool m_LinkPlanes; + + typedef std::vector SNCVector; + SNCVector m_RotatableSNCs; + SNCVector m_SNCsToBeRotated; + + Point3D m_LastCursorPosition; + Point3D m_CenterOfRotation; + + Point2D m_ReferenceCursor; + + Vector3D m_RotationPlaneNormal; + Vector3D m_RotationPlaneXVector; + Vector3D m_RotationPlaneYVector; + + Vector3D m_PreviousRotationAxis; + ScalarType m_PreviousRotationAngle; + }; +} // end namespace + +#endif // MITKDISPLAYACTIONEVENTBROADCAST_H diff --git a/Modules/Core/include/mitkDisplayActionEventFunctions.h b/Modules/Core/include/mitkDisplayActionEventFunctions.h new file mode 100644 index 0000000000..519d022c6e --- /dev/null +++ b/Modules/Core/include/mitkDisplayActionEventFunctions.h @@ -0,0 +1,90 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKDISPLAYACTIONEVENTFUNCTIONS_H +#define MITKDISPLAYACTIONEVENTFUNCTIONS_H + +#include + +#include "mitkStdFunctionCommand.h" + +namespace mitk +{ + namespace DisplayActionEventFunctions + { + /** + * @brief Returns an 'std::function' that can be used to react on the 'DisplayMoveEvent'. + * The function performs a move of the camera controller of the sending renderer by a vector + * that was previously determined by the mouse interaction event. + */ + MITKCORE_EXPORT StdFunctionCommand::ActionFunction MoveSenderCameraAction(); + /** + * @brief Returns an 'std::function' that can be used to react on the 'DisplaySetCrosshairEvent'. + * The function performs a slice selection of the slice navigation controller and will set + * the cross hair for all 2D-render windows. + * The new position was previously determined by the mouse interaction event. + */ + MITKCORE_EXPORT StdFunctionCommand::ActionFunction SetCrosshairAction(); + /** + * @brief Returns an 'std::function' that can be used to react on the 'DisplayZoomEvent'. + * The function performs a zoom of the camera controller of the sending renderer by a zoom factor + * that was previously determined by the mouse interaction event. + */ + MITKCORE_EXPORT StdFunctionCommand::ActionFunction ZoomSenderCameraAction(); + /** + * @brief Returns an 'std::function' that can be used to react on the 'DisplayScrollEvent'. + * The function performs a slice scrolling of the slice navigation controller of the sending renderer. + * The new position was previously determined by the mouse interaction event. + */ + MITKCORE_EXPORT StdFunctionCommand::ActionFunction ScrollSliceStepperAction(); + /** + * @brief Returns an 'std::function' that can be used to react on the 'DisplaySetLevelWindowEvent'. + * The function sets the 'levelwindow' property of the topmost visible image that is display by the sending renderer. + * The level and window value for this property were previously determined by the mouse interaction event. + */ + MITKCORE_EXPORT StdFunctionCommand::ActionFunction SetLevelWindowAction(); + /** + * @brief Returns an 'std::function' that can be used to react on the 'DisplayMoveEvent'. + * The function performs a move of the camera controller of all renderer (synchronized) + * by a vector that was previously determined by the mouse interaction event. + * The renderer need to be managed by the same rendering manager. + */ + MITKCORE_EXPORT StdFunctionCommand::ActionFunction MoveCameraSynchronizedAction(); + /** + * @brief Returns an 'std::function' that can be used to react on the 'DisplaySetCrosshairEvent'. + * The function performs a slice selection of the slice navigation controller and will set + * the cross hair for all 2D-render windows. + * The new position was previously determined by the mouse interaction event. + * #TODO: currently there is no need to distinguish between this and the non-synchronized version + */ + MITKCORE_EXPORT StdFunctionCommand::ActionFunction SetCrosshairSynchronizedAction(); + /** + * @brief Returns an 'std::function' that can be used to react on the 'DisplayZoomEvent'. + * The function performs a zoom of the camera controller of all 2D-renderer (synchronized) + * by a zoom factor that was previously determined by the mouse interaction event. + */ + MITKCORE_EXPORT StdFunctionCommand::ActionFunction ZoomCameraSynchronizedAction(); + /** + * @brief Returns an 'std::function' that can be used to react on the 'DisplayScrollEvent'. + * The function performs a slice scrolling of the slice navigation controller of all 2D-renderer (synchronized). + * The new position was previously determined by the mouse interaction event. + */ + MITKCORE_EXPORT StdFunctionCommand::ActionFunction ScrollSliceStepperSynchronizedAction(); + + } // end namespace DisplayActionEventFunctions +} // end namespace mitk + +#endif // MITKDISPLAYACTIONEVENTFUNCTIONS_H diff --git a/Modules/Core/include/mitkDisplayActionEventHandler.h b/Modules/Core/include/mitkDisplayActionEventHandler.h new file mode 100644 index 0000000000..de83a934cc --- /dev/null +++ b/Modules/Core/include/mitkDisplayActionEventHandler.h @@ -0,0 +1,91 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKDISPLAYACTIONEVENTHANDLER_H +#define MITKDISPLAYACTIONEVENTHANDLER_H + +#include + +// mitk core +#include "mitkDisplayActionEventBroadcast.h" +#include "mitkDisplayActionEvents.h" +#include "mitkStdFunctionCommand.h" + +namespace mitk +{ + class MITKCORE_EXPORT DisplayActionEventHandler + { + public: + + using OberserverTagType = unsigned long; + + /** + * @brief Sets the display action event broadcast class that should be observed. + * This class receives events from the given broadcast class and triggers the "corresponding functions" to perform the custom actions. + * "Corresponding functions" are std::functions inside commands that observe the specific display action event. + * + * @post If the same broadcast class was already set, nothing changed + * @post If a different broadcast class was already set, the observing commands are removed as observer. + * Attention: All registered commands are removed from the list of observer. + * + * @par observableBroadcast The 'DisplayActionEventBroadcast'-class that should be observed. + */ + void SetObservableBroadcast(mitk::DisplayActionEventBroadcast* observableBroadcast); + + /** + * @brief Uses the given std::functions to customize a command: + * The display action event is used to define on which event the command should react. + * The display action event broadcast class member is then observed by the newly created command. + * A tag for the command is returned and stored in a member vector. + * + * @pre The class' observable (the display action event broadcast) has to be set to connect display events. + * @throw mitk::Exception, if the class' observable is null. + * + * @par displayActionEvent The 'DisplayActionEvent' on which the command should react. + * @par actionFunction A custom std::Function that will be executed if the command receives the correct event. + * @par filterFunction A custom std::Function that will be checked before the execution of the action function. + * If the filter function is not specified, a default filter always returning 'true' will be used. + * + * @return A tag to identify, receive or remove the newly created 'StdFunctionCommand'. + */ + OberserverTagType ConnectDisplayActionEvent(const mitk::DisplayActionEvent& displayActionEvent, + const mitk::StdFunctionCommand::ActionFunction& actionFunction, + const mitk::StdFunctionCommand::FilterFunction& filterFunction = [](const itk::EventObject& eventObject) { return true; }); + + /** + * @brief Uses the given observer tag to remove the corresponding custom command as an observer of the observed + * display action event broadcast class. + * If the given tag is not contained in the member vector of observer tags, nothing happens. + * + * @pre The class' observable (the display action event broadcast) has to be set to connect display events. + * @throw mitk::Exception, if the class' observable is null. + * + * @par observerTag The tag to identify the 'StdFunctionCommand' observer. + */ + void DisconnectObserver(OberserverTagType observerTag); + + const std::vector& GetAllObserverTags() { return m_ObserverTags; }; + + protected: + + mitk::WeakPointer m_ObservableBroadcast; + std::vector m_ObserverTags; + + }; + +} // end namespace mitk + +#endif // MITKDISPLAYACTIONEVENTHANDLER_H diff --git a/Modules/Core/include/mitkDisplayActionEvents.h b/Modules/Core/include/mitkDisplayActionEvents.h new file mode 100644 index 0000000000..f56823720c --- /dev/null +++ b/Modules/Core/include/mitkDisplayActionEvents.h @@ -0,0 +1,188 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKDISPLAYACTIONEVENTS_H +#define MITKDISPLAYACTIONEVENTS_H + +#include + +// mitk core +#include "mitkInteractionEvent.h" + +// itk +#include + +#include +#include + +namespace mitk +{ + class MITKCORE_EXPORT DisplayActionEvent : public itk::AnyEvent + { + public: + typedef DisplayActionEvent Self; + typedef itk::AnyEvent Superclass; + + DisplayActionEvent() : m_InteractionEvent(nullptr) {} + DisplayActionEvent(InteractionEvent* interactionEvent) : m_InteractionEvent(interactionEvent) {} + virtual ~DisplayActionEvent() {} + virtual const char* GetEventName() const override { return "DisplayActionEvent"; } + virtual bool CheckEvent(const itk::EventObject* e) const override + { return dynamic_cast(e) != nullptr; } + virtual itk::EventObject* MakeObject() const override { return new Self(m_InteractionEvent); } + InteractionEvent* GetInteractionEvent() const { return m_InteractionEvent; } + BaseRenderer* GetSender() const + { + return m_InteractionEvent != nullptr ? m_InteractionEvent->GetSender() : nullptr; + } + DisplayActionEvent(const Self& s) : Superclass(s), m_InteractionEvent(s.GetInteractionEvent()) {}; + + private: + InteractionEvent* m_InteractionEvent; + void operator=(const Self &); + }; + + class MITKCORE_EXPORT DisplayMoveEvent : public DisplayActionEvent + { + public: + typedef DisplayMoveEvent Self; + typedef DisplayActionEvent Superclass; + + DisplayMoveEvent() : Superclass() {} + DisplayMoveEvent(InteractionEvent* interactionEvent, const Vector2D& moveVector) + : Superclass(interactionEvent) + , m_MoveVector(moveVector) + { + } + virtual ~DisplayMoveEvent() {} + virtual const char* GetEventName() const override { return "DisplayMoveEvent"; } + virtual bool CheckEvent(const itk::EventObject* e) const override + { return dynamic_cast(e) != nullptr; } + virtual itk::EventObject* MakeObject() const override { return new Self(GetInteractionEvent(), m_MoveVector); } + const Vector2D& GetMoveVector() const { return m_MoveVector; } + DisplayMoveEvent(const Self& s) : Superclass(s), m_MoveVector(s.GetMoveVector()) {}; + + private: + Vector2D m_MoveVector; + }; + + class MITKCORE_EXPORT DisplaySetCrosshairEvent : public DisplayActionEvent + { + public: + typedef DisplaySetCrosshairEvent Self; + typedef DisplayActionEvent Superclass; + + DisplaySetCrosshairEvent() : Superclass() {} + DisplaySetCrosshairEvent(InteractionEvent* interactionEvent, const Point3D& position) + : Superclass(interactionEvent) + , m_Position(position) + { + } + virtual ~DisplaySetCrosshairEvent() {} + virtual const char* GetEventName() const override { return "DisplaySetCrosshairEvent"; } + virtual bool CheckEvent(const itk::EventObject* e) const override + { return dynamic_cast(e) != nullptr; } + virtual itk::EventObject* MakeObject() const override { return new Self(GetInteractionEvent(), m_Position); } + const Point3D& GetPosition() const { return m_Position; } + DisplaySetCrosshairEvent(const Self& s) : Superclass(s), m_Position(s.GetPosition()) {}; + + private: + Point3D m_Position; + }; + + class MITKCORE_EXPORT DisplayZoomEvent : public DisplayActionEvent + { + public: + typedef DisplayZoomEvent Self; + typedef DisplayActionEvent Superclass; + + DisplayZoomEvent() : Superclass() {} + DisplayZoomEvent(InteractionEvent* interactionEvent, float zoomFactor, const Point2D& startCoordinate) + : Superclass(interactionEvent) + , m_ZoomFactor(zoomFactor) + , m_StartCoordinate(startCoordinate) + { + } + virtual ~DisplayZoomEvent() {} + virtual const char* GetEventName() const override { return "DisplayZoomEvent"; } + virtual bool CheckEvent(const itk::EventObject* e) const override + { return dynamic_cast(e) != nullptr; } + virtual itk::EventObject* MakeObject() const override { return new Self(GetInteractionEvent(), m_ZoomFactor, m_StartCoordinate); } + float GetZoomFactor() const { return m_ZoomFactor; } + const Point2D& GetStartCoordinate() const { return m_StartCoordinate; } + DisplayZoomEvent(const Self& s) : Superclass(s), m_ZoomFactor(s.GetZoomFactor()), m_StartCoordinate(s.GetStartCoordinate()) {}; + + private: + float m_ZoomFactor; + Point2D m_StartCoordinate; + }; + + class MITKCORE_EXPORT DisplayScrollEvent : public DisplayActionEvent + { + public: + typedef DisplayScrollEvent Self; + typedef DisplayActionEvent Superclass; + + DisplayScrollEvent() : Superclass() {} + DisplayScrollEvent(InteractionEvent* interactionEvent, int sliceDelta) + : Superclass(interactionEvent) + , m_SliceDelta(sliceDelta) + { + } + virtual ~DisplayScrollEvent() {} + virtual const char* GetEventName() const override { return "DisplayScrollEvent"; } + virtual bool CheckEvent(const itk::EventObject* e) const override + { return dynamic_cast(e) != nullptr; } + virtual itk::EventObject* MakeObject() const override { return new Self(GetInteractionEvent(), m_SliceDelta); } + int GetSliceDelta() const { return m_SliceDelta; } + DisplayScrollEvent(const Self& s) : Superclass(s), m_SliceDelta(s.GetSliceDelta()) {}; + + private: + int m_SliceDelta; + }; + + class MITKCORE_EXPORT DisplaySetLevelWindowEvent : public DisplayActionEvent + { + public: + typedef DisplaySetLevelWindowEvent Self; + typedef DisplayActionEvent Superclass; + + DisplaySetLevelWindowEvent() : Superclass() {} + DisplaySetLevelWindowEvent(InteractionEvent* interactionEvent, mitk::ScalarType level, mitk::ScalarType window) + : Superclass(interactionEvent) + , m_Level(level) + , m_Window(window) + { + } + virtual ~DisplaySetLevelWindowEvent() {} + virtual const char* GetEventName() const override { return "DisplaySetLevelWindowEvent"; } + virtual bool CheckEvent(const itk::EventObject* e) const override + { + return dynamic_cast(e) != nullptr; + } + virtual itk::EventObject* MakeObject() const override { return new Self(GetInteractionEvent(), m_Level, m_Window); } + mitk::ScalarType GetLevel() const { return m_Level; } + mitk::ScalarType GetWindow() const { return m_Window; } + DisplaySetLevelWindowEvent(const Self& s) : Superclass(s), m_Level(s.GetLevel()), m_Window(s.GetWindow()) {}; + + private: + mitk::ScalarType m_Level; + mitk::ScalarType m_Window; + }; + +} // end namespace + +#endif // MITKDISPLAYACTIONEVENTS_H diff --git a/Modules/Core/include/mitkDisplayInteractor.h b/Modules/Core/include/mitkDisplayInteractor.h index 42cf91b91b..b2fbac200c 100644 --- a/Modules/Core/include/mitkDisplayInteractor.h +++ b/Modules/Core/include/mitkDisplayInteractor.h @@ -1,282 +1,283 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkDisplayInteractor_h #define mitkDisplayInteractor_h #include "mitkInteractionEventObserver.h" #include namespace mitk { /** *\class DisplayInteractor *@brief Observer that manages the interaction with the display. * * This includes the interaction of Zooming, Panning, Scrolling and adjusting the LevelWindow. * * @ingroup Interaction **/ /** * Inherits from mitk::InteractionEventObserver since it doesn't alter any data (only their representation), * and its actions cannot be associated with a DataNode. Also inherits from EventStateMachine */ class MITKCORE_EXPORT DisplayInteractor : public EventStateMachine, public InteractionEventObserver { public: - mitkClassMacro(DisplayInteractor, EventStateMachine) itkFactorylessNewMacro(Self) itkCloneMacro(Self) - /** - * By this function the Observer gets notified about new events. - * Here it is adapted to pass the events to the state machine in order to use - * its infrastructure. - * It also checks if event is to be accepted when it already has been processed by a DataInteractor. - */ - void Notify(InteractionEvent *interactionEvent, bool isHandled) override; + + mitkClassMacro(DisplayInteractor, EventStateMachine) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + /** + * By this function the Observer gets notified about new events. + * Here it is adapted to pass the events to the state machine in order to use + * its infrastructure. + * It also checks if event is to be accepted when it already has been processed by a DataInteractor. + */ + virtual void Notify(InteractionEvent *interactionEvent, bool isHandled) override; protected: + DisplayInteractor(); ~DisplayInteractor() override; /** * Derived function. * Connects the action names used in the state machine pattern with functions implemented within * this InteractionEventObserver. This is only necessary here because the events are processed by the state machine. */ void ConnectActionsAndFunctions() override; /** * Derived function. * Is executed when config object is set / changed. * Here it is used to read out the parameters set in the configuration file, * and set the member variables accordingly. */ void ConfigurationChanged() override; /** * Derived function. * Is executed when config object is set / changed. * Here it is used to read out the parameters set in the configuration file, * and set the member variables accordingly. */ bool FilterEvents(InteractionEvent *interactionEvent, DataNode *dataNode) override; virtual bool CheckPositionEvent(const InteractionEvent *interactionEvent); virtual bool CheckRotationPossible(const InteractionEvent *interactionEvent); virtual bool CheckSwivelPossible(const InteractionEvent *interactionEvent); /** * \brief Initializes an interaction, saves the pointers start position for further reference. */ virtual void Init(StateMachineAction *, InteractionEvent *); /** * \brief Performs panning of the data set in the render window. */ virtual void Move(StateMachineAction *, InteractionEvent *); /** * \brief Sets crosshair at clicked position* */ virtual void SetCrosshair(StateMachineAction *, InteractionEvent *); /** * \brief Increases the time step in 3d+t data */ virtual void IncreaseTimeStep(StateMachineAction *, InteractionEvent *); /** * \brief Decreases the time step in 3d+t data */ virtual void DecreaseTimeStep(StateMachineAction *, InteractionEvent *); /** * \brief Performs zooming relative to mouse/pointer movement. * * Behavior is determined by \see m_ZoomDirection and \see m_ZoomFactor. * */ virtual void Zoom(StateMachineAction *, InteractionEvent *); /** * \brief Performs scrolling relative to mouse/pointer movement. * * Behavior is determined by \see m_ScrollDirection and \see m_AutoRepeat. * */ virtual void Scroll(StateMachineAction *, InteractionEvent *); /** * \brief Scrolls one layer up */ virtual void ScrollOneDown(StateMachineAction *, InteractionEvent *); /** * \brief Scrolls one layer down */ virtual void ScrollOneUp(StateMachineAction *, InteractionEvent *); /** * \brief Adjusts the level windows relative to mouse/pointer movement. */ virtual void AdjustLevelWindow(StateMachineAction *, InteractionEvent *); /** * \brief Starts crosshair rotation */ virtual void StartRotation(StateMachineAction *, InteractionEvent *); /** * \brief Ends crosshair rotation */ virtual void EndRotation(StateMachineAction *, InteractionEvent *); /** * \brief */ virtual void Rotate(StateMachineAction *, InteractionEvent *event); virtual void Swivel(StateMachineAction *, InteractionEvent *event); /** * \brief Updates the Statusbar information with the information about the clicked position */ virtual void UpdateStatusbar(StateMachineAction *, InteractionEvent *event); /** * \brief Method to retrieve bool-value for given property from string-property * in given propertylist. */ bool GetBoolProperty(mitk::PropertyList::Pointer propertyList, const char *propertyName, bool defaultValue); - // Typedefs - typedef std::vector SNCVector; - private: /** * @brief UpdateStatusBar * @param image3D * @param idx * @param time * @param component If the PixelType of image3D is a vector (for example a 2D velocity vector), then only one of the vector components can be * displayed at once. Setting this parameter will determine which of the vector's components will be used to determine the displayed PixelValue. * Set this to 0 for scalar images */ void UpdateStatusBar(itk::SmartPointer image3D, itk::Index<3> idx, TimeStepType time=0, int component=0); /** * \brief Coordinate of the pointer at begin of an interaction translated to mm unit */ mitk::Point2D m_StartCoordinateInMM; /** * \brief Coordinate of the pointer in the last step within an interaction. */ mitk::Point2D m_LastDisplayCoordinate; /** * \brief Coordinate of the pointer in the last step within an interaction translated to mm unit */ mitk::Point2D m_LastCoordinateInMM; /** * \brief Current coordinates of the pointer. */ mitk::Point2D m_CurrentDisplayCoordinate; /** * \brief Modifier that defines how many slices are scrolled per pixel that the mouse has moved * * This modifier defines how many slices the scene is scrolled per pixel that the mouse cursor has moved. * By default the modifier is 4. This means that when the user moves the cursor by 4 pixels in Y-direction * the scene is scrolled by one slice. If the user has moved the the cursor by 20 pixels, the scene is * scrolled by 5 slices. * * If the cursor has moved less than m_IndexToSliceModifier pixels the scene is scrolled by one slice. */ int m_IndexToSliceModifier; /** Defines behavior at end of data set. * If set to true it will restart at end of data set from the beginning. */ bool m_AutoRepeat; /** * Defines scroll behavior. * Default is up/down movement of pointer performs scrolling */ std::string m_ScrollDirection; /** * Defines how the axis of interaction influences scroll behavior. */ bool m_InvertScrollDirection; /** * Defines scroll behavior. * Default is up/down movement of pointer performs zooming */ std::string m_ZoomDirection; /** * Defines how the axis of interaction influences zoom behavior. */ bool m_InvertZoomDirection; /** * Defines how the axis of interaction influences move behavior. */ bool m_InvertMoveDirection; /** * Defines level/window behavior. * Default is left/right movement of pointer modifies the level. */ std::string m_LevelDirection; /** * Defines how the axis of interaction influences level/window behavior. */ bool m_InvertLevelWindowDirection; /** * Determines if the Observer reacts to events that already have been processed by a DataInteractor. * The default value is false. */ bool m_AlwaysReact; /** * Factor to adjust zooming speed. */ float m_ZoomFactor; ///// Members to deal with rotating slices /** * @brief m_LinkPlanes Determines if angle between crosshair remains fixed when rotating */ bool m_LinkPlanes; + typedef std::vector SNCVector; SNCVector m_RotatableSNCs; /// all SNCs that currently have CreatedWorldGeometries, that can be rotated. - SNCVector - m_SNCsToBeRotated; /// all SNCs that will be rotated (exceptions are the ones parallel to the one being clicked) + SNCVector m_SNCsToBeRotated; /// all SNCs that will be rotated (exceptions are the ones parallel to the one being clicked) Point3D m_LastCursorPosition; /// used for calculation of the rotation angle Point3D m_CenterOfRotation; /// used for calculation of the rotation angle Point2D m_ReferenceCursor; Vector3D m_RotationPlaneNormal; Vector3D m_RotationPlaneXVector; Vector3D m_RotationPlaneYVector; Vector3D m_PreviousRotationAxis; ScalarType m_PreviousRotationAngle; }; } #endif diff --git a/Modules/Core/include/mitkInteractionSchemeSwitcher.h b/Modules/Core/include/mitkInteractionSchemeSwitcher.h new file mode 100644 index 0000000000..245f4b2d8d --- /dev/null +++ b/Modules/Core/include/mitkInteractionSchemeSwitcher.h @@ -0,0 +1,111 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKINTERACTIONSCHEMESWITCHER_H +#define MITKINTERACTIONSCHEMESWITCHER_H + +#include "MitkCoreExports.h" + +#include "mitkInteractionEventHandler.h" + +#include + +namespace mitk +{ + /*********************************************************************** + * + * \brief Class that offers a convenient way to switch between different + * interaction schemes and their different modes. + * + * This class offers the possibility to switch between the two different + * interaction schemes that are available: + * + * - MITK : The original interaction scheme + * - left mouse button : setting the cross position in the MPR view + * - middle mouse button : panning + * - right mouse button : zooming + * + * There are 3 different MITK modes that are available in the MITK scheme. + * + * - PACS : An alternative interaction scheme that behaves more like a + * PACS workstation + * - left mouse button : behavior depends on current MouseMode + * - middle mouse button : fast scrolling + * - right mouse button : level-window + * - ctrl + right button : zooming + * - shift+ right button : panning + * + * There are 5 different PACS modes that are available in the PACS scheme. + * Each mode defines the interaction that is performed on a left + * mouse button click: + * - Pointer : sets the cross position for the MPR + * - Scroll + * - Level-Window + * - Zoom + * - Pan + * + * When the interaction scheme is changed, this class sets the corresponding + * interaction .xml-files for a given interaction event handler. + * + ***********************************************************************/ + class MITKCORE_EXPORT InteractionSchemeSwitcher : public itk::Object + { + public: +#pragma GCC visibility push(default) + /** + \brief Can be observed by GUI class to update button states when type is changed programmatically. + */ + itkEventMacro(InteractionSchemeChangedEvent, itk::AnyEvent); +#pragma GCC visibility pop + + mitkClassMacroItkParent(InteractionSchemeSwitcher, itk::Object); + itkFactorylessNewMacro(Self) itkCloneMacro(Self) + + // enum of the different interaction schemes that are available + enum InteractionScheme + { + MITKStandard = 0, + MITKRotationUncoupled, + MITKRotationCoupled, + MITKSwivel, + PACSStandard, + PACSLevelWindow, + PACSPan, + PACSScroll, + PACSZoom + }; + + /** + * @brief Set the current interaction scheme of the given interaction event handler + */ + void SetInteractionScheme(mitk::InteractionEventHandler* interactionEventHandler, InteractionScheme interactionScheme); + /** + * @brief Return the current interaction scheme + */ + InteractionScheme GetInteractionScheme() const { return m_InteractionScheme; }; + + protected: + + InteractionSchemeSwitcher(); + virtual ~InteractionSchemeSwitcher() override; + + private: + + InteractionScheme m_InteractionScheme; + }; +} // namespace mitk + +#endif // MITKINTERACTIONSCHEMESWITCHER_H diff --git a/Modules/Core/include/mitkSliceNavigationController.h b/Modules/Core/include/mitkSliceNavigationController.h index c03c961ead..89f122fe2a 100644 --- a/Modules/Core/include/mitkSliceNavigationController.h +++ b/Modules/Core/include/mitkSliceNavigationController.h @@ -1,518 +1,518 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F #define SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F #include "mitkBaseController.h" #include "mitkMessage.h" #include "mitkRenderingManager.h" #include "mitkTimeGeometry.h" #include #pragma GCC visibility push(default) #include #pragma GCC visibility pop #include "mitkDataStorage.h" #include "mitkRestorePlanePositionOperation.h" #include #include // DEPRECATED #include namespace mitk { #define mitkTimeSlicedGeometryEventMacro(classname, super) \ class MITKCORE_EXPORT DEPRECATED(classname) : public super \ { \ public: \ typedef classname Self; \ typedef super Superclass; \ classname(TimeGeometry *aTimeGeometry, unsigned int aPos) : Superclass(aTimeGeometry, aPos) {} \ virtual ~classname() {} \ virtual const char *GetEventName() const { return #classname; } \ virtual bool CheckEvent(const ::itk::EventObject *e) const { return dynamic_cast(e); } \ virtual ::itk::EventObject *MakeObject() const { return new Self(GetTimeGeometry(), GetPos()); } \ private: \ void operator=(const Self &); \ } #define mitkTimeGeometryEventMacro(classname, super) \ class MITKCORE_EXPORT classname : public super \ { \ public: \ typedef classname Self; \ typedef super Superclass; \ classname(TimeGeometry *aTimeGeometry, unsigned int aPos) : Superclass(aTimeGeometry, aPos) {} \ virtual ~classname() {} \ virtual const char *GetEventName() const { return #classname; } \ virtual bool CheckEvent(const ::itk::EventObject *e) const { return dynamic_cast(e); } \ virtual ::itk::EventObject *MakeObject() const { return new Self(GetTimeGeometry(), GetPos()); } \ private: \ void operator=(const Self &); \ } class PlaneGeometry; class BaseGeometry; class BaseRenderer; /** * \brief Controls the selection of the slice the associated BaseRenderer * will display * * A SliceNavigationController takes a BaseGeometry or a TimeGeometry as input world geometry * (TODO what are the exact requirements?) and generates a TimeGeometry * as output. The TimeGeometry holds a number of SlicedGeometry3Ds and * these in turn hold a series of PlaneGeometries. One of these PlaneGeometries is * selected as world geometry for the BaseRenderers associated to 2D views. * * The SliceNavigationController holds has Steppers (one for the slice, a * second for the time step), which control the selection of a single * PlaneGeometry from the TimeGeometry. SliceNavigationController generates * ITK events to tell observers, like a BaseRenderer, when the selected slice * or timestep changes. * * Example: * \code * // Initialization * sliceCtrl = mitk::SliceNavigationController::New(); * * // Tell the navigator the geometry to be sliced (with geometry a * // BaseGeometry::ConstPointer) * sliceCtrl->SetInputWorldGeometry(geometry.GetPointer()); * * // Tell the navigator in which direction it shall slice the data * sliceCtrl->SetViewDirection(mitk::SliceNavigationController::Axial); * * // Connect one or more BaseRenderer to this navigator, i.e.: events sent * // by the navigator when stepping through the slices (e.g. by * // sliceCtrl->GetSlice()->Next()) will be received by the BaseRenderer * // (in this example only slice-changes, see also ConnectGeometryTimeEvent * // and ConnectGeometryEvents.) * sliceCtrl->ConnectGeometrySliceEvent(renderer.GetPointer()); * * //create a world geometry and send the information to the connected renderer(s) * sliceCtrl->Update(); * \endcode * * * You can connect visible navigators to a SliceNavigationController, e.g., a * QmitkSliderNavigator (for Qt): * * \code * // Create the visible navigator (a slider with a spin-box) * QmitkSliderNavigator* navigator = * new QmitkSliderNavigator(parent, "slidernavigator"); * * // Connect the navigator to the slice-stepper of the * // SliceNavigationController. For initialization (position, mininal and * // maximal values) the values of the SliceNavigationController are used. * // Thus, accessing methods of a navigator is normally not necessary, since * // everything can be set via the (Qt-independent) SliceNavigationController. * // The QmitkStepperAdapter converts the Qt-signals to Qt-independent * // itk-events. * new QmitkStepperAdapter(navigator, sliceCtrl->GetSlice(), "navigatoradaptor"); * \endcode * * If you do not want that all renderwindows are updated when a new slice is * selected, you can use a specific RenderingManager, which updates only those * renderwindows that should be updated. This is sometimes useful when a 3D view * does not need to be updated when the slices in some 2D views are changed. * QmitkSliderNavigator (for Qt): * * \code * // create a specific RenderingManager * mitk::RenderingManager::Pointer myManager = mitk::RenderingManager::New(); * * // tell the RenderingManager to update only renderwindow1 and renderwindow2 * myManager->AddRenderWindow(renderwindow1); * myManager->AddRenderWindow(renderwindow2); * * // tell the SliceNavigationController of renderwindow1 and renderwindow2 * // to use the specific RenderingManager instead of the global one * renderwindow1->GetSliceNavigationController()->SetRenderingManager(myManager); * renderwindow2->GetSliceNavigationController()->SetRenderingManager(myManager); * \endcode * * \todo implement for non-evenly-timed geometry! * \ingroup NavigationControl */ class MITKCORE_EXPORT SliceNavigationController : public BaseController { public: mitkClassMacro(SliceNavigationController, BaseController); // itkFactorylessNewMacro(Self) // mitkNewMacro1Param(Self, const char *); itkNewMacro(Self); // itkCloneMacro(Self) /** * \brief Possible view directions, \a Original will uses * the PlaneGeometry instances in a SlicedGeometry3D provided * as input world geometry (by SetInputWorldGeometry). */ enum ViewDirection { Axial, Sagittal, Frontal, Original }; /** * \brief Set the input world geometry3D out of which the * geometries for slicing will be created. * * Any previous previous set input geometry (3D or Time) will * be ignored in future. */ void SetInputWorldGeometry3D(const mitk::BaseGeometry *geometry); itkGetConstObjectMacro(InputWorldGeometry3D, mitk::BaseGeometry); /** * \brief Set the input world geometry3D out of which the * geometries for slicing will be created. * * Any previous previous set input geometry (3D or Time) will * be ignored in future. * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(void SetInputWorldGeometry(const mitk::TimeSlicedGeometry *geometry)); /** * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(TimeSlicedGeometry *GetInputWorldGeometry()); void SetInputWorldTimeGeometry(const mitk::TimeGeometry *geometry); itkGetConstObjectMacro(InputWorldTimeGeometry, mitk::TimeGeometry); /** * \brief Access the created geometry */ itkGetConstObjectMacro(CreatedWorldGeometry, mitk::TimeGeometry); /** * \brief Set the desired view directions * * \sa ViewDirection * \sa Update(ViewDirection viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(ViewDirection, ViewDirection); itkGetEnumMacro(ViewDirection, ViewDirection); /** * \brief Set the default view direction * * This is used to re-initialize the view direction of the SNC to the * default value with SetViewDirectionToDefault() * * \sa ViewDirection * \sa Update(ViewDirection viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(DefaultViewDirection, ViewDirection); itkGetEnumMacro(DefaultViewDirection, ViewDirection); const char *GetViewDirectionAsString() const; virtual void SetViewDirectionToDefault(); /** * \brief Do the actual creation and send it to the connected * observers (renderers) * */ virtual void Update(); /** * \brief Extended version of Update, additionally allowing to * specify the direction/orientation of the created geometry. * */ virtual void Update(ViewDirection viewDirection, bool top = true, bool frontside = true, bool rotated = false); /** * \brief Send the created geometry to the connected * observers (renderers) * * Called by Update(). */ virtual void SendCreatedWorldGeometry(); /** * \brief Tell observers to re-read the currently selected 2D geometry * */ virtual void SendCreatedWorldGeometryUpdate(); /** * \brief Send the currently selected slice to the connected * observers (renderers) * * Called by Update(). */ virtual void SendSlice(); /** * \brief Send the currently selected time to the connected * observers (renderers) * * Called by Update(). */ virtual void SendTime(); /** * \brief Set the RenderingManager to be used * * If \a nullptr, the default RenderingManager will be used. */ itkSetObjectMacro(RenderingManager, RenderingManager); mitk::RenderingManager *GetRenderingManager() const; #pragma GCC visibility push(default) itkEventMacro(UpdateEvent, itk::AnyEvent); #pragma GCC visibility pop class MITKCORE_EXPORT TimeGeometryEvent : public itk::AnyEvent { public: typedef TimeGeometryEvent Self; typedef itk::AnyEvent Superclass; TimeGeometryEvent(TimeGeometry *aTimeGeometry, unsigned int aPos) : m_TimeGeometry(aTimeGeometry), m_Pos(aPos) {} ~TimeGeometryEvent() override {} const char *GetEventName() const override { return "TimeGeometryEvent"; } bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast(e); } ::itk::EventObject *MakeObject() const override { return new Self(m_TimeGeometry, m_Pos); } TimeGeometry *GetTimeGeometry() const { return m_TimeGeometry; } unsigned int GetPos() const { return m_Pos; } private: TimeGeometry::Pointer m_TimeGeometry; unsigned int m_Pos; // TimeGeometryEvent(const Self&); void operator=(const Self &); // just hide }; /** * \deprecatedSince{2013_09} Please use TimeGeometryEvent instead: For additional information see * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(typedef TimeGeometryEvent TimeSlicedGeometryEvent); mitkTimeGeometryEventMacro(GeometrySendEvent, TimeGeometryEvent); mitkTimeGeometryEventMacro(GeometryUpdateEvent, TimeGeometryEvent); mitkTimeGeometryEventMacro(GeometryTimeEvent, TimeGeometryEvent); mitkTimeGeometryEventMacro(GeometrySliceEvent, TimeGeometryEvent); template void ConnectGeometrySendEvent(T *receiver) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometry); unsigned long tag = AddObserver(GeometrySendEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template void ConnectGeometryUpdateEvent(T *receiver) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::UpdateGeometry); unsigned long tag = AddObserver(GeometryUpdateEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template void ConnectGeometrySliceEvent(T *receiver, bool connectSendEvent = true) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometrySlice); unsigned long tag = AddObserver(GeometrySliceEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); if (connectSendEvent) ConnectGeometrySendEvent(receiver); } template void ConnectGeometryTimeEvent(T *receiver, bool connectSendEvent = true) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometryTime); unsigned long tag = AddObserver(GeometryTimeEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); if (connectSendEvent) ConnectGeometrySendEvent(receiver); } template void ConnectGeometryEvents(T *receiver) { // connect sendEvent only once ConnectGeometrySliceEvent(receiver, false); ConnectGeometryTimeEvent(receiver); } // use a templated method to get the right offset when casting to void* template void Disconnect(T *receiver) { auto i = m_ReceiverToObserverTagsMap.find(static_cast(receiver)); if (i == m_ReceiverToObserverTagsMap.end()) return; const std::list &tags = i->second; for (auto tagIter = tags.begin(); tagIter != tags.end(); ++tagIter) { RemoveObserver(*tagIter); } m_ReceiverToObserverTagsMap.erase(i); } - Message<> crosshairPositionEvent; + Message1 SetCrosshairEvent; /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface * \warning not implemented */ virtual void SetGeometry(const itk::EventObject &geometrySliceEvent); /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface */ virtual void SetGeometrySlice(const itk::EventObject &geometrySliceEvent); /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface */ virtual void SetGeometryTime(const itk::EventObject &geometryTimeEvent); /** \brief Positions the SNC according to the specified point */ void SelectSliceByPoint(const mitk::Point3D &point); /** \brief Returns the TimeGeometry created by the SNC. */ mitk::TimeGeometry *GetCreatedWorldGeometry(); /** \brief Returns the BaseGeometry of the currently selected time step. */ const mitk::BaseGeometry *GetCurrentGeometry3D(); /** \brief Returns the currently selected Plane in the current * BaseGeometry (if existent). */ const mitk::PlaneGeometry *GetCurrentPlaneGeometry(); /** \brief Sets the BaseRenderer associated with this SNC (if any). While * the BaseRenderer is not directly used by SNC, this is a convenience * method to enable BaseRenderer access via the SNC. */ void SetRenderer(BaseRenderer *renderer); /** \brief Gets the BaseRenderer associated with this SNC (if any). While * the BaseRenderer is not directly used by SNC, this is a convenience * method to enable BaseRenderer access via the SNC. Returns nullptr if no * BaseRenderer has been specified*/ BaseRenderer *GetRenderer() const; /** \brief Re-orients the slice stack. All slices will be oriented to the given normal vector. The given point (world coordinates) defines the selected slice. Careful: The resulting axis vectors are not clearly defined this way. If you want to define them clearly, use ReorientSlices (const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1). */ void ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &normal); /** \brief Re-orients the slice stack so that all planes are oriented according to the * given axis vectors. The given Point eventually defines selected slice. */ void ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1); void ExecuteOperation(Operation *operation) override; /** * \brief Feature option to lock planes during mouse interaction. * This option flag disables the mouse event which causes the center * cross to move near by. */ itkSetMacro(SliceLocked, bool); itkGetMacro(SliceLocked, bool); itkBooleanMacro(SliceLocked); /** * \brief Feature option to lock slice rotation. * * This option flag disables separately the rotation of a slice which is * implemented in mitkSliceRotator. */ itkSetMacro(SliceRotationLocked, bool); itkGetMacro(SliceRotationLocked, bool); itkBooleanMacro(SliceRotationLocked); /** * \brief Adjusts the numerical range of the slice stepper according to * the current geometry orientation of this SNC's SlicedGeometry. */ void AdjustSliceStepperRange(); protected: SliceNavigationController(); ~SliceNavigationController() override; mitk::BaseGeometry::ConstPointer m_InputWorldGeometry3D; mitk::TimeGeometry::ConstPointer m_InputWorldTimeGeometry; mitk::TimeGeometry::Pointer m_CreatedWorldGeometry; ViewDirection m_ViewDirection; ViewDirection m_DefaultViewDirection; mitk::RenderingManager::Pointer m_RenderingManager; mitk::BaseRenderer *m_Renderer; itkSetMacro(Top, bool); itkGetMacro(Top, bool); itkBooleanMacro(Top); itkSetMacro(FrontSide, bool); itkGetMacro(FrontSide, bool); itkBooleanMacro(FrontSide); itkSetMacro(Rotated, bool); itkGetMacro(Rotated, bool); itkBooleanMacro(Rotated); bool m_Top; bool m_FrontSide; bool m_Rotated; bool m_BlockUpdate; bool m_SliceLocked; bool m_SliceRotationLocked; unsigned int m_OldPos; typedef std::map> ObserverTagsMapType; ObserverTagsMapType m_ReceiverToObserverTagsMap; }; } // namespace mitk #endif /* SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F */ diff --git a/Modules/Core/include/mitkStdDisplayActionEventHandler.h b/Modules/Core/include/mitkStdDisplayActionEventHandler.h new file mode 100644 index 0000000000..76758df9eb --- /dev/null +++ b/Modules/Core/include/mitkStdDisplayActionEventHandler.h @@ -0,0 +1,41 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKSTDDISPLAYACTIONEVENTHANDLER_H +#define MITKSTDDISPLAYACTIONEVENTHANDLER_H + +#include + +// mitk core +#include "mitkDisplayActionEventHandler.h" + +namespace mitk +{ + class MITKCORE_EXPORT StdDisplayActionEventHandler : public DisplayActionEventHandler + { + public: + + /** + * @brief Initializes common standard display actions by using the default display action event functions. + * + * @pre The class' observable (the display action event broadcast) has to be set to connect display events. + * @throw mitk::Exception, if the class' observable is null. + */ + void InitStdActions(); + }; +} // end namespace mitk + +#endif // MITKSTDDISPLAYACTIONEVENTHANDLER_H diff --git a/Modules/Core/include/mitkStdFunctionCommand.h b/Modules/Core/include/mitkStdFunctionCommand.h new file mode 100644 index 0000000000..e1ce8dfc2a --- /dev/null +++ b/Modules/Core/include/mitkStdFunctionCommand.h @@ -0,0 +1,95 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKSTDFUNCTIONCOMMAND_H +#define MITKSTDFUNCTIONCOMMAND_H + +#include + +// itk +#include + +// c++ +#include + +namespace mitk +{ + // define custom command to accept std::functions as "filter" and as "action" + class MITKCORE_EXPORT StdFunctionCommand : public itk::Command + { + public: + using Self = StdFunctionCommand; + using Pointer = itk::SmartPointer; + + using FilterFunction = std::function; + using ActionFunction = std::function; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(StdFunctionCommand, itk::Command); + + void SetCommandFilter(FilterFunction stdFunctionFilter) + { + m_StdFilterFunction = stdFunctionFilter; + } + + void SetCommandAction(ActionFunction stdFunctionAction) + { + m_StdActionFunction = stdFunctionAction; + } + + virtual void Execute(Object*, const itk::EventObject& event) override + { + if (m_StdFilterFunction && m_StdActionFunction) + { + if (m_StdFilterFunction(event)) + { + m_StdActionFunction(event); + } + } + } + + virtual void Execute(const Object*, const itk::EventObject& event) override + { + if (m_StdFilterFunction && m_StdActionFunction) + { + if (m_StdFilterFunction(event)) + { + m_StdActionFunction(event); + } + } + } + + protected: + FilterFunction m_StdFilterFunction; + ActionFunction m_StdActionFunction; + + StdFunctionCommand() + : m_StdFilterFunction(nullptr) + , m_StdActionFunction(nullptr) + {} + + virtual ~StdFunctionCommand() {} + + private: + ITK_DISALLOW_COPY_AND_ASSIGN(StdFunctionCommand); + }; + +} // end namespace mitk + +#endif // MITKSTDFUNCTIONCOMMAND_H diff --git a/Modules/Core/include/mitkStepper.h b/Modules/Core/include/mitkStepper.h index d86c668fc1..16b51ebf87 100644 --- a/Modules/Core/include/mitkStepper.h +++ b/Modules/Core/include/mitkStepper.h @@ -1,146 +1,148 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef STEPPER_H_HEADER_INCLUDED_C1E77191 #define STEPPER_H_HEADER_INCLUDED_C1E77191 #include "mitkNumericTypes.h" #include #include #include #include #include namespace mitk { /** * \brief Helper class to step through a list * * A helper class to step through a list. Does not contain the list, just the * position in the list (between 0 and GetSteps()). Provides methods like * First (go to the first element), Next (go to the next one), etc. * * Besides the actual number of steps, the stepper can also hold a stepping * range, indicating the scalar values corresponding to the covered steps. * For example, steppers are generally used to slice a dataset with a plane; * Hereby, Steps indicates the total number of steps (positions) available for * the plane, Pos indicates the current step, and Range indicates the physical * minimum and maximum values for the plane, in this case a value in mm. * * The range can also be supplied with a unit name (a string) which can be * used by classes providing information about the stepping (e.g. graphical * sliders). * * \ingroup NavigationControl */ class MITKCORE_EXPORT Stepper : public itk::Object { public: mitkClassMacroItkParent(Stepper, itk::Object); itkFactorylessNewMacro(Self) itkCloneMacro(Self) itkGetConstMacro(Pos, unsigned int); virtual void SetPos(unsigned int pos) { // copied from itkMacro.h, itkSetClampMacro(...) unsigned int newPos; if (m_Steps != 0) { newPos = (pos > m_Steps - 1 ? m_Steps - 1 : pos); } else { newPos = 0; } if (this->m_Pos != newPos) { this->m_Pos = newPos; this->Modified(); } } itkGetConstMacro(Steps, unsigned int); itkSetMacro(Steps, unsigned int); itkGetConstMacro(AutoRepeat, bool); itkSetMacro(AutoRepeat, bool); itkBooleanMacro(AutoRepeat); /** Causes the stepper to shift direction when the boundary is reached */ itkSetMacro(PingPong, bool); itkGetConstMacro(PingPong, bool); itkBooleanMacro(PingPong); /** If set to true, the Next() decreases the stepper and Previous() * decreases it */ itkSetMacro(InverseDirection, bool); itkGetConstMacro(InverseDirection, bool); itkBooleanMacro(InverseDirection); void SetRange(ScalarType min, ScalarType max); void InvalidateRange(); ScalarType GetRangeMin() const; ScalarType GetRangeMax() const; bool HasValidRange() const; void RemoveRange(); bool HasRange() const; void SetUnitName(const char *unitName); const char *GetUnitName() const; void RemoveUnitName(); bool HasUnitName() const; virtual void Next(); virtual void Previous(); + virtual void MoveSlice(int sliceDelta); + virtual void First(); virtual void Last(); protected: Stepper(); ~Stepper() override; void Increase(); void Decrease(); unsigned int m_Pos; unsigned int m_Steps; bool m_AutoRepeat; bool m_PingPong; bool m_InverseDirection; ScalarType m_RangeMin; ScalarType m_RangeMax; bool m_RangeValid; bool m_HasRange; std::string m_UnitName; bool m_HasUnitName; }; } // namespace mitk #endif /* STEPPER_H_HEADER_INCLUDED_C1E77191 */ diff --git a/Modules/Core/resource/Interactions/DisplayConfigMITK.xml b/Modules/Core/resource/Interactions/DisplayConfigMITK.xml index f5a8e80498..269d079e79 100644 --- a/Modules/Core/resource/Interactions/DisplayConfigMITK.xml +++ b/Modules/Core/resource/Interactions/DisplayConfigMITK.xml @@ -1,59 +1,59 @@ - - - + + + diff --git a/Modules/Core/resource/Interactions/DisplayConfigMITKRotation.xml b/Modules/Core/resource/Interactions/DisplayConfigMITKRotation.xml index f8fc7c3ede..94f1a24f3d 100644 --- a/Modules/Core/resource/Interactions/DisplayConfigMITKRotation.xml +++ b/Modules/Core/resource/Interactions/DisplayConfigMITKRotation.xml @@ -1,55 +1,53 @@ - - diff --git a/Modules/Core/resource/Interactions/DisplayConfigMITKRotationUnCoupled.xml b/Modules/Core/resource/Interactions/DisplayConfigMITKRotationUnCoupled.xml index 852aafa2de..43e68d7d36 100644 --- a/Modules/Core/resource/Interactions/DisplayConfigMITKRotationUnCoupled.xml +++ b/Modules/Core/resource/Interactions/DisplayConfigMITKRotationUnCoupled.xml @@ -1,55 +1,53 @@ - + - - diff --git a/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp b/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp index aeea570d69..ae9cda0839 100644 --- a/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp +++ b/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp @@ -1,659 +1,661 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSliceNavigationController.h" #include "mitkAction.h" #include "mitkBaseRenderer.h" #include "mitkCrosshairPositionEvent.h" #include "mitkInteractionConst.h" #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkPlaneGeometry.h" #include "mitkProportionalTimeGeometry.h" #include "mitkArbitraryTimeGeometry.h" #include "mitkRenderingManager.h" #include "mitkSlicedGeometry3D.h" #include "mitkVtkPropRenderer.h" #include "mitkImage.h" #include "mitkImagePixelReadAccessor.h" #include "mitkInteractionConst.h" #include "mitkNodePredicateDataType.h" #include "mitkOperationEvent.h" #include "mitkPixelTypeMultiplex.h" #include "mitkPlaneOperation.h" #include "mitkPointOperation.h" #include "mitkStatusBar.h" #include "mitkUndoController.h" #include "mitkApplyTransformMatrixOperation.h" #include "mitkMemoryUtilities.h" #include namespace mitk { SliceNavigationController::SliceNavigationController() : BaseController(), m_InputWorldGeometry3D( mitk::BaseGeometry::ConstPointer() ), m_InputWorldTimeGeometry( mitk::TimeGeometry::ConstPointer() ), m_CreatedWorldGeometry( mitk::TimeGeometry::Pointer() ), m_ViewDirection(Axial), m_DefaultViewDirection(Axial), m_RenderingManager( mitk::RenderingManager::Pointer() ), m_Renderer( nullptr ), m_Top(false), m_FrontSide(false), m_Rotated(false), m_BlockUpdate(false), m_SliceLocked(false), m_SliceRotationLocked(false), m_OldPos(0) { typedef itk::SimpleMemberCommand SNCCommandType; SNCCommandType::Pointer sliceStepperChangedCommand, timeStepperChangedCommand; sliceStepperChangedCommand = SNCCommandType::New(); timeStepperChangedCommand = SNCCommandType::New(); sliceStepperChangedCommand->SetCallbackFunction(this, &SliceNavigationController::SendSlice); timeStepperChangedCommand->SetCallbackFunction(this, &SliceNavigationController::SendTime); m_Slice->AddObserver(itk::ModifiedEvent(), sliceStepperChangedCommand); m_Time->AddObserver(itk::ModifiedEvent(), timeStepperChangedCommand); m_Slice->SetUnitName("mm"); m_Time->SetUnitName("ms"); m_Top = false; m_FrontSide = false; m_Rotated = false; } SliceNavigationController::~SliceNavigationController() {} void SliceNavigationController::SetInputWorldGeometry3D(const BaseGeometry *geometry) { if ( geometry != nullptr ) { if (geometry->GetBoundingBox()->GetDiagonalLength2() < eps) { itkWarningMacro("setting an empty bounding-box"); geometry = nullptr; } } if (m_InputWorldGeometry3D != geometry) { m_InputWorldGeometry3D = geometry; m_InputWorldTimeGeometry = mitk::TimeGeometry::ConstPointer(); this->Modified(); } } void SliceNavigationController::SetInputWorldTimeGeometry(const TimeGeometry *geometry) { if ( geometry != nullptr ) { if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() < eps) { itkWarningMacro("setting an empty bounding-box"); geometry = nullptr; } } if (m_InputWorldTimeGeometry != geometry) { m_InputWorldTimeGeometry = geometry; m_InputWorldGeometry3D = mitk::BaseGeometry::ConstPointer(); this->Modified(); } } RenderingManager *SliceNavigationController::GetRenderingManager() const { mitk::RenderingManager *renderingManager = m_RenderingManager.GetPointer(); if (renderingManager != nullptr) return renderingManager; if ( m_Renderer != nullptr ) { renderingManager = m_Renderer->GetRenderingManager(); if (renderingManager != nullptr) return renderingManager; } return mitk::RenderingManager::GetInstance(); } void SliceNavigationController::SetViewDirectionToDefault() { m_ViewDirection = m_DefaultViewDirection; } const char *SliceNavigationController::GetViewDirectionAsString() const { const char *viewDirectionString; switch (m_ViewDirection) { case SliceNavigationController::Axial: viewDirectionString = "Axial"; break; case SliceNavigationController::Sagittal: viewDirectionString = "Sagittal"; break; case SliceNavigationController::Frontal: viewDirectionString = "Coronal"; break; case SliceNavigationController::Original: viewDirectionString = "Original"; break; default: viewDirectionString = "No View Direction Available"; break; } return viewDirectionString; } void SliceNavigationController::Update() { if (!m_BlockUpdate) { if (m_ViewDirection == Sagittal) { this->Update(Sagittal, true, true, false); } else if (m_ViewDirection == Frontal) { this->Update(Frontal, false, true, false); } else if (m_ViewDirection == Axial) { this->Update(Axial, false, false, true); } else { this->Update(m_ViewDirection); } } } void SliceNavigationController::Update(SliceNavigationController::ViewDirection viewDirection, bool top, bool frontside, bool rotated) { TimeGeometry::ConstPointer worldTimeGeometry = m_InputWorldTimeGeometry; if (m_BlockUpdate || (m_InputWorldTimeGeometry.IsNull() && m_InputWorldGeometry3D.IsNull()) || ((worldTimeGeometry.IsNotNull()) && (worldTimeGeometry->CountTimeSteps() == 0))) { return; } m_BlockUpdate = true; if (m_InputWorldTimeGeometry.IsNotNull() && m_LastUpdateTime < m_InputWorldTimeGeometry->GetMTime()) { Modified(); } if (m_InputWorldGeometry3D.IsNotNull() && m_LastUpdateTime < m_InputWorldGeometry3D->GetMTime()) { Modified(); } this->SetViewDirection(viewDirection); this->SetTop(top); this->SetFrontSide(frontside); this->SetRotated(rotated); if (m_LastUpdateTime < GetMTime()) { m_LastUpdateTime = GetMTime(); // initialize the viewplane SlicedGeometry3D::Pointer slicedWorldGeometry = SlicedGeometry3D::Pointer(); BaseGeometry::ConstPointer currentGeometry = BaseGeometry::ConstPointer(); if (m_InputWorldTimeGeometry.IsNotNull()) if (m_InputWorldTimeGeometry->IsValidTimeStep(GetTime()->GetPos())) currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(GetTime()->GetPos()); else currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(0); else currentGeometry = m_InputWorldGeometry3D; m_CreatedWorldGeometry = mitk::TimeGeometry::Pointer(); switch (viewDirection) { case Original: if (worldTimeGeometry.IsNotNull()) { m_CreatedWorldGeometry = worldTimeGeometry->Clone(); worldTimeGeometry = m_CreatedWorldGeometry.GetPointer(); slicedWorldGeometry = dynamic_cast( m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()).GetPointer()); if (slicedWorldGeometry.IsNotNull()) { break; } } else { const auto *worldSlicedGeometry = dynamic_cast(currentGeometry.GetPointer()); if ( worldSlicedGeometry != nullptr ) { slicedWorldGeometry = static_cast(currentGeometry->Clone().GetPointer()); break; } } slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::None, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; case Axial: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Axial, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; case Frontal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Frontal, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; case Sagittal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Sagittal, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; default: itkExceptionMacro("unknown ViewDirection"); } m_Slice->SetPos(0); m_Slice->SetSteps((int)slicedWorldGeometry->GetSlices()); if ( worldTimeGeometry.IsNull() ) { auto createdTimeGeometry = ProportionalTimeGeometry::New(); createdTimeGeometry->Initialize( slicedWorldGeometry, 1 ); m_CreatedWorldGeometry = createdTimeGeometry; m_Time->SetSteps(0); m_Time->SetPos(0); m_Time->InvalidateRange(); } else { m_BlockUpdate = true; m_Time->SetSteps(worldTimeGeometry->CountTimeSteps()); m_Time->SetPos(0); const TimeBounds &timeBounds = worldTimeGeometry->GetTimeBounds(); m_Time->SetRange(timeBounds[0], timeBounds[1]); m_BlockUpdate = false; const auto currentTemporalPosition = this->GetTime()->GetPos(); assert( worldTimeGeometry->GetGeometryForTimeStep( currentTemporalPosition ).IsNotNull() ); if ( dynamic_cast( worldTimeGeometry.GetPointer() ) != nullptr ) { const TimePointType minimumTimePoint = worldTimeGeometry->TimeStepToTimePoint( currentTemporalPosition ); const TimePointType stepDuration = worldTimeGeometry->TimeStepToTimePoint( currentTemporalPosition + 1 ) - minimumTimePoint; auto createdTimeGeometry = ProportionalTimeGeometry::New(); createdTimeGeometry->Initialize( slicedWorldGeometry, worldTimeGeometry->CountTimeSteps() ); createdTimeGeometry->SetFirstTimePoint( minimumTimePoint ); createdTimeGeometry->SetStepDuration( stepDuration ); m_CreatedWorldGeometry = createdTimeGeometry; } else { auto createdTimeGeometry = mitk::ArbitraryTimeGeometry::New(); const TimeStepType numberOfTimeSteps = worldTimeGeometry->CountTimeSteps(); createdTimeGeometry->ReserveSpaceForGeometries( numberOfTimeSteps ); for ( TimeStepType i = 0; i < numberOfTimeSteps; ++i ) { const BaseGeometry::Pointer clonedGeometry = slicedWorldGeometry->Clone().GetPointer(); const auto bounds = worldTimeGeometry->GetTimeBounds( i ); createdTimeGeometry->AppendNewTimeStep( clonedGeometry, bounds[0], bounds[1]); } createdTimeGeometry->Update(); m_CreatedWorldGeometry = createdTimeGeometry; } } } // unblock update; we may do this now, because if m_BlockUpdate was already // true before this method was entered, then we will never come here. m_BlockUpdate = false; // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry and time/slice data. this->SendCreatedWorldGeometry(); this->SendSlice(); this->SendTime(); // Adjust the stepper range of slice stepper according to geometry this->AdjustSliceStepperRange(); } void SliceNavigationController::SendCreatedWorldGeometry() { // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry. if (!m_BlockUpdate) { this->InvokeEvent(GeometrySendEvent(m_CreatedWorldGeometry, 0)); } } void SliceNavigationController::SendCreatedWorldGeometryUpdate() { if (!m_BlockUpdate) { this->InvokeEvent(GeometryUpdateEvent(m_CreatedWorldGeometry, m_Slice->GetPos())); } } void SliceNavigationController::SendSlice() { if (!m_BlockUpdate) { if (m_CreatedWorldGeometry.IsNotNull()) { this->InvokeEvent(GeometrySliceEvent(m_CreatedWorldGeometry, m_Slice->GetPos())); // send crosshair event - crosshairPositionEvent.Send(); + //SetCrosshairEvent.Send(); // Request rendering update for all views this->GetRenderingManager()->RequestUpdateAll(); } } } void SliceNavigationController::SendTime() { if (!m_BlockUpdate) { if (m_CreatedWorldGeometry.IsNotNull()) { this->InvokeEvent(GeometryTimeEvent(m_CreatedWorldGeometry, m_Time->GetPos())); // Request rendering update for all views this->GetRenderingManager()->RequestUpdateAll(); } } } void SliceNavigationController::SetGeometry(const itk::EventObject &) {} void SliceNavigationController::SetGeometryTime(const itk::EventObject &geometryTimeEvent) { if (m_CreatedWorldGeometry.IsNull()) { return; } const auto *timeEvent = dynamic_cast< const SliceNavigationController::GeometryTimeEvent * >(&geometryTimeEvent); assert( timeEvent != nullptr ); TimeGeometry *timeGeometry = timeEvent->GetTimeGeometry(); assert( timeGeometry != nullptr ); auto timeStep = (int)timeEvent->GetPos(); ScalarType timeInMS; timeInMS = timeGeometry->TimeStepToTimePoint(timeStep); timeStep = m_CreatedWorldGeometry->TimePointToTimeStep(timeInMS); this->GetTime()->SetPos(timeStep); } void SliceNavigationController::SetGeometrySlice(const itk::EventObject &geometrySliceEvent) { const auto *sliceEvent = dynamic_cast(&geometrySliceEvent); assert(sliceEvent!=nullptr); this->GetSlice()->SetPos(sliceEvent->GetPos()); } void SliceNavigationController::SelectSliceByPoint(const Point3D &point) { if (m_CreatedWorldGeometry.IsNull()) { return; } //@todo add time to PositionEvent and use here!! SlicedGeometry3D *slicedWorldGeometry = dynamic_cast( m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()).GetPointer()); if (slicedWorldGeometry) { int bestSlice = -1; double bestDistance = itk::NumericTraits::max(); int s, slices; slices = slicedWorldGeometry->GetSlices(); if (slicedWorldGeometry->GetEvenlySpaced()) { mitk::PlaneGeometry *plane = slicedWorldGeometry->GetPlaneGeometry(0); const Vector3D &direction = slicedWorldGeometry->GetDirectionVector(); Point3D projectedPoint; plane->Project(point, projectedPoint); // Check whether the point is somewhere within the slice stack volume; // otherwise, the default slice (0) will be selected if (direction[0] * (point[0] - projectedPoint[0]) + direction[1] * (point[1] - projectedPoint[1]) + direction[2] * (point[2] - projectedPoint[2]) >= 0) { bestSlice = (int)(plane->Distance(point) / slicedWorldGeometry->GetSpacing()[2] + 0.5); } } else { Point3D projectedPoint; for (s = 0; s < slices; ++s) { slicedWorldGeometry->GetPlaneGeometry(s)->Project(point, projectedPoint); const Vector3D distance = projectedPoint - point; ScalarType currentDistance = distance.GetSquaredNorm(); if (currentDistance < bestDistance) { bestDistance = currentDistance; bestSlice = s; } } } if (bestSlice >= 0) { this->GetSlice()->SetPos(bestSlice); } else { this->GetSlice()->SetPos(0); } this->SendCreatedWorldGeometryUpdate(); + // send crosshair event + SetCrosshairEvent.Send(point); } } void SliceNavigationController::ReorientSlices(const Point3D &point, const Vector3D &normal) { if (m_CreatedWorldGeometry.IsNull()) { return; } PlaneOperation op(OpORIENT, point, normal); m_CreatedWorldGeometry->ExecuteOperation(&op); this->SendCreatedWorldGeometryUpdate(); } void SliceNavigationController::ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1) { if (m_CreatedWorldGeometry) { PlaneOperation op(OpORIENT, point, axisVec0, axisVec1); m_CreatedWorldGeometry->ExecuteOperation(&op); this->SendCreatedWorldGeometryUpdate(); } } mitk::TimeGeometry *SliceNavigationController::GetCreatedWorldGeometry() { return m_CreatedWorldGeometry; } const mitk::BaseGeometry *SliceNavigationController::GetCurrentGeometry3D() { if (m_CreatedWorldGeometry.IsNotNull()) { return m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()); } else { return nullptr; } } const mitk::PlaneGeometry *SliceNavigationController::GetCurrentPlaneGeometry() { const auto *slicedGeometry = dynamic_cast(this->GetCurrentGeometry3D()); if (slicedGeometry) { const mitk::PlaneGeometry *planeGeometry = (slicedGeometry->GetPlaneGeometry(this->GetSlice()->GetPos())); return planeGeometry; } else { return nullptr; } } void SliceNavigationController::SetRenderer(BaseRenderer *renderer) { m_Renderer = renderer; } BaseRenderer *SliceNavigationController::GetRenderer() const { return m_Renderer; } void SliceNavigationController::AdjustSliceStepperRange() { const auto *slicedGeometry = dynamic_cast(this->GetCurrentGeometry3D()); const Vector3D &direction = slicedGeometry->GetDirectionVector(); int c = 0; int i, k = 0; for (i = 0; i < 3; ++i) { if (fabs(direction[i]) < 0.000000001) { ++c; } else { k = i; } } if (c == 2) { ScalarType min = slicedGeometry->GetOrigin()[k]; ScalarType max = min + slicedGeometry->GetExtentInMM(k); m_Slice->SetRange(min, max); } else { m_Slice->InvalidateRange(); } } void SliceNavigationController::ExecuteOperation(Operation *operation) { // switch on type // - select best slice for a given point // - rotate created world geometry according to Operation->SomeInfo() if (!operation || m_CreatedWorldGeometry.IsNull()) { return; } switch (operation->GetOperationType()) { case OpMOVE: // should be a point operation { if (!m_SliceLocked) // do not move the cross position { // select a slice auto *po = dynamic_cast(operation); if (po && po->GetIndex() == -1) { this->SelectSliceByPoint(po->GetPoint()); } else if (po && po->GetIndex() != -1) // undo case because index != -1, index holds the old position of this slice { this->GetSlice()->SetPos(po->GetIndex()); } } break; } case OpRESTOREPLANEPOSITION: { m_CreatedWorldGeometry->ExecuteOperation(operation); this->SendCreatedWorldGeometryUpdate(); break; } case OpAPPLYTRANSFORMMATRIX: { m_CreatedWorldGeometry->ExecuteOperation(operation); this->SendCreatedWorldGeometryUpdate(); break; } default: { // do nothing break; } } } } // namespace diff --git a/Modules/Core/src/Controllers/mitkStepper.cpp b/Modules/Core/src/Controllers/mitkStepper.cpp index cdecc526e3..14e980a2dd 100644 --- a/Modules/Core/src/Controllers/mitkStepper.cpp +++ b/Modules/Core/src/Controllers/mitkStepper.cpp @@ -1,180 +1,209 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkStepper.h" mitk::Stepper::Stepper() : m_Pos(0), m_Steps(0), m_AutoRepeat(false), m_PingPong(false), m_InverseDirection(false), m_RangeMin(0.0), m_RangeMax(-1.0), m_RangeValid(false), m_HasRange(false), m_HasUnitName(false) { } mitk::Stepper::~Stepper() { } void mitk::Stepper::SetRange(ScalarType min, ScalarType max) { m_RangeMin = min; m_RangeMax = max; m_HasRange = true; m_RangeValid = true; this->Modified(); } void mitk::Stepper::InvalidateRange() { m_HasRange = true; m_RangeValid = false; this->Modified(); } mitk::ScalarType mitk::Stepper::GetRangeMin() const { return m_RangeMin; } mitk::ScalarType mitk::Stepper::GetRangeMax() const { return m_RangeMax; } void mitk::Stepper::RemoveRange() { m_HasRange = false; this->Modified(); } bool mitk::Stepper::HasValidRange() const { return (m_HasRange && m_RangeValid); } bool mitk::Stepper::HasRange() const { return m_HasRange; } void mitk::Stepper::SetUnitName(const char *unitName) { m_UnitName = std::string(unitName); m_HasUnitName = true; this->Modified(); } const char *mitk::Stepper::GetUnitName() const { return m_UnitName.c_str(); } void mitk::Stepper::RemoveUnitName() { m_HasUnitName = false; this->Modified(); } bool mitk::Stepper::HasUnitName() const { return m_HasUnitName; } void mitk::Stepper::Increase() { if (this->GetPos() < this->GetSteps() - 1) { this->SetPos(this->GetPos() + 1); } else if (m_AutoRepeat) { if (!m_PingPong) { this->SetPos(0); } else { m_InverseDirection = true; if (this->GetPos() > 0) { this->SetPos(this->GetPos() - 1); } } } } void mitk::Stepper::Decrease() { if (this->GetPos() > 0) { this->SetPos(this->GetPos() - 1); } else if (m_AutoRepeat) { if (!m_PingPong) { this->SetPos(this->GetSteps() - 1); } else { m_InverseDirection = false; if (this->GetPos() < this->GetSteps() - 1) { this->SetPos(this->GetPos() + 1); } } } } void mitk::Stepper::Next() { if (!m_InverseDirection) { this->Increase(); } else { this->Decrease(); } } void mitk::Stepper::Previous() { if (!m_InverseDirection) { this->Decrease(); } else { this->Increase(); } } +void mitk::Stepper::MoveSlice(int sliceDelta) +{ + int newPosition = this->GetPos() + sliceDelta; + // if auto repeat is on, increasing continues at the first slice if the last slice was reached and vice versa + int maxSlices = this->GetSteps(); + if (m_AutoRepeat) + { + while (newPosition < 0) + { + newPosition += maxSlices; + } + while (newPosition >= maxSlices) + { + newPosition -= maxSlices; + } + } + else + { + // if the new slice is below 0 we still show slice 0 + // due to the stepper using unsigned int we have to do this ourselves + if (newPosition < 1) + { + newPosition = 0; + } + } + + this->SetPos(newPosition); +} + void mitk::Stepper::First() { this->SetPos(0); } void mitk::Stepper::Last() { this->SetPos(this->GetSteps() - 1); } diff --git a/Modules/Core/src/DataManagement/mitkDataStorage.cpp b/Modules/Core/src/DataManagement/mitkDataStorage.cpp index f25faaa1b8..07fba9f600 100644 --- a/Modules/Core/src/DataManagement/mitkDataStorage.cpp +++ b/Modules/Core/src/DataManagement/mitkDataStorage.cpp @@ -1,574 +1,633 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDataStorage.h" #include "itkCommand.h" #include "itkMutexLockHolder.h" #include "mitkDataNode.h" #include "mitkGroupTagProperty.h" #include "mitkImage.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" #include "mitkArbitraryTimeGeometry.h" mitk::DataStorage::DataStorage() : itk::Object(), m_BlockNodeModifiedEvents(false) { } mitk::DataStorage::~DataStorage() { ///// we can not call GetAll() in destructor, because it is implemented in a subclass // SetOfObjects::ConstPointer all = this->GetAll(); // for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) // this->RemoveListeners(it->Value()); // m_NodeModifiedObserverTags.clear(); // m_NodeDeleteObserverTags.clear(); } void mitk::DataStorage::Add(DataNode *node, DataNode *parent) { DataStorage::SetOfObjects::Pointer parents = DataStorage::SetOfObjects::New(); if (parent != nullptr) //< Return empty set if parent is null parents->InsertElement(0, parent); this->Add(node, parents); } void mitk::DataStorage::Remove(const DataStorage::SetOfObjects *nodes) { if (nodes == nullptr) return; for (DataStorage::SetOfObjects::ConstIterator it = nodes->Begin(); it != nodes->End(); it++) this->Remove(it.Value()); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::GetSubset(const NodePredicateBase *condition) const { DataStorage::SetOfObjects::ConstPointer result = this->FilterSetOfObjects(this->GetAll(), condition); return result; } +mitk::DataNode::Pointer mitk::DataStorage::GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes, const mitk::Point3D worldPosition, const mitk::BaseRenderer* renderer) +{ + if (nodes.IsNull()) + { + return nullptr; + } + + mitk::DataNode::Pointer topLayerNode = nullptr; + int maxLayer = std::numeric_limits::min(); + + for (auto node : *nodes) + { + if (node.IsNull()) + { + continue; + } + + bool isHelperObject = false; + node->GetBoolProperty("helper object", isHelperObject); + if (isHelperObject) + { + continue; + } + + auto data = node->GetData(); + if (nullptr == data) + { + continue; + } + + auto geometry = data->GetGeometry(); + if (nullptr == geometry || !geometry->IsInside(worldPosition)) + { + continue; + } + + int layer = 0; + if (!node->GetIntProperty("layer", layer, renderer)) + { + continue; + } + + if (layer <= maxLayer) + { + continue; + } + + if (!node->IsVisible(renderer)) + { + continue; + } + + topLayerNode = node; + maxLayer = layer; + } + + return topLayerNode; +} + mitk::DataNode *mitk::DataStorage::GetNamedNode(const char *name) const { if (name == nullptr) return nullptr; StringProperty::Pointer s(StringProperty::New(name)); NodePredicateProperty::Pointer p = NodePredicateProperty::New("name", s); DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(p); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } mitk::DataNode *mitk::DataStorage::GetNode(const NodePredicateBase *condition) const { if (condition == nullptr) return nullptr; DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(condition); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } mitk::DataNode *mitk::DataStorage::GetNamedDerivedNode(const char *name, const DataNode *sourceNode, bool onlyDirectDerivations) const { if (name == nullptr) return nullptr; StringProperty::Pointer s(StringProperty::New(name)); NodePredicateProperty::Pointer p = NodePredicateProperty::New("name", s); DataStorage::SetOfObjects::ConstPointer rs = this->GetDerivations(sourceNode, p, onlyDirectDerivations); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } void mitk::DataStorage::PrintSelf(std::ostream &os, itk::Indent indent) const { // Superclass::PrintSelf(os, indent); DataStorage::SetOfObjects::ConstPointer all = this->GetAll(); os << indent << "DataStorage " << this << " is managing " << all->Size() << " objects. List of objects:" << std::endl; for (DataStorage::SetOfObjects::ConstIterator allIt = all->Begin(); allIt != all->End(); allIt++) { std::string name; allIt.Value()->GetName(name); std::string datatype; if (allIt.Value()->GetData() != nullptr) datatype = allIt.Value()->GetData()->GetNameOfClass(); os << indent << " " << allIt.Value().GetPointer() << "<" << datatype << ">: " << name << std::endl; DataStorage::SetOfObjects::ConstPointer parents = this->GetSources(allIt.Value()); if (parents->Size() > 0) { os << indent << " Direct sources: "; for (DataStorage::SetOfObjects::ConstIterator parentIt = parents->Begin(); parentIt != parents->End(); parentIt++) os << parentIt.Value().GetPointer() << ", "; os << std::endl; } DataStorage::SetOfObjects::ConstPointer derivations = this->GetDerivations(allIt.Value()); if (derivations->Size() > 0) { os << indent << " Direct derivations: "; for (DataStorage::SetOfObjects::ConstIterator derivationIt = derivations->Begin(); derivationIt != derivations->End(); derivationIt++) os << derivationIt.Value().GetPointer() << ", "; os << std::endl; } } os << std::endl; } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::FilterSetOfObjects(const SetOfObjects *set, const NodePredicateBase *condition) const { if (set == nullptr) return nullptr; DataStorage::SetOfObjects::Pointer result = DataStorage::SetOfObjects::New(); for (DataStorage::SetOfObjects::ConstIterator it = set->Begin(); it != set->End(); it++) if (condition == nullptr || condition->CheckNode(it.Value()) == true) // alway copy the set, otherwise the iterator in DataStorage::Remove() will crash result->InsertElement(result->Size(), it.Value()); return DataStorage::SetOfObjects::ConstPointer(result); } const mitk::DataNode::GroupTagList mitk::DataStorage::GetGroupTags() const { DataNode::GroupTagList result; SetOfObjects::ConstPointer all = this->GetAll(); if (all.IsNull()) return result; for (DataStorage::SetOfObjects::ConstIterator nodeIt = all->Begin(); nodeIt != all->End(); nodeIt++) // for each node { PropertyList *pl = nodeIt.Value()->GetPropertyList(); for (auto propIt = pl->GetMap()->begin(); propIt != pl->GetMap()->end(); ++propIt) if (dynamic_cast(propIt->second.GetPointer()) != nullptr) result.insert(propIt->first); } return result; } void mitk::DataStorage::EmitAddNodeEvent(const DataNode *node) { AddNodeEvent.Send(node); } void mitk::DataStorage::EmitRemoveNodeEvent(const DataNode *node) { RemoveNodeEvent.Send(node); } void mitk::DataStorage::OnNodeInteractorChanged(itk::Object *caller, const itk::EventObject &) { const auto *_Node = dynamic_cast(caller); if (_Node) { InteractorChangedNodeEvent.Send(_Node); } } void mitk::DataStorage::OnNodeModifiedOrDeleted(const itk::Object *caller, const itk::EventObject &event) { if (m_BlockNodeModifiedEvents) return; const auto *_Node = dynamic_cast(caller); if (_Node) { const auto *modEvent = dynamic_cast(&event); if (modEvent) ChangedNodeEvent.Send(_Node); else DeleteNodeEvent.Send(_Node); } } void mitk::DataStorage::AddListeners(const DataNode *_Node) { itk::MutexLockHolder locked(m_MutexOne); // node must not be 0 and must not be yet registered auto *NonConstNode = const_cast(_Node); if (_Node && m_NodeModifiedObserverTags.find(NonConstNode) == m_NodeModifiedObserverTags.end()) { itk::MemberCommand::Pointer nodeModifiedCommand = itk::MemberCommand::New(); nodeModifiedCommand->SetCallbackFunction(this, &DataStorage::OnNodeModifiedOrDeleted); m_NodeModifiedObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand); itk::MemberCommand::Pointer interactorChangedCommand = itk::MemberCommand::New(); interactorChangedCommand->SetCallbackFunction(this, &DataStorage::OnNodeInteractorChanged); m_NodeInteractorChangedObserverTags[NonConstNode] = NonConstNode->AddObserver(DataNode::InteractorChangedEvent(), interactorChangedCommand); // add itk delete listener on datastorage itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); deleteCommand->SetCallbackFunction(this, &DataStorage::OnNodeModifiedOrDeleted); // add observer m_NodeDeleteObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::DeleteEvent(), deleteCommand); } } void mitk::DataStorage::RemoveListeners(const DataNode *_Node) { itk::MutexLockHolder locked(m_MutexOne); // node must not be 0 and must be registered auto *NonConstNode = const_cast(_Node); if (_Node && m_NodeModifiedObserverTags.find(NonConstNode) != m_NodeModifiedObserverTags.end()) { // const cast is bad! but sometimes it is necessary. removing an observer does not really // touch the internal state NonConstNode->RemoveObserver(m_NodeModifiedObserverTags.find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeDeleteObserverTags.find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeInteractorChangedObserverTags.find(NonConstNode)->second); m_NodeModifiedObserverTags.erase(NonConstNode); m_NodeDeleteObserverTags.erase(NonConstNode); m_NodeInteractorChangedObserverTags.erase(NonConstNode); } } mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeBoundingGeometry3D(const SetOfObjects *input, const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) const { if (input == nullptr) throw std::invalid_argument("DataStorage: input is invalid"); BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid = 0; Point3D point; Vector3D minSpacing; minSpacing.Fill(itk::NumericTraits::max()); ScalarType stmax = itk::NumericTraits::max(); ScalarType stmin = itk::NumericTraits::NonpositiveMin(); std::set existingTimePoints; ScalarType maximalTime = 0; // Needed for check of zero bounding boxes ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); for (SetOfObjects::ConstIterator it = input->Begin(); it != input->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *timeGeometry = node->GetData()->GetUpdatedTimeGeometry(); if (timeGeometry != nullptr) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = timeGeometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for (i = 0; i < 8; ++i) { point = timeGeometry->GetCornerPointInWorld(i); if (point[0] * point[0] + point[1] * point[1] + point[2] * point[2] < large) pointscontainer->InsertElement(pointid++, point); else { itkGenericOutputMacro(<< "Unrealistically distant corner point encountered. Ignored. Node: " << node); } } try { // time bounds // iterate over all time steps // Attention: Objects with zero bounding box are not respected in time bound calculation for (TimeStepType i = 0; i < timeGeometry->CountTimeSteps(); i++) { // We must not use 'node->GetData()->GetGeometry(i)->GetSpacing()' here, as it returns the spacing // in its original space, which, in case of an image geometry, can have the values in different // order than in world space. For the further calculations, we need to have the spacing values // in world coordinate order (sag-cor-ax). Vector3D spacing; spacing.Fill(1.0); node->GetData()->GetGeometry(i)->IndexToWorld(spacing, spacing); for (int axis = 0; axis < 3; ++ axis) { ScalarType space = std::abs(spacing[axis]); if (space < minSpacing[axis]) { minSpacing[axis] = space; } } const auto curTimeBounds = timeGeometry->GetTimeBounds(i); if ((curTimeBounds[0] > stmin) && (curTimeBounds[0] < stmax)) { existingTimePoints.insert(curTimeBounds[0]); } if ((curTimeBounds[1] > maximalTime) && (curTimeBounds[1] < stmax)) { maximalTime = curTimeBounds[1]; } } } catch (itk::ExceptionObject &e) { MITK_ERROR << e << std::endl; } } } } BoundingBox::Pointer result = BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); // compute the number of time steps if (existingTimePoints.empty()) // make sure that there is at least one time sliced geometry in the data storage { existingTimePoints.insert(0.0); maximalTime = 1.0; } ArbitraryTimeGeometry::Pointer timeGeometry = nullptr; if (result->GetPoints()->Size() > 0) { // Initialize a geometry of a single time step Geometry3D::Pointer geometry = Geometry3D::New(); geometry->Initialize(); // correct bounding-box (is now in mm, should be in index-coordinates) // according to spacing BoundingBox::BoundsArrayType bounds = result->GetBounds(); AffineTransform3D::OutputVectorType offset; for (int i = 0; i < 3; ++i) { offset[i] = bounds[i * 2]; bounds[i * 2] = 0.0; bounds[i * 2 + 1] = (bounds[i * 2 + 1] - offset[i]) / minSpacing[i]; } geometry->GetIndexToWorldTransform()->SetOffset(offset); geometry->SetBounds(bounds); geometry->SetSpacing(minSpacing); // Initialize the time sliced geometry auto tsIterator = existingTimePoints.cbegin(); auto tsPredecessor = tsIterator++; auto tsEnd = existingTimePoints.cend(); timeGeometry = ArbitraryTimeGeometry::New(); for (; tsIterator != tsEnd; ++tsIterator, ++tsPredecessor) { timeGeometry->AppendNewTimeStep(geometry, *tsPredecessor, *tsIterator); } timeGeometry->AppendNewTimeStep(geometry, *tsPredecessor, maximalTime); timeGeometry->Update(); } return timeGeometry.GetPointer(); } mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeBoundingGeometry3D(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) const { return this->ComputeBoundingGeometry3D(this->GetAll(), boolPropertyKey, renderer, boolPropertyKey2); } mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeVisibleBoundingGeometry3D(const BaseRenderer *renderer, const char *boolPropertyKey) { return ComputeBoundingGeometry3D("visible", renderer, boolPropertyKey); } mitk::BoundingBox::Pointer mitk::DataStorage::ComputeBoundingBox(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) { BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid = 0; Point3D point; // Needed for check of zero bounding boxes ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != nullptr) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = geometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for (i = 0; i < 8; ++i) { point = geometry->GetCornerPointInWorld(i); if (point[0] * point[0] + point[1] * point[1] + point[2] * point[2] < large) pointscontainer->InsertElement(pointid++, point); else { itkGenericOutputMacro(<< "Unrealistically distant corner point encountered. Ignored. Node: " << node); } } } } } BoundingBox::Pointer result = BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } mitk::TimeBounds mitk::DataStorage::ComputeTimeBounds(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) { TimeBounds timeBounds; ScalarType stmin, stmax, cur; stmin = itk::NumericTraits::NonpositiveMin(); stmax = itk::NumericTraits::max(); timeBounds[0] = stmax; timeBounds[1] = stmin; SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != nullptr) { const TimeBounds &curTimeBounds = geometry->GetTimeBounds(); cur = curTimeBounds[0]; // is it after -infinity, but before everything else that we found until now? if ((cur > stmin) && (cur < timeBounds[0])) timeBounds[0] = cur; cur = curTimeBounds[1]; // is it before infinity, but after everything else that we found until now? if ((cur < stmax) && (cur > timeBounds[1])) timeBounds[1] = cur; } } } if (!(timeBounds[0] < stmax)) { timeBounds[0] = stmin; timeBounds[1] = stmax; } return timeBounds; } void mitk::DataStorage::BlockNodeModifiedEvents(bool block) { m_BlockNodeModifiedEvents = block; } mitk::DataNode::Pointer mitk::FindTopmostVisibleNode(const DataStorage::SetOfObjects* nodes, const Point3D worldposition, const TimePointType timePoint, const BaseRenderer* baseRender) { DataNode::Pointer topLayerNode; if (nullptr == nodes) return nullptr; int maxLayer = std::numeric_limits::min(); for (auto node : *nodes) { if (node.IsNull()) continue; bool isHelperObject = false; node->GetBoolProperty("helper object", isHelperObject); if (isHelperObject) continue; auto data = node->GetData(); if (nullptr == data) continue; auto geometry = data->GetGeometry(); if (nullptr == geometry || !geometry->IsInside(worldposition)) continue; auto timeGeometry = data->GetUpdatedTimeGeometry(); if (timeGeometry == nullptr) continue; if (!timeGeometry->IsValidTimePoint(timePoint)) continue; int layer = 0; if (!node->GetIntProperty("layer", layer)) continue; if (layer <= maxLayer) continue; if (!node->IsVisible(baseRender)) continue; topLayerNode = node; maxLayer = layer; } return topLayerNode; } diff --git a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp new file mode 100644 index 0000000000..e4e927d1ef --- /dev/null +++ b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp @@ -0,0 +1,786 @@ +/*=================================================================== + + The Medical Imaging Interaction Toolkit (MITK) + + Copyright (c) German Cancer Research Center, + Division of Medical Image Computing. + All rights reserved. + + This software is distributed WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. + + See LICENSE.txt or http://www.mitk.org for details. + + ===================================================================*/ + +#include "mitkDisplayActionEventBroadcast.h" + + // us +#include "usGetModuleContext.h" +#include "usModuleContext.h" + +// mitk core module +#include "mitkCompositePixelValueToString.h" +#include "mitkDisplayActionEvents.h" +#include "mitkImage.h" +#include "mitkImagePixelReadAccessor.h" +#include "mitkInteractionPositionEvent.h" +#include "mitkLine.h" +#include "mitkNodePredicateDataType.h" +#include "mitkPixelTypeMultiplex.h" +#include "mitkStatusBar.h" + +mitk::DisplayActionEventBroadcast::DisplayActionEventBroadcast() + : m_AlwaysReact(false) + , m_AutoRepeat(false) + , m_IndexToSliceModifier(4) + , m_InvertScrollDirection(false) + , m_InvertZoomDirection(false) + , m_ZoomFactor(2) + , m_InvertMoveDirection(false) + , m_InvertLevelWindowDirection(false) + , m_LinkPlanes(true) +{ + m_StartCoordinateInMM.Fill(0); + m_LastDisplayCoordinate.Fill(0); + m_LastCoordinateInMM.Fill(0); + m_CurrentDisplayCoordinate.Fill(0); + + // register the broadcast class (itself) as an interaction event observer via micro services + us::ServiceProperties props; + props["name"] = std::string("DisplayActionEventBroadcast"); + m_ServiceRegistration = us::GetModuleContext()->RegisterService(this, props); +} + +mitk::DisplayActionEventBroadcast::~DisplayActionEventBroadcast() +{ + m_ServiceRegistration.Unregister(); +} + +void mitk::DisplayActionEventBroadcast::Notify(InteractionEvent* interactionEvent, bool isHandled) +{ + // the event is passed to the state machine interface to be handled + if (!isHandled || m_AlwaysReact) + { + HandleEvent(interactionEvent, nullptr); + } +} + +void mitk::DisplayActionEventBroadcast::ConnectActionsAndFunctions() +{ + CONNECT_CONDITION("check_position_event", CheckPositionEvent); + CONNECT_CONDITION("check_can_rotate", CheckRotationPossible); + CONNECT_CONDITION("check_can_swivel", CheckSwivelPossible); + + CONNECT_FUNCTION("init", Init); + CONNECT_FUNCTION("move", Move); + CONNECT_FUNCTION("zoom", Zoom); + CONNECT_FUNCTION("scroll", Scroll); + CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp); + CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown); + CONNECT_FUNCTION("levelWindow", AdjustLevelWindow); + CONNECT_FUNCTION("setCrosshair", SetCrosshair); + + CONNECT_FUNCTION("updateStatusbar", UpdateStatusbar) + + CONNECT_FUNCTION("startRotation", StartRotation); + CONNECT_FUNCTION("endRotation", EndRotation); + CONNECT_FUNCTION("rotate", Rotate); + + CONNECT_FUNCTION("swivel", Swivel); +} + +void mitk::DisplayActionEventBroadcast::ConfigurationChanged() +{ + mitk::PropertyList::Pointer properties = GetAttributes(); + + // allwaysReact + std::string strAlwaysReact = ""; + m_AlwaysReact = false; + if (properties->GetStringProperty("alwaysReact", strAlwaysReact)) + { + if (strAlwaysReact == "true") + { + m_AlwaysReact = true; + } + } + + // auto repeat + std::string strAutoRepeat = ""; + m_AutoRepeat = false; + if (properties->GetStringProperty("autoRepeat", strAutoRepeat)) + { + if (strAutoRepeat == "true") + { + m_AutoRepeat = true; + } + } + + // pixel movement for scrolling one slice + std::string strPixelPerSlice = ""; + m_IndexToSliceModifier = 4; + if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice)) + { + m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str()); + } + + // scroll direction + if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection)) + { + m_ScrollDirection = "updown"; + } + + m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false); + + // zoom direction + if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection)) + { + m_ZoomDirection = "updown"; + } + + m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false); + m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false); + + if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection)) + { + m_LevelDirection = "leftright"; + } + + m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false); + + // coupled rotation + std::string strCoupled = ""; + m_LinkPlanes = false; + if (properties->GetStringProperty("coupled", strCoupled)) + { + if (strCoupled == "true") + { + m_LinkPlanes = true; + } + } + + // zoom factor + std::string strZoomFactor = ""; + properties->GetStringProperty("zoomFactor", strZoomFactor); + m_ZoomFactor = .05; + if (atoi(strZoomFactor.c_str()) > 0) + { + m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0); + } +} + +bool mitk::DisplayActionEventBroadcast::FilterEvents(InteractionEvent* interactionEvent, DataNode * /*dataNode*/) +{ + mitk::BaseRenderer* sendingRenderer = interactionEvent->GetSender(); + if (nullptr == sendingRenderer) + { + return false; + } + + if (BaseRenderer::Standard3D == sendingRenderer->GetMapperID()) + { + return false; + } + + return true; +} + +bool mitk::DisplayActionEventBroadcast::CheckPositionEvent(const InteractionEvent *interactionEvent) +{ + const auto* positionEvent = dynamic_cast(interactionEvent); + if (nullptr == positionEvent) + { + return false; + } + + return true; +} + +bool mitk::DisplayActionEventBroadcast::CheckRotationPossible(const InteractionEvent *interactionEvent) +{ + // Decide between moving and rotation slices. + /* + Detailed logic: + + 1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be + rotated. Needs not even be counted or checked. + 2. Inspect every other SliceNavigationController + - calculate the line intersection of this SliceNavigationController's plane with our rendering plane + - if there is NO interesection, ignore and continue + - IF there is an intersection + - check the mouse cursor's distance from that line. + 0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in + "locked" mode) + 1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate + 2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to + rotate + - if yes, we just push this line to the "other" lines and rotate it along + - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to + rotate + */ + const auto* positionEvent = dynamic_cast(interactionEvent); + if (nullptr == positionEvent) + { + return false; + } + + BaseRenderer* clickedRenderer = positionEvent->GetSender(); + const PlaneGeometry* ourViewportGeometry = clickedRenderer->GetCurrentWorldPlaneGeometry(); + + if (nullptr == ourViewportGeometry) + { + return false; + } + + Point3D cursorPosition = positionEvent->GetPositionInWorld(); + const auto spacing = ourViewportGeometry->GetSpacing(); + const PlaneGeometry *geometryToBeRotated = nullptr; // this one is under the mouse cursor + const PlaneGeometry *anyOtherGeometry = nullptr; // this is also visible (for calculation of intersection ONLY) + Line3D intersectionLineWithGeometryToBeRotated; + + bool hitMultipleLines(false); + m_SNCsToBeRotated.clear(); + + const double threshholdDistancePixels = 12.0; + + auto renWindows = interactionEvent->GetSender()->GetRenderingManager()->GetAllRegisteredRenderWindows(); + + for (auto renWin : renWindows) + { + SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); + + // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. + if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) + continue; + + const PlaneGeometry *otherRenderersRenderPlane = snc->GetCurrentPlaneGeometry(); + if (nullptr == otherRenderersRenderPlane) + { + continue; // ignore, we don't see a plane + } + + // check if there is an intersection + Line3D intersectionLine; // between rendered/clicked geometry and the one being analyzed + if (!ourViewportGeometry->IntersectionLine(otherRenderersRenderPlane, intersectionLine)) + { + continue; // we ignore this plane, it's parallel to our plane + } + + // check distance from intersection line + const double distanceFromIntersectionLine = intersectionLine.Distance(cursorPosition) / spacing[snc->GetDefaultViewDirection()]; + + // far away line, only remember for linked rotation if necessary + if (distanceFromIntersectionLine > threshholdDistancePixels) + { + anyOtherGeometry = otherRenderersRenderPlane; // we just take the last one, so overwrite each iteration (we just + // need some crossing point) + // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used + if (m_LinkPlanes) + { + m_SNCsToBeRotated.push_back(snc); + } + } + else // close to cursor + { + if (nullptr == geometryToBeRotated) // first one close to the cursor + { + geometryToBeRotated = otherRenderersRenderPlane; + intersectionLineWithGeometryToBeRotated = intersectionLine; + m_SNCsToBeRotated.push_back(snc); + } + else + { + // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane + // together with the primary one + // if different, DON'T rotate + if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated) && + intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < mitk::eps) + { + m_SNCsToBeRotated.push_back(snc); + } + else + { + hitMultipleLines = true; + } + } + } + } + + bool moveSlices(true); + + if (geometryToBeRotated && anyOtherGeometry && ourViewportGeometry && !hitMultipleLines) + { + // assure all three are valid, so calculation of center of rotation can be done + moveSlices = false; + } + // question in state machine is: "rotate?" + if (moveSlices) // i.e. NOT rotate + { + return false; + } + else + { + // we DO have enough information for rotation + // remember where the last cursor position ON THE LINE has been observed + m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project(cursorPosition); + + // find center of rotation by intersection with any of the OTHER lines + if (anyOtherGeometry->IntersectionPoint(intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) + { + return true; + } + else + { + return false; + } + } + return false; +} + +bool mitk::DisplayActionEventBroadcast::CheckSwivelPossible(const InteractionEvent *interactionEvent) +{ + const ScalarType ThresholdDistancePixels = 6.0; + + // Decide between moving and rotation: if we're close to the crossing + // point of the planes, moving mode is entered, otherwise + // rotation/swivel mode + const auto* positionEvent = dynamic_cast(interactionEvent); + if (nullptr == positionEvent) + { + return false; + } + + BaseRenderer *renderer = interactionEvent->GetSender(); + if (nullptr == renderer) + { + return false; + } + + const Point3D& cursor = positionEvent->GetPositionInWorld(); + + m_SNCsToBeRotated.clear(); + + const PlaneGeometry* clickedGeometry(nullptr); + const PlaneGeometry* otherGeometry1(nullptr); + const PlaneGeometry* otherGeometry2(nullptr); + + auto registeredRenderWindows = interactionEvent->GetSender()->GetRenderingManager()->GetAllRegisteredRenderWindows(); + for (auto renWin : registeredRenderWindows) + { + SliceNavigationController* snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); + + // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. + if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) + continue; + + // unsigned int slice = (*iter)->GetSlice()->GetPos(); + // unsigned int time = (*iter)->GetTime()->GetPos(); + + const PlaneGeometry *planeGeometry = snc->GetCurrentPlaneGeometry(); + if (!planeGeometry) + continue; + + if (snc == renderer->GetSliceNavigationController()) + { + clickedGeometry = planeGeometry; + m_SNCsToBeRotated.push_back(snc); + } + else + { + if (otherGeometry1 == nullptr) + { + otherGeometry1 = planeGeometry; + } + else + { + otherGeometry2 = planeGeometry; + } + if (m_LinkPlanes) + { + // If planes are linked, apply rotation to all planes + m_SNCsToBeRotated.push_back(snc); + } + } + } + + Line3D line; + Point3D point; + if ((clickedGeometry != nullptr) && (otherGeometry1 != nullptr) && (otherGeometry2 != nullptr) && + clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point)) + { + m_CenterOfRotation = point; + if (m_CenterOfRotation.EuclideanDistanceTo(cursor) < ThresholdDistancePixels) + { + return false; + } + else + { + m_ReferenceCursor = positionEvent->GetPointerPositionOnScreen(); + + // Get main axes of rotation plane and store it for rotation step + m_RotationPlaneNormal = clickedGeometry->GetNormal(); + + ScalarType xVector[] = { 1.0, 0.0, 0.0 }; + ScalarType yVector[] = { 0.0, 1.0, 0.0 }; + clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector); + clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(yVector), m_RotationPlaneYVector); + + m_RotationPlaneNormal.Normalize(); + m_RotationPlaneXVector.Normalize(); + m_RotationPlaneYVector.Normalize(); + + m_PreviousRotationAxis.Fill(0.0); + m_PreviousRotationAxis[2] = 1.0; + m_PreviousRotationAngle = 0.0; + + return true; + } + } + else + { + return false; + } + return false; +} + +void mitk::DisplayActionEventBroadcast::Init(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +{ + const auto* positionEvent = dynamic_cast(interactionEvent); + if (nullptr == positionEvent) + { + return; + } + + m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); + m_CurrentDisplayCoordinate = m_LastDisplayCoordinate; + positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM); + m_LastCoordinateInMM = m_StartCoordinateInMM; +} + +void mitk::DisplayActionEventBroadcast::Move(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +{ + const auto* positionEvent = dynamic_cast(interactionEvent); + if (nullptr == positionEvent) + { + return; + } + + BaseRenderer* sender = interactionEvent->GetSender(); + Vector2D moveVector = m_LastDisplayCoordinate - positionEvent->GetPointerPositionOnScreen(); + + if (m_InvertMoveDirection) + { + moveVector *= -1.0; + } + + moveVector *= sender->GetScaleFactorMMPerDisplayUnit(); // #TODO: put here? + + // store new display coordinate + m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); + + // propagate move event with computed geometry values + InvokeEvent(DisplayMoveEvent(interactionEvent, moveVector)); +} + +void mitk::DisplayActionEventBroadcast::SetCrosshair(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +{ + const auto* positionEvent = dynamic_cast(interactionEvent); + if (nullptr == positionEvent) + { + return; + } + + Point3D position = positionEvent->GetPositionInWorld(); + + // propagate set crosshair event with computed geometry values + InvokeEvent(DisplaySetCrosshairEvent(interactionEvent, position)); +} + +void mitk::DisplayActionEventBroadcast::Zoom(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +{ + const auto* positionEvent = dynamic_cast(interactionEvent); + if (nullptr == positionEvent) + { + return; + } + + float factor = 1.0; + float distance = 0; + + if (m_ZoomDirection == "updown") + { + distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; + } + else + { + distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; + } + + if (m_InvertZoomDirection) + { + distance *= -1.0; + } + + // set zooming speed + if (distance < 0.0) + { + factor = 1.0 / m_ZoomFactor; + } + else if (distance > 0.0) + { + factor = 1.0 * m_ZoomFactor; + } + + // store new display coordinates + m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; + m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); + + // propagate zoom event with computed geometry values + InvokeEvent(DisplayZoomEvent(interactionEvent, factor, m_StartCoordinateInMM)); +} + +void mitk::DisplayActionEventBroadcast::Scroll(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +{ + const auto* positionEvent = dynamic_cast(interactionEvent); + if (nullptr == positionEvent) + { + return; + } + + int sliceDelta = 0; + + // scroll direction + if (m_ScrollDirection == "updown") + { + sliceDelta = static_cast(m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]); + } + else + { + sliceDelta = static_cast(m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]); + } + + if (m_InvertScrollDirection) + { + sliceDelta *= -1; + } + + // set how many pixels the mouse has to be moved to scroll one slice + // if the mouse has been moved less than 'm_IndexToSliceModifier', pixels slice ONE slice only + if (sliceDelta > 0 && sliceDelta < m_IndexToSliceModifier) + { + sliceDelta = m_IndexToSliceModifier; + } + else if (sliceDelta < 0 && sliceDelta > -m_IndexToSliceModifier) + { + sliceDelta = -m_IndexToSliceModifier; + } + sliceDelta /= m_IndexToSliceModifier; + + // store new display coordinates + m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; + m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); + + // propagate scroll event with computed geometry values + InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta)); +} + +void mitk::DisplayActionEventBroadcast::ScrollOneUp(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +{ + int sliceDelta = 1; + if (m_InvertScrollDirection) + { + sliceDelta = -1; + } + + // propagate scroll event with a single slice delta (increase) + InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta)); +} + +void mitk::DisplayActionEventBroadcast::ScrollOneDown(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +{ + int sliceDelta = -1; + if (m_InvertScrollDirection) + { + sliceDelta = 1; + } + + // propagate scroll event with a single slice delta (decrease) + InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta)); +} + +void mitk::DisplayActionEventBroadcast::AdjustLevelWindow(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +{ + const auto* positionEvent = dynamic_cast(interactionEvent); + if (nullptr == positionEvent) + { + return; + } + + mitk::ScalarType level; + mitk::ScalarType window; + + if (m_LevelDirection == "leftright") + { + level = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; + window = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; + } + else + { + level = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; + window = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; + } + + if (m_InvertLevelWindowDirection) + { + level *= -1; + window *= -1; + } + + level *= static_cast(2); + window *= static_cast(2); + + // store new display coordinates + m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; + m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); + + // propagate set level window event with the level and window delta + InvokeEvent(DisplaySetLevelWindowEvent(interactionEvent, level, window)); +} + +void mitk::DisplayActionEventBroadcast::StartRotation(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +{ + // nothing here; no event sent +} + +void mitk::DisplayActionEventBroadcast::EndRotation(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +{ + // nothing here; no event sent +} + +void mitk::DisplayActionEventBroadcast::Rotate(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +{ + // nothing here; no event sent +} + +void mitk::DisplayActionEventBroadcast::Swivel(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +{ + // nothing here; no event sent +} + +void mitk::DisplayActionEventBroadcast::UpdateStatusbar(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) +{ + const auto* positionEvent = dynamic_cast(interactionEvent); + if (nullptr == positionEvent) + { + return; + } + + mitk::BaseRenderer::Pointer renderer = positionEvent->GetSender(); + + TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); + mitk::DataStorage::SetOfObjects::ConstPointer nodes = renderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); + if (nodes.IsNull()) + { + return; + } + + Point3D worldposition; + renderer->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), worldposition); + + mitk::Image::Pointer image3D; + mitk::DataNode::Pointer node; + mitk::DataNode::Pointer topSourceNode; + + int component = 0; + + node = renderer->GetDataStorage()->GetTopLayerNode(nodes, worldposition, renderer); + if (node.IsNull()) + { + return; + } + + bool isBinary(false); + node->GetBoolProperty("binary", isBinary); + if (isBinary) + { + mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = renderer->GetDataStorage()->GetSources(node, nullptr, true); + if (!sourcenodes->empty()) + { + topSourceNode = renderer->GetDataStorage()->GetTopLayerNode(sourcenodes, worldposition, renderer); + } + if (topSourceNode.IsNotNull()) + { + image3D = dynamic_cast(topSourceNode->GetData()); + topSourceNode->GetIntProperty("Image.Displayed Component", component); + } + else + { + image3D = dynamic_cast(node->GetData()); + node->GetIntProperty("Image.Displayed Component", component); + } + } + else + { + image3D = dynamic_cast(node->GetData()); + node->GetIntProperty("Image.Displayed Component", component); + } + + // get the position and pixel value from the image and build up status bar text + auto statusBar = mitk::StatusBar::GetInstance(); + if (image3D.IsNotNull() && statusBar != nullptr) + { + itk::Index<3> p; + image3D->GetGeometry()->WorldToIndex(worldposition, p); + + auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType(); + if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA) + { + std::string pixelValue = "Pixel RGB(A) value: "; + pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); + statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue.c_str()); + } + else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR) + { + std::string pixelValue = "See ODF Details view. "; + statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue.c_str()); + } + else + { + mitk::ScalarType pixelValue; + mitkPixelTypeMultiplex5( + mitk::FastSinglePixelAccess, + image3D->GetChannelDescriptor().GetPixelType(), + image3D, + image3D->GetVolumeData(renderer->GetTimeStep()), + p, + pixelValue, + component); + statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue); + } + } + else + { + statusBar->DisplayImageInfoInvalid(); + } +} + +bool mitk::DisplayActionEventBroadcast::GetBoolProperty(mitk::PropertyList::Pointer propertyList, const char* propertyName, bool defaultValue) +{ + std::string valueAsString; + if (!propertyList->GetStringProperty(propertyName, valueAsString)) + { + return defaultValue; + } + else + { + if (valueAsString == "true") + { + return true; + } + else + { + return false; + } + } +} diff --git a/Modules/Core/src/Interactions/mitkDisplayActionEventFunctions.cpp b/Modules/Core/src/Interactions/mitkDisplayActionEventFunctions.cpp new file mode 100644 index 0000000000..71bf9fe76f --- /dev/null +++ b/Modules/Core/src/Interactions/mitkDisplayActionEventFunctions.cpp @@ -0,0 +1,330 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkDisplayActionEventFunctions.h" + +// mitk core +#include "mitkBaseRenderer.h" +#include "mitkCameraController.h" +#include "mitkDisplayActionEvents.h" +#include "mitkInteractionPositionEvent.h" +#include "mitkLevelWindow.h" +#include "mitkLevelWindowProperty.h" +#include "mitkNodePredicateDataType.h" + +////////////////////////////////////////////////////////////////////////// +// STANDARD FUNCTIONS +////////////////////////////////////////////////////////////////////////// +mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::MoveSenderCameraAction() +{ + mitk::StdFunctionCommand::ActionFunction actionFunction = [](const itk::EventObject& displayInteractorEvent) + { + if (DisplayMoveEvent().CheckEvent(&displayInteractorEvent)) + { + const DisplayMoveEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); + const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); + if (nullptr == sendingRenderer) + { + return; + } + + // concrete action + sendingRenderer->GetCameraController()->MoveBy(displayActionEvent->GetMoveVector()); + sendingRenderer->GetRenderingManager()->RequestUpdate(sendingRenderer->GetRenderWindow()); + } + }; + + return actionFunction; +} + +mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::SetCrosshairAction() +{ + auto actionFunction = [](const itk::EventObject& displayInteractorEvent) + { + if (DisplaySetCrosshairEvent().CheckEvent(&displayInteractorEvent)) + { + const DisplaySetCrosshairEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); + const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); + if (nullptr == sendingRenderer) + { + return; + } + + // concrete action + BaseRenderer::GetInstance(sendingRenderer->GetRenderWindow())->GetSliceNavigationController()->SelectSliceByPoint(displayActionEvent->GetPosition()); + } + }; + + return actionFunction; +} + +mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::ZoomSenderCameraAction() +{ + auto actionFunction = [](const itk::EventObject& displayInteractorEvent) + { + if (DisplayZoomEvent().CheckEvent(&displayInteractorEvent)) + { + const DisplayZoomEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); + const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); + if (nullptr == sendingRenderer) + { + return; + } + + // concrete action + if (1.0 != displayActionEvent->GetZoomFactor()) + { + sendingRenderer->GetCameraController()->Zoom(displayActionEvent->GetZoomFactor(), displayActionEvent->GetStartCoordinate()); + sendingRenderer->GetRenderingManager()->RequestUpdate(sendingRenderer->GetRenderWindow()); + } + } + }; + + return actionFunction; +} + +mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::ScrollSliceStepperAction() +{ + auto actionFunction = [](const itk::EventObject& displayInteractorEvent) + { + if (DisplayScrollEvent().CheckEvent(&displayInteractorEvent)) + { + const DisplayScrollEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); + const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); + if (nullptr == sendingRenderer) + { + return; + } + + // concrete action + mitk::SliceNavigationController* sliceNavigationController = sendingRenderer->GetSliceNavigationController(); + if (nullptr == sliceNavigationController) + { + return; + } + if (sliceNavigationController->GetSliceLocked()) + { + return; + } + mitk::Stepper* sliceStepper = sliceNavigationController->GetSlice(); + if (nullptr == sliceStepper) + { + return; + } + + // if only a single slice image was loaded, scrolling will affect the time steps + if (sliceStepper->GetSteps() <= 1) + { + sliceStepper = sliceNavigationController->GetTime(); + } + + sliceStepper->MoveSlice(displayActionEvent->GetSliceDelta()); + } + }; + + return actionFunction; +} + +mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::SetLevelWindowAction() +{ + auto actionFunction = [](const itk::EventObject& displayInteractorEvent) + { + if (DisplaySetLevelWindowEvent().CheckEvent(&displayInteractorEvent)) + { + const DisplaySetLevelWindowEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); + const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); + if (nullptr == sendingRenderer) + { + return; + } + + // concrete action + + // get the the topmost visible image of the sending renderer + DataStorage::Pointer storage = sendingRenderer->GetDataStorage(); + DataStorage::SetOfObjects::ConstPointer allImageNodes = storage->GetSubset(NodePredicateDataType::New("Image")); + Point3D worldposition; + const auto* positionEvent = dynamic_cast(displayActionEvent->GetInteractionEvent()); + sendingRenderer->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), worldposition); + DataNode::Pointer node = storage->GetTopLayerNode(allImageNodes, worldposition, sendingRenderer); + if (node.IsNull()) + { + return; + } + + LevelWindow levelWindow = LevelWindow(); + node->GetLevelWindow(levelWindow); + ScalarType level = levelWindow.GetLevel(); + ScalarType window = levelWindow.GetWindow(); + + level += displayActionEvent->GetLevel(); + window += displayActionEvent->GetWindow(); + + levelWindow.SetLevelWindow(level, window); + auto* levelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); + if (nullptr != levelWindowProperty) + { + levelWindowProperty->SetLevelWindow(levelWindow); + sendingRenderer->GetRenderingManager()->RequestUpdateAll(); + } + } + }; + + return actionFunction; +} + +////////////////////////////////////////////////////////////////////////// +// SYNCHRONIZED FUNCTIONS +////////////////////////////////////////////////////////////////////////// +mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::MoveCameraSynchronizedAction() +{ + mitk::StdFunctionCommand::ActionFunction actionFunction = [](const itk::EventObject& displayInteractorEvent) + { + if (DisplayMoveEvent().CheckEvent(&displayInteractorEvent)) + { + const DisplayMoveEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); + const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); + if (nullptr == sendingRenderer) + { + return; + } + + // concrete action + auto allRenderWindows = sendingRenderer->GetRenderingManager()->GetAllRegisteredRenderWindows(); + for (auto renderWindow : allRenderWindows) + { + if (BaseRenderer::GetInstance(renderWindow)->GetMapperID() == BaseRenderer::Standard2D) + { + BaseRenderer* currentRenderer = BaseRenderer::GetInstance(renderWindow); + currentRenderer->GetCameraController()->MoveBy(displayActionEvent->GetMoveVector()); + currentRenderer->GetRenderingManager()->RequestUpdate(currentRenderer->GetRenderWindow()); + } + } + } + }; + + return actionFunction; +} + +mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::SetCrosshairSynchronizedAction() +{ + auto actionFunction = [](const itk::EventObject& displayInteractorEvent) + { + if (DisplaySetCrosshairEvent().CheckEvent(&displayInteractorEvent)) + { + const DisplaySetCrosshairEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); + const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); + if (nullptr == sendingRenderer) + { + return; + } + + // concrete action + auto allRenderWindows = sendingRenderer->GetRenderingManager()->GetAllRegisteredRenderWindows(); + for (auto renderWindow : allRenderWindows) + { + if (BaseRenderer::GetInstance(renderWindow)->GetMapperID() == BaseRenderer::Standard2D) + { + BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController()->SelectSliceByPoint(displayActionEvent->GetPosition()); + } + } + } + }; + + return actionFunction; +} + +mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::ZoomCameraSynchronizedAction() +{ + auto actionFunction = [](const itk::EventObject& displayInteractorEvent) + { + if (DisplayZoomEvent().CheckEvent(&displayInteractorEvent)) + { + const DisplayZoomEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); + const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); + if (nullptr == sendingRenderer) + { + return; + } + + // concrete action + if (1.0 != displayActionEvent->GetZoomFactor()) + { + auto allRenderWindows = sendingRenderer->GetRenderingManager()->GetAllRegisteredRenderWindows(); + for (auto renderWindow : allRenderWindows) + { + if (BaseRenderer::GetInstance(renderWindow)->GetMapperID() == BaseRenderer::Standard2D) + { + BaseRenderer* currentRenderer = BaseRenderer::GetInstance(renderWindow); + currentRenderer->GetCameraController()->Zoom(displayActionEvent->GetZoomFactor(), displayActionEvent->GetStartCoordinate()); + currentRenderer->GetRenderingManager()->RequestUpdate(currentRenderer->GetRenderWindow()); + } + } + } + } + }; + + return actionFunction; +} + +mitk::StdFunctionCommand::ActionFunction mitk::DisplayActionEventFunctions::ScrollSliceStepperSynchronizedAction() +{ + auto actionFunction = [](const itk::EventObject& displayInteractorEvent) + { + if (DisplayScrollEvent().CheckEvent(&displayInteractorEvent)) + { + const DisplayScrollEvent* displayActionEvent = dynamic_cast(&displayInteractorEvent); + const BaseRenderer::Pointer sendingRenderer = displayActionEvent->GetSender(); + if (nullptr == sendingRenderer) + { + return; + } + + // concrete action + auto allRenderWindows = sendingRenderer->GetRenderingManager()->GetAllRegisteredRenderWindows(); + for (auto renderWindow : allRenderWindows) + { + if (BaseRenderer::GetInstance(renderWindow)->GetMapperID() == BaseRenderer::Standard2D) + { + mitk::SliceNavigationController* sliceNavigationController = BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController(); + if (nullptr == sliceNavigationController) + { + return; + } + if (sliceNavigationController->GetSliceLocked()) + { + return; + } + mitk::Stepper* sliceStepper = sliceNavigationController->GetSlice(); + if (nullptr == sliceStepper) + { + return; + } + + // if only a single slice image was loaded, scrolling will affect the time steps + if (sliceStepper->GetSteps() <= 1) + { + sliceStepper = sliceNavigationController->GetTime(); + } + + sliceStepper->MoveSlice(displayActionEvent->GetSliceDelta()); + } + } + } + }; + + return actionFunction; +} diff --git a/Modules/Core/src/Interactions/mitkDisplayActionEventHandler.cpp b/Modules/Core/src/Interactions/mitkDisplayActionEventHandler.cpp new file mode 100644 index 0000000000..6702cbae73 --- /dev/null +++ b/Modules/Core/src/Interactions/mitkDisplayActionEventHandler.cpp @@ -0,0 +1,73 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkDisplayActionEventHandler.h" + +void mitk::DisplayActionEventHandler::SetObservableBroadcast(mitk::DisplayActionEventBroadcast* observableBroadcast) +{ + if (m_ObservableBroadcast == observableBroadcast) + { + // no need to update the broadcast class + return; + } + + if (m_ObservableBroadcast.IsNotNull()) + { + // remove current observer + for (const auto& tag : m_ObserverTags) + { + m_ObservableBroadcast->RemoveObserver(tag); + } + m_ObserverTags.clear(); + } + + // set new broadcast class (possibly nullptr) + m_ObservableBroadcast = observableBroadcast; +} + +mitk::DisplayActionEventHandler::OberserverTagType mitk::DisplayActionEventHandler::ConnectDisplayActionEvent(const mitk::DisplayActionEvent& displayActionEvent, + const mitk::StdFunctionCommand::ActionFunction& actionFunction, + const mitk::StdFunctionCommand::FilterFunction& filterFunction) +{ + // #TODO: change function call for new mitk::WeakPointer + if (m_ObservableBroadcast.IsNull()) + { + mitkThrow() << "No display action event broadcast class set to observe. Use 'SetObservableBroadcast' before connecting events."; + } + + auto command = mitk::StdFunctionCommand::New(); + command->SetCommandAction(actionFunction); + command->SetCommandFilter(filterFunction); + OberserverTagType tag = m_ObservableBroadcast->AddObserver(displayActionEvent, command); + m_ObserverTags.push_back(tag); + return tag; +} + +void mitk::DisplayActionEventHandler::DisconnectObserver(OberserverTagType observerTag) +{ + // #TODO: change function call for new mitk::WeakPointer + if (m_ObservableBroadcast.IsNull()) + { + mitkThrow() << "No display action event broadcast class set to observe. Use 'SetObservableBroadcast' before disconnecting observer."; + } + + std::vector::iterator observerTagPosition = std::find(m_ObserverTags.begin(), m_ObserverTags.end(), observerTag); + if (observerTagPosition != m_ObserverTags.end()) + { + m_ObservableBroadcast->RemoveObserver(observerTag); + m_ObserverTags.erase(observerTagPosition); + } +} diff --git a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp index 229a555fdb..7053492979 100644 --- a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp @@ -1,945 +1,993 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDisplayInteractor.h" + #include "mitkBaseRenderer.h" #include "mitkCameraController.h" #include "mitkInteractionPositionEvent.h" #include "mitkPropertyList.h" #include #include #include // level window #include "mitkLevelWindow.h" #include "mitkLevelWindowProperty.h" #include "mitkLine.h" #include "mitkNodePredicateDataType.h" #include "mitkStandaloneDataStorage.h" #include "vtkRenderWindowInteractor.h" // Rotation #include "mitkInteractionConst.h" #include "rotate_cursor.xpm" #include #include #include "mitkImage.h" #include "mitkImagePixelReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include "mitkStatusBar.h" #include +#include "mitkDisplayActionEvents.h" + void mitk::DisplayInteractor::Notify(InteractionEvent *interactionEvent, bool isHandled) { // to use the state machine pattern, // the event is passed to the state machine interface to be handled if (!isHandled || m_AlwaysReact) { - this->HandleEvent(interactionEvent, nullptr); + HandleEvent(interactionEvent, nullptr); } } +mitk::DisplayInteractor::DisplayInteractor() + : m_IndexToSliceModifier(4) + , m_AutoRepeat(false) + , m_InvertScrollDirection(false) + , m_InvertZoomDirection(false) + , m_InvertMoveDirection(false) + , m_InvertLevelWindowDirection(false) + , m_AlwaysReact(false) + , m_ZoomFactor(2) + , m_LinkPlanes(true) +{ + m_StartCoordinateInMM.Fill(0); + m_LastDisplayCoordinate.Fill(0); + m_LastCoordinateInMM.Fill(0); + m_CurrentDisplayCoordinate.Fill(0); +} + +mitk::DisplayInteractor::~DisplayInteractor() +{ + // nothing here +} + void mitk::DisplayInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("check_position_event", CheckPositionEvent); CONNECT_CONDITION("check_can_rotate", CheckRotationPossible); CONNECT_CONDITION("check_can_swivel", CheckSwivelPossible); CONNECT_FUNCTION("init", Init); CONNECT_FUNCTION("move", Move); CONNECT_FUNCTION("zoom", Zoom); CONNECT_FUNCTION("scroll", Scroll); CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown); CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp); CONNECT_FUNCTION("levelWindow", AdjustLevelWindow); CONNECT_FUNCTION("setCrosshair", SetCrosshair); CONNECT_FUNCTION("updateStatusbar", UpdateStatusbar) CONNECT_FUNCTION("startRotation", StartRotation); CONNECT_FUNCTION("endRotation", EndRotation); CONNECT_FUNCTION("rotate", Rotate); CONNECT_FUNCTION("swivel", Swivel); CONNECT_FUNCTION("IncreaseTimeStep", IncreaseTimeStep); CONNECT_FUNCTION("DecreaseTimeStep", DecreaseTimeStep); } -mitk::DisplayInteractor::DisplayInteractor() - : m_IndexToSliceModifier(4), - m_AutoRepeat(false), - m_InvertScrollDirection(false), - m_InvertZoomDirection(false), - m_InvertMoveDirection(false), - m_InvertLevelWindowDirection(false), - m_AlwaysReact(false), - m_ZoomFactor(2), - m_LinkPlanes(true) -{ - m_StartCoordinateInMM.Fill(0); - m_LastDisplayCoordinate.Fill(0); - m_LastCoordinateInMM.Fill(0); - m_CurrentDisplayCoordinate.Fill(0); -} - -mitk::DisplayInteractor::~DisplayInteractor() -{ -} - bool mitk::DisplayInteractor::CheckPositionEvent(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) { return false; } return true; } bool mitk::DisplayInteractor::CheckRotationPossible(const mitk::InteractionEvent *interactionEvent) { // Decide between moving and rotation slices. /* Detailed logic: 1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be rotated. Needs not even be counted or checked. 2. Inspect every other SliceNavigationController - calculate the line intersection of this SliceNavigationController's plane with our rendering plane - if there is NO interesection, ignore and continue - IF there is an intersection - check the mouse cursor's distance from that line. 0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in "locked" mode) 1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate 2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to rotate - if yes, we just push this line to the "other" lines and rotate it along - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to rotate */ const auto *posEvent = dynamic_cast(interactionEvent); if (posEvent == nullptr) return false; BaseRenderer *clickedRenderer = posEvent->GetSender(); const PlaneGeometry *ourViewportGeometry = (clickedRenderer->GetCurrentWorldPlaneGeometry()); if (!ourViewportGeometry) return false; Point3D cursorPosition = posEvent->GetPositionInWorld(); const auto spacing = ourViewportGeometry->GetSpacing(); const PlaneGeometry *geometryToBeRotated = nullptr; // this one is under the mouse cursor const PlaneGeometry *anyOtherGeometry = nullptr; // this is also visible (for calculation of intersection ONLY) Line3D intersectionLineWithGeometryToBeRotated; bool hitMultipleLines(false); m_SNCsToBeRotated.clear(); const double threshholdDistancePixels = 12.0; auto renWindows = interactionEvent->GetSender()->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; const PlaneGeometry *otherRenderersRenderPlane = snc->GetCurrentPlaneGeometry(); if (otherRenderersRenderPlane == nullptr) continue; // ignore, we don't see a plane // check if there is an intersection Line3D intersectionLine; // between rendered/clicked geometry and the one being analyzed if (!ourViewportGeometry->IntersectionLine(otherRenderersRenderPlane, intersectionLine)) { continue; // we ignore this plane, it's parallel to our plane } // check distance from intersection line const double distanceFromIntersectionLine = intersectionLine.Distance(cursorPosition) / spacing[snc->GetDefaultViewDirection()]; // far away line, only remember for linked rotation if necessary if (distanceFromIntersectionLine > threshholdDistancePixels) { anyOtherGeometry = otherRenderersRenderPlane; // we just take the last one, so overwrite each iteration (we just // need some crossing point) // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used if (m_LinkPlanes) { m_SNCsToBeRotated.push_back(snc); } } else // close to cursor { if (geometryToBeRotated == nullptr) // first one close to the cursor { geometryToBeRotated = otherRenderersRenderPlane; intersectionLineWithGeometryToBeRotated = intersectionLine; m_SNCsToBeRotated.push_back(snc); } else { // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane // together with the primary one // if different, DON'T rotate if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated) && intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < mitk::eps) { m_SNCsToBeRotated.push_back(snc); } else { hitMultipleLines = true; } } } } bool moveSlices(true); if (geometryToBeRotated && anyOtherGeometry && ourViewportGeometry && !hitMultipleLines) { // assure all three are valid, so calculation of center of rotation can be done moveSlices = false; } // question in state machine is: "rotate?" if (moveSlices) // i.e. NOT rotate { return false; } else { // we DO have enough information for rotation m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project( cursorPosition); // remember where the last cursor position ON THE LINE has been observed if (anyOtherGeometry->IntersectionPoint( intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) // find center of rotation by intersection with any of the OTHER lines { return true; } else { return false; } } return false; } bool mitk::DisplayInteractor::CheckSwivelPossible(const mitk::InteractionEvent *interactionEvent) { const ScalarType ThresholdDistancePixels = 6.0; // Decide between moving and rotation: if we're close to the crossing // point of the planes, moving mode is entered, otherwise // rotation/swivel mode const auto *posEvent = dynamic_cast(interactionEvent); BaseRenderer *renderer = interactionEvent->GetSender(); if (!posEvent || !renderer) return false; const Point3D &cursor = posEvent->GetPositionInWorld(); m_SNCsToBeRotated.clear(); const PlaneGeometry *clickedGeometry(nullptr); const PlaneGeometry *otherGeometry1(nullptr); const PlaneGeometry *otherGeometry2(nullptr); auto renWindows = interactionEvent->GetSender()->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; // unsigned int slice = (*iter)->GetSlice()->GetPos(); // unsigned int time = (*iter)->GetTime()->GetPos(); const PlaneGeometry *planeGeometry = snc->GetCurrentPlaneGeometry(); if (!planeGeometry) continue; if (snc == renderer->GetSliceNavigationController()) { clickedGeometry = planeGeometry; m_SNCsToBeRotated.push_back(snc); } else { if (otherGeometry1 == nullptr) { otherGeometry1 = planeGeometry; } else { otherGeometry2 = planeGeometry; } if (m_LinkPlanes) { // If planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } } mitk::Line3D line; mitk::Point3D point; if ((clickedGeometry != nullptr) && (otherGeometry1 != nullptr) && (otherGeometry2 != nullptr) && clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point)) { m_CenterOfRotation = point; if (m_CenterOfRotation.EuclideanDistanceTo(cursor) < ThresholdDistancePixels) { return false; } else { m_ReferenceCursor = posEvent->GetPointerPositionOnScreen(); // Get main axes of rotation plane and store it for rotation step m_RotationPlaneNormal = clickedGeometry->GetNormal(); ScalarType xVector[] = {1.0, 0.0, 0.0}; ScalarType yVector[] = {0.0, 1.0, 0.0}; clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector); clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(yVector), m_RotationPlaneYVector); m_RotationPlaneNormal.Normalize(); m_RotationPlaneXVector.Normalize(); m_RotationPlaneYVector.Normalize(); m_PreviousRotationAxis.Fill(0.0); m_PreviousRotationAxis[2] = 1.0; m_PreviousRotationAngle = 0.0; return true; } } else { return false; } return false; } void mitk::DisplayInteractor::Init(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); m_CurrentDisplayCoordinate = m_LastDisplayCoordinate; positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM); m_LastCoordinateInMM = m_StartCoordinateInMM; } void mitk::DisplayInteractor::Move(StateMachineAction *, InteractionEvent *interactionEvent) { BaseRenderer *sender = interactionEvent->GetSender(); auto *positionEvent = static_cast(interactionEvent); float invertModifier = -1.0; if (m_InvertMoveDirection) { invertModifier = 1.0; } // perform translation Vector2D moveVector = (positionEvent->GetPointerPositionOnScreen() - m_LastDisplayCoordinate) * invertModifier; moveVector *= sender->GetScaleFactorMMPerDisplayUnit(); + sender->GetCameraController()->MoveBy(moveVector); sender->GetRenderingManager()->RequestUpdate(sender->GetRenderWindow()); + /* + auto renWindows = sender->GetRenderingManager()->GetAllRegisteredRenderWindows(); + + for (auto renWin : renWindows) + { + if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D) + { + BaseRenderer::GetInstance(renWin)->GetCameraController()->MoveBy(moveVector); + BaseRenderer::GetInstance(renWin)->GetRenderingManager()->RequestUpdate(renWin); + } + } + */ + m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); } void mitk::DisplayInteractor::SetCrosshair(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { - const BaseRenderer::Pointer sender = interactionEvent->GetSender(); - auto renWindows = sender->GetRenderingManager()->GetAllRegisteredRenderWindows(); - auto *positionEvent = static_cast(interactionEvent); + auto* positionEvent = static_cast(interactionEvent); Point3D pos = positionEvent->GetPositionInWorld(); + const BaseRenderer::Pointer sender = interactionEvent->GetSender(); + auto renWindows = sender->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && - renWin != sender->GetRenderWindow()) + renWin != sender->GetRenderWindow()) + { BaseRenderer::GetInstance(renWin)->GetSliceNavigationController()->SelectSliceByPoint(pos); + } } } void mitk::DisplayInteractor::IncreaseTimeStep(StateMachineAction *, InteractionEvent *interactionEvent) { auto sliceNaviController = interactionEvent->GetSender()->GetRenderingManager()->GetTimeNavigationController(); auto stepper = sliceNaviController->GetTime(); stepper->SetAutoRepeat(true); stepper->Next(); } void mitk::DisplayInteractor::DecreaseTimeStep(StateMachineAction *, InteractionEvent *interactionEvent) { auto sliceNaviController = interactionEvent->GetSender()->GetRenderingManager()->GetTimeNavigationController(); auto stepper = sliceNaviController->GetTime(); stepper->SetAutoRepeat(true); stepper->Previous(); } void mitk::DisplayInteractor::Zoom(StateMachineAction *, InteractionEvent *interactionEvent) { - const BaseRenderer::Pointer sender = interactionEvent->GetSender(); - auto *positionEvent = static_cast(interactionEvent); - float factor = 1.0; float distance = 0; if (m_ZoomDirection == "updown") { distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } if (m_InvertZoomDirection) { distance *= -1.0; } // set zooming speed if (distance < 0.0) { factor = 1.0 / m_ZoomFactor; } else if (distance > 0.0) { factor = 1.0 * m_ZoomFactor; } + auto* positionEvent = static_cast(interactionEvent); + m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; + m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); + if (factor != 1.0) { + const BaseRenderer::Pointer sender = interactionEvent->GetSender(); sender->GetCameraController()->Zoom(factor, m_StartCoordinateInMM); sender->GetRenderingManager()->RequestUpdate(sender->GetRenderWindow()); - } - m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; - m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); + //InvokeEvent(DisplayZoomEvent(interactionEvent, factor, m_StartCoordinateInMM)); + /* + auto renWindows = sender->GetRenderingManager()->GetAllRegisteredRenderWindows(); + for (auto renWin : renWindows) + { + if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && + renWin != sender->GetRenderWindow()) + { + BaseRenderer::GetInstance(renWin)->GetCameraController()->Zoom(factor, m_StartCoordinateInMM); + BaseRenderer::GetInstance(renWin)->GetRenderingManager()->RequestUpdate(sender->GetRenderWindow()); + } + } + */ + } } void mitk::DisplayInteractor::Scroll(StateMachineAction *, InteractionEvent *interactionEvent) { - auto *positionEvent = static_cast(interactionEvent); + auto* positionEvent = static_cast(interactionEvent); - mitk::SliceNavigationController::Pointer sliceNaviController = - interactionEvent->GetSender()->GetSliceNavigationController(); + mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (sliceNaviController) { int delta = 0; // Scrolling direction if (m_ScrollDirection == "updown") { delta = static_cast(m_LastDisplayCoordinate[1] - positionEvent->GetPointerPositionOnScreen()[1]); } else { delta = static_cast(m_LastDisplayCoordinate[0] - positionEvent->GetPointerPositionOnScreen()[0]); } if (m_InvertScrollDirection) { delta *= -1; } // Set how many pixels the mouse has to be moved to scroll one slice // if we moved less than 'm_IndexToSliceModifier' pixels slice ONE slice only if (delta > 0 && delta < m_IndexToSliceModifier) { delta = m_IndexToSliceModifier; } else if (delta < 0 && delta > -m_IndexToSliceModifier) { delta = -m_IndexToSliceModifier; } delta /= m_IndexToSliceModifier; int newPos = sliceNaviController->GetSlice()->GetPos() + delta; // if auto repeat is on, start at first slice if you reach the last slice and vice versa int maxSlices = sliceNaviController->GetSlice()->GetSteps(); if (m_AutoRepeat) { while (newPos < 0) { newPos += maxSlices; } while (newPos >= maxSlices) { newPos -= maxSlices; } } else { // if the new slice is below 0 we still show slice 0 // due to the stepper using unsigned int we have to do this ourselves if (newPos < 1) { newPos = 0; } } - // set the new position - sliceNaviController->GetSlice()->SetPos(newPos); + m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); + + // set the new position + sliceNaviController->GetSlice()->SetPos(newPos); + /* + const BaseRenderer::Pointer sender = interactionEvent->GetSender(); + auto renWindows = sender->GetRenderingManager()->GetAllRegisteredRenderWindows(); + for (auto renWin : renWindows) + { + if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && + renWin != sender->GetRenderWindow()) + { + BaseRenderer::GetInstance(renWin)->GetSliceNavigationController()->GetSlice()->SetPos(newPos); + } + } + */ } } void mitk::DisplayInteractor::ScrollOneDown(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper *stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Next(); } } void mitk::DisplayInteractor::ScrollOneUp(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper *stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Previous(); } } void mitk::DisplayInteractor::AdjustLevelWindow(StateMachineAction *, InteractionEvent *interactionEvent) { BaseRenderer::Pointer sender = interactionEvent->GetSender(); auto *positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // search for active image mitk::DataStorage::Pointer storage = sender->GetDataStorage(); mitk::DataNode::Pointer node = nullptr; - mitk::DataStorage::SetOfObjects::ConstPointer allImageNodes = - storage->GetSubset(mitk::NodePredicateDataType::New("Image")); - for (unsigned int i = 0; i < allImageNodes->size(); i++) + mitk::DataStorage::SetOfObjects::ConstPointer allImageNodes = storage->GetSubset(mitk::NodePredicateDataType::New("Image")); + for (unsigned int i = 0; i < allImageNodes->size(); ++i) { bool isActiveImage = false; bool propFound = allImageNodes->at(i)->GetBoolProperty("imageForLevelWindow", isActiveImage); if (propFound && isActiveImage) { node = allImageNodes->at(i); continue; } } if (node.IsNull()) { node = storage->GetNode(mitk::NodePredicateDataType::New("Image")); } if (node.IsNull()) { return; } mitk::LevelWindow lv = mitk::LevelWindow(); node->GetLevelWindow(lv); ScalarType level = lv.GetLevel(); ScalarType window = lv.GetWindow(); int levelIndex = 0; int windowIndex = 1; if (m_LevelDirection != "leftright") { levelIndex = 1; windowIndex = 0; } int directionModifier = 1; if (m_InvertLevelWindowDirection) { directionModifier = -1; } // calculate adjustments from mouse movements level += (m_CurrentDisplayCoordinate[levelIndex] - m_LastDisplayCoordinate[levelIndex]) * static_cast(2) * directionModifier; window += (m_CurrentDisplayCoordinate[windowIndex] - m_LastDisplayCoordinate[windowIndex]) * static_cast(2) * directionModifier; lv.SetLevelWindow(level, window); dynamic_cast(node->GetProperty("levelwindow"))->SetLevelWindow(lv); sender->GetRenderingManager()->RequestUpdateAll(); } void mitk::DisplayInteractor::StartRotation(mitk::StateMachineAction *, mitk::InteractionEvent *) { this->SetMouseCursor(rotate_cursor_xpm, 0, 0); } void mitk::DisplayInteractor::EndRotation(mitk::StateMachineAction *, mitk::InteractionEvent *) { this->ResetMouseCursor(); } void mitk::DisplayInteractor::Rotate(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const auto *posEvent = dynamic_cast(event); if (posEvent == nullptr) return; Point3D cursor = posEvent->GetPositionInWorld(); Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation; Vector3D toCursor = cursor - m_CenterOfRotation; // cross product: | A x B | = |A| * |B| * sin(angle) Vector3D axisOfRotation; vnl_vector_fixed vnlDirection = vnl_cross_3d(toCursor.GetVnlVector(), toProjected.GetVnlVector()); axisOfRotation.SetVnlVector(vnlDirection); // scalar product: A * B = |A| * |B| * cos(angle) // tan = sin / cos ScalarType angle = -atan2((double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected)); angle *= 180.0 / vnl_math::pi; m_LastCursorPosition = cursor; // create RotationOperation and apply to all SNCs that should be rotated RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle); // iterate the OTHER slice navigation controllers: these are filled in DoDecideBetweenRotationAndSliceSelection for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&rotationOperation); (*iter)->SendCreatedWorldGeometryUpdate(); } RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::DisplayInteractor::Swivel(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const auto *posEvent = dynamic_cast(event); if (!posEvent) return; // Determine relative mouse movement projected onto world space Point2D cursor = posEvent->GetPointerPositionOnScreen(); Vector2D relativeCursor = cursor - m_ReferenceCursor; Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1]; // Determine rotation axis (perpendicular to rotation plane and cursor // movement) Vector3D rotationAxis = itk::CrossProduct(m_RotationPlaneNormal, relativeCursorAxis); ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0; // Restore the initial plane pose by undoing the previous rotation // operation RotationOperation op(OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle); SNCVector::iterator iter; for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&op); (*iter)->SendCreatedWorldGeometryUpdate(); } } // Apply new rotation operation to all relevant SNCs RotationOperation op2(OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle); for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { // Retrieve the TimeGeometry of this SliceNavigationController TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; // Execute the new rotation timeGeometry->ExecuteOperation(&op2); // Notify listeners (*iter)->SendCreatedWorldGeometryUpdate(); } } m_PreviousRotationAxis = rotationAxis; m_PreviousRotationAngle = rotationAngle; RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::DisplayInteractor::UpdateStatusbar(mitk::StateMachineAction *, mitk::InteractionEvent *event) { - const auto *posEvent = dynamic_cast(event); - - if (!posEvent) + const auto* posEvent = dynamic_cast(event); + if (nullptr == posEvent) + { return; + } - std::string statusText; + const mitk::BaseRenderer::Pointer baseRenderer = posEvent->GetSender(); TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); - - mitk::BaseRenderer* baseRenderer = posEvent->GetSender(); auto globalCurrentTimePoint = baseRenderer->GetTime(); - mitk::DataStorage::SetOfObjects::ConstPointer nodes = baseRenderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); + mitk::DataStorage::SetOfObjects::ConstPointer nodes = renderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); + if (nodes.IsNull()) + { + return; + } // posEvent->GetPositionInWorld() would return the world position at the // time of initiating the interaction. However, we need to update the // status bar with the position after changing slice. Therefore, we // translate the same display position with the renderer again to // get the new world position. Point3D worldposition; - event->GetSender()->DisplayToWorld(posEvent->GetPointerPositionOnScreen(), worldposition); + baseRenderer->DisplayToWorld(posEvent->GetPointerPositionOnScreen(), worldposition); mitk::Image::Pointer image3D; mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; int component = 0; node = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, baseRenderer); if (node.IsNotNull()) { - bool isBinary(false); - node->GetBoolProperty("binary", isBinary); - if (isBinary) + return; + } + + bool isBinary(false); + node->GetBoolProperty("binary", isBinary); + if (isBinary) + { + mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = baseRenderer->GetDataStorage()->GetSources(node, nullptr, true); + if (!sourcenodes->empty()) { - mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = baseRenderer->GetDataStorage()->GetSources(node, nullptr, true); - if (!sourcenodes->empty()) - { topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, worldposition, globalCurrentTimePoint, baseRenderer); - } - if (topSourceNode.IsNotNull()) - { + } + if (topSourceNode.IsNotNull()) + { image3D = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } - } - else - { - image3D = dynamic_cast(node->GetData()); - node->GetIntProperty("Image.Displayed Component", component); - } - } + } + else + { + image3D = dynamic_cast(node->GetData()); + node->GetIntProperty("Image.Displayed Component", component); + } // get the position and gray value from the image and build up status bar text auto statusBar = StatusBar::GetInstance(); - if (image3D.IsNotNull() && statusBar != nullptr) { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(worldposition, p); auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType(); - if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA) { std::string pixelValue = "Pixel RGB(A) value: "; pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue.c_str()); } - else if ( pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR ) + else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR) { std::string pixelValue = "See ODF Details view. "; statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue.c_str()); } else { mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5(mitk::FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, image3D->GetVolumeData(image3D->GetTimeGeometry()->TimePointToTimeStep(globalCurrentTimePoint)), p, pixelValue, component); statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue); } } else { statusBar->DisplayImageInfoInvalid(); } } void mitk::DisplayInteractor::ConfigurationChanged() { mitk::PropertyList::Pointer properties = GetAttributes(); // auto repeat std::string strAutoRepeat = ""; if (properties->GetStringProperty("autoRepeat", strAutoRepeat)) { if (strAutoRepeat == "true") { m_AutoRepeat = true; } else { m_AutoRepeat = false; } } // pixel movement for scrolling one slice std::string strPixelPerSlice = ""; if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice)) { m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str()); } else { m_IndexToSliceModifier = 4; } // scroll direction if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection)) { m_ScrollDirection = "updown"; } m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false); // zoom direction if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection)) { m_ZoomDirection = "updown"; } m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false); m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false); if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection)) { m_LevelDirection = "leftright"; } m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false); // coupled rotation std::string strCoupled = ""; if (properties->GetStringProperty("coupled", strCoupled)) { if (strCoupled == "true") m_LinkPlanes = true; else m_LinkPlanes = false; } // zoom factor std::string strZoomFactor = ""; properties->GetStringProperty("zoomFactor", strZoomFactor); m_ZoomFactor = .05; if (atoi(strZoomFactor.c_str()) > 0) { m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0); } // allwaysReact std::string strAlwaysReact = ""; if (properties->GetStringProperty("alwaysReact", strAlwaysReact)) { if (strAlwaysReact == "true") { m_AlwaysReact = true; } else { m_AlwaysReact = false; } } else { m_AlwaysReact = false; } } bool mitk::DisplayInteractor::FilterEvents(InteractionEvent *interactionEvent, DataNode * /*dataNode*/) { if (interactionEvent->GetSender() == nullptr) return false; if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard3D) return false; return true; } bool mitk::DisplayInteractor::GetBoolProperty(mitk::PropertyList::Pointer propertyList, const char *propertyName, bool defaultValue) { std::string valueAsString; if (!propertyList->GetStringProperty(propertyName, valueAsString)) { return defaultValue; } else { if (valueAsString == "true") { return true; } else { return false; } } } diff --git a/Modules/Core/src/Interactions/mitkInteractionSchemeSwitcher.cpp b/Modules/Core/src/Interactions/mitkInteractionSchemeSwitcher.cpp new file mode 100644 index 0000000000..5cb7f5eb82 --- /dev/null +++ b/Modules/Core/src/Interactions/mitkInteractionSchemeSwitcher.cpp @@ -0,0 +1,96 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkInteractionSchemeSwitcher.h" + +// us +#include "usGetModuleContext.h" +#include "usModuleContext.h" + +// mitk core +#include "mitkInteractionEventObserver.h" + +mitk::InteractionSchemeSwitcher::InteractionSchemeSwitcher() + : m_InteractionScheme(MITKStandard) +{ + // nothing here +} + +mitk::InteractionSchemeSwitcher::~InteractionSchemeSwitcher() +{ + // nothing here +} + +void mitk::InteractionSchemeSwitcher::SetInteractionScheme(mitk::InteractionEventHandler* interactionEventHandler, InteractionScheme interactionScheme) +{ + switch (interactionScheme) + { + // MITK MODE + case MITKStandard: + { + interactionEventHandler->SetEventConfig("DisplayConfigMITK.xml"); + break; + } + case MITKRotationUncoupled: + { + interactionEventHandler->SetEventConfig("DisplayConfigMITKRotationUnCoupled.xml"); + break; + } + case MITKRotationCoupled: + { + interactionEventHandler->SetEventConfig("DisplayConfigMITKRotation.xml"); + break; + } + case MITKSwivel: + { + interactionEventHandler->SetEventConfig("DisplayConfigMITKSwivel.xml"); + break; + } + // PACS MODE + case PACSStandard: + { + interactionEventHandler->SetEventConfig("DisplayConfigPACS.xml"); + break; + } + case PACSLevelWindow: + { + interactionEventHandler->SetEventConfig("DisplayConfigPACS.xml"); + interactionEventHandler->AddEventConfig("DisplayConfigPACSLevelWindow.xml"); + break; + } + case PACSPan: + { + interactionEventHandler->SetEventConfig("DisplayConfigPACS.xml"); + interactionEventHandler->AddEventConfig("DisplayConfigPACSPan.xml"); + break; + } + case PACSScroll: + { + interactionEventHandler->SetEventConfig("DisplayConfigPACS.xml"); + interactionEventHandler->AddEventConfig("DisplayConfigPACSScroll.xml"); + break; + } + case PACSZoom: + { + interactionEventHandler->SetEventConfig("DisplayConfigPACS.xml"); + interactionEventHandler->AddEventConfig("DisplayConfigPACSZoom.xml"); + break; + } + } + + m_InteractionScheme = interactionScheme; + InvokeEvent(InteractionSchemeChangedEvent()); +} diff --git a/Modules/Core/src/Interactions/mitkStdDisplayActionEventHandler.cpp b/Modules/Core/src/Interactions/mitkStdDisplayActionEventHandler.cpp new file mode 100644 index 0000000000..06eea8b3bb --- /dev/null +++ b/Modules/Core/src/Interactions/mitkStdDisplayActionEventHandler.cpp @@ -0,0 +1,44 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkStdDisplayActionEventHandler.h" + +// mitk core +#include "mitkDisplayActionEventFunctions.h" + +// itk +#include + +void mitk::StdDisplayActionEventHandler::InitStdActions() +{ + // #TODO: change function call for new mitk::WeakPointer + if (m_ObservableBroadcast.IsNull()) + { + mitkThrow() << "No display action event broadcast class set to observe. Use 'SetObservableBroadcast' before initializing actions."; + } + + StdFunctionCommand::ActionFunction actionFunction = DisplayActionEventFunctions::MoveSenderCameraAction(); + ConnectDisplayActionEvent(DisplayMoveEvent(nullptr, Vector2D()), actionFunction); + + actionFunction = DisplayActionEventFunctions::SetCrosshairAction(); + ConnectDisplayActionEvent(DisplaySetCrosshairEvent(nullptr, Point3D()), actionFunction); + + actionFunction = DisplayActionEventFunctions::ZoomSenderCameraAction(); + ConnectDisplayActionEvent(DisplayZoomEvent(nullptr, 0.0, Point2D()), actionFunction); + + actionFunction = DisplayActionEventFunctions::ScrollSliceStepperAction(); + ConnectDisplayActionEvent(DisplayScrollEvent(nullptr, 0), actionFunction); +} diff --git a/Modules/QtWidgets/files.cmake b/Modules/QtWidgets/files.cmake index 4822eb025f..ad9a92ab7b 100644 --- a/Modules/QtWidgets/files.cmake +++ b/Modules/QtWidgets/files.cmake @@ -1,107 +1,118 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES QmitkAbstractDataStorageModel.cpp QmitkApplicationCursor.cpp QmitkDataStorageComboBox.cpp QmitkDataStorageDefaultListModel.cpp QmitkDataStorageListModel.cpp QmitkDataStorageTableModel.cpp QmitkDataStorageSimpleTreeModel.cpp QmitkDataStorageTreeModel.cpp QmitkDataStorageTreeModelInternalItem.cpp QmitkFileReaderOptionsDialog.cpp QmitkFileReaderWriterOptionsWidget.cpp QmitkFileWriterOptionsDialog.cpp + QmitkInteractionSchemeToolBar.cpp QmitkIOUtil.cpp QmitkLevelWindowPresetDefinitionDialog.cpp QmitkLevelWindowRangeChangeDialog.cpp QmitkLevelWindowWidgetContextMenu.cpp QmitkLevelWindowWidget.cpp QmitkLineEditLevelWindowWidget.cpp QmitkMemoryUsageIndicatorView.cpp + QmitkMouseModeSwitcher.cpp QmitkMimeTypes.cpp + QmitkMultiWidgetConfigurationToolBar.cpp + QmitkMultiWidgetLayoutSelectionWidget.cpp QmitkNodeDescriptor.cpp QmitkColoredNodeDescriptor.cpp QmitkNodeDescriptorManager.cpp - QmitkRenderWindowMenu.cpp QmitkProgressBar.cpp QmitkPropertiesTableEditor.cpp QmitkPropertiesTableModel.cpp QmitkPropertyDelegate.cpp QmitkRegisterClasses.cpp QmitkRenderingManager.cpp QmitkRenderingManagerFactory.cpp QmitkRenderWindow.cpp + QmitkRenderWindowMenu.cpp + QmitkRenderWindowWidget.cpp QmitkServiceListWidget.cpp QmitkSliderLevelWindowWidget.cpp QmitkStdMultiWidget.cpp - QmitkMouseModeSwitcher.cpp - QmitkDataStorageFilterProxyModel.cpp + QmitkCustomMultiWidget.cpp QmitkDataStorageComboBoxWithSelectNone.cpp + QmitkDataStorageFilterProxyModel.cpp QmitkPropertyItem.cpp QmitkPropertyItemDelegate.cpp QmitkPropertyItemModel.cpp QmitkStyleManager.cpp QmitkAbstractDataStorageInspector.cpp QmitkDataStorageListInspector.cpp QmitkDataStorageTreeInspector.cpp QmitkModelViewSelectionConnector.cpp mitkIDataStorageInspectorProvider.cpp mitkQtWidgetsActivator.cpp mitkDataStorageInspectorGenerator.cpp ) set(MOC_H_FILES include/QmitkAbstractDataStorageModel.h include/QmitkDataStorageComboBox.h include/QmitkDataStorageTableModel.h include/QmitkDataStorageTreeModel.h include/QmitkDataStorageSimpleTreeModel.h include/QmitkDataStorageDefaultListModel.h include/QmitkFileReaderOptionsDialog.h include/QmitkFileReaderWriterOptionsWidget.h include/QmitkFileWriterOptionsDialog.h + include/QmitkInteractionSchemeToolBar.h include/QmitkLevelWindowPresetDefinitionDialog.h include/QmitkLevelWindowRangeChangeDialog.h include/QmitkLevelWindowWidgetContextMenu.h include/QmitkLevelWindowWidget.h include/QmitkLineEditLevelWindowWidget.h include/QmitkMemoryUsageIndicatorView.h + include/QmitkMouseModeSwitcher.h + include/QmitkMultiWidgetConfigurationToolBar.h + include/QmitkMultiWidgetLayoutSelectionWidget.h include/QmitkNodeDescriptor.h include/QmitkColoredNodeDescriptor.h include/QmitkNodeDescriptorManager.h - include/QmitkRenderWindowMenu.h include/QmitkProgressBar.h include/QmitkPropertiesTableEditor.h include/QmitkPropertyDelegate.h include/QmitkRenderingManager.h include/QmitkRenderWindow.h + include/QmitkRenderWindowMenu.h + include/QmitkRenderWindowWidget.h include/QmitkServiceListWidget.h include/QmitkSliderLevelWindowWidget.h include/QmitkStdMultiWidget.h - include/QmitkMouseModeSwitcher.h + include/QmitkCustomMultiWidget.h include/QmitkDataStorageComboBoxWithSelectNone.h include/QmitkPropertyItemDelegate.h include/QmitkPropertyItemModel.h include/QmitkDataStorageListInspector.h include/QmitkAbstractDataStorageInspector.h include/QmitkDataStorageTreeInspector.h include/QmitkModelViewSelectionConnector.h ) set(UI_FILES src/QmitkFileReaderOptionsDialog.ui src/QmitkFileWriterOptionsDialog.ui src/QmitkLevelWindowPresetDefinition.ui src/QmitkLevelWindowWidget.ui src/QmitkLevelWindowRangeChange.ui src/QmitkMemoryUsageIndicator.ui + src/QmitkMultiWidgetLayoutSelectionWidget.ui src/QmitkServiceListWidgetControls.ui src/QmitkDataStorageListInspector.ui src/QmitkDataStorageTreeInspector.ui ) set(QRC_FILES resource/Qmitk.qrc ) diff --git a/Modules/QtWidgets/include/QmitkCustomMultiWidget.h b/Modules/QtWidgets/include/QmitkCustomMultiWidget.h new file mode 100644 index 0000000000..7b39e35f3a --- /dev/null +++ b/Modules/QtWidgets/include/QmitkCustomMultiWidget.h @@ -0,0 +1,176 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QMITKCUSTOMMULTIWIDGET_H +#define QMITKCUSTOMMULTIWIDGET_H + +// qt widgets module +#include "MitkQtWidgetsExports.h" +#include "QmitkRenderWindowWidget.h" + +// mitk core +#include +#include +#include +#include + +// qt +#include + +class QHBoxLayout; +class QVBoxLayout; +class QGridLayout; +class QSpacerItem; +class QmitkRenderWindow; + +namespace mitk +{ + class RenderingManager; +} + +/** +* @brief The 'QmitkCustomMultiWidget' is a 'QWidget' that is used to display multiple render windows at once. +* +* Render windows can dynamically be added and removed to change the layout of the multi widget. +*/ +class MITKQTWIDGETS_EXPORT QmitkCustomMultiWidget : public QWidget +{ + Q_OBJECT + +public: + + using RenderWindowWidgetPointer = std::shared_ptr; + using RenderWindowWidgetMap = std::map>; + using RenderWindowHash = QHash; + + QmitkCustomMultiWidget(QWidget* parent = 0, + Qt::WindowFlags f = 0, + mitk::RenderingManager* renderingManager = nullptr, + mitk::BaseRenderer::RenderingMode::Type renderingMode = mitk::BaseRenderer::RenderingMode::Standard, + const QString& multiWidgetName = "custommulti"); + + virtual ~QmitkCustomMultiWidget(); + + void SetDataStorage(mitk::DataStorage* dataStorage); + void InitializeRenderWindowWidgets(); + + mitk::InteractionEventHandler::Pointer GetInteractionEventHandler() { return m_DisplayActionEventBroadcast.GetPointer(); }; + + void ResetLayout(int row, int column); + void Synchronize(bool synchronized); + + RenderWindowWidgetMap GetRenderWindowWidgets() const; + RenderWindowWidgetPointer GetRenderWindowWidget(int row, int column) const; + RenderWindowWidgetPointer GetRenderWindowWidget(const QString& widgetName) const; + RenderWindowHash GetRenderWindows() const; + QmitkRenderWindow* GetRenderWindow(int row, int column) const; + QmitkRenderWindow* GetRenderWindow(const QString& widgetName) const; + + void SetActiveRenderWindowWidget(RenderWindowWidgetPointer activeRenderWindowWidget); + RenderWindowWidgetPointer GetActiveRenderWindowWidget() const; + RenderWindowWidgetPointer GetFirstRenderWindowWidget() const; + RenderWindowWidgetPointer GetLastRenderWindowWidget() const; + + unsigned int GetNumberOfRenderWindowWidgets() const; + + void RequestUpdate(const QString& widgetName); + void RequestUpdateAll(); + void ForceImmediateUpdate(const QString& widgetName); + void ForceImmediateUpdateAll(); + + const mitk::Point3D GetSelectedPosition(const QString& widgetName) const; + +public Q_SLOTS: + + /** + * @brief Listener to the CrosshairPositionEvent + * + * Ensures the CrosshairPositionEvent is handled only once and at the end of the Qt-Event loop + */ + void HandleCrosshairPositionEvent(); + /** + * @brief Receives the signal from HandleCrosshairPositionEvent, executes the StatusBar update + * + */ + void HandleCrosshairPositionEventDelayed(); + + void SetSelectedPosition(const QString& widgetName, const mitk::Point3D& newPosition); + + void ResetCrosshair(); + + // mouse events + void wheelEvent(QWheelEvent* e) override; + + void mousePressEvent(QMouseEvent* e) override; + + void moveEvent(QMoveEvent* e) override; + +Q_SIGNALS: + + void WheelMoved(QWheelEvent *); + void Moved(); + +public: + + enum + { + AXIAL, + SAGITTAL, + CORONAL, + THREE_D + }; + +private: + + void InitializeGUI(); + void InitializeWidget(); + void InitializeDisplayActionEventHandling(); + + void CreateRenderWindowWidget(const std::string& cornerAnnotation = ""); + void DestroyRenderWindowWidget(); + void FillMultiWidgetLayout(); + + QString GetNameFromIndex(int row, int column) const; + QString GetNameFromIndex(size_t index) const; + + // #TODO: see T24173 + mitk::DataNode::Pointer GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes); + + QGridLayout* m_CustomMultiWidgetLayout; + RenderWindowWidgetMap m_RenderWindowWidgets; + + RenderWindowWidgetPointer m_ActiveRenderWindowWidget; + + int m_MultiWidgetRows; + int m_MultiWidgetColumns; + + int m_PlaneMode; + + mitk::RenderingManager* m_RenderingManager; + mitk::BaseRenderer::RenderingMode::Type m_RenderingMode; + QString m_MultiWidgetName; + + mitk::DisplayActionEventBroadcast::Pointer m_DisplayActionEventBroadcast; + std::unique_ptr m_DisplayActionEventHandler; + + mitk::DataStorage::Pointer m_DataStorage; + + bool m_PendingCrosshairPositionEvent; + bool m_CrosshairNavigationEnabled; + +}; + +#endif // QMITKCUSTOMMULTIWIDGET_H diff --git a/Modules/QtWidgets/include/QmitkInteractionSchemeToolBar.h b/Modules/QtWidgets/include/QmitkInteractionSchemeToolBar.h new file mode 100644 index 0000000000..e9df9eb9a8 --- /dev/null +++ b/Modules/QtWidgets/include/QmitkInteractionSchemeToolBar.h @@ -0,0 +1,60 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QMITKINTERACTIONSCHEMETOOLBAR_H +#define QMITKINTERACTIONSCHEMETOOLBAR_H + +#include "MitkQtWidgetsExports.h" + +// mitk core +#include "mitkInteractionSchemeSwitcher.h" + +#include +#include + +/** +* @brief +* +* +*/ +class MITKQTWIDGETS_EXPORT QmitkInteractionSchemeToolBar : public QToolBar +{ + Q_OBJECT + +public: + + using InteractionScheme = mitk::InteractionSchemeSwitcher::InteractionScheme; + + QmitkInteractionSchemeToolBar(QWidget* parent = nullptr); + virtual ~QmitkInteractionSchemeToolBar() override; + + void SetInteractionEventHandler(mitk::InteractionEventHandler::Pointer interactionEventHandler); + +protected slots: + + void OnInteractionSchemeChanged(); + void AddButton(InteractionScheme id, const QString& toolName, const QIcon& icon, bool on = false); + +private: + + QActionGroup* m_ActionGroup; + + mitk::InteractionSchemeSwitcher::Pointer m_InteractionSchemeSwitcher; + mitk::InteractionEventHandler::Pointer m_InteractionEventHandler; + +}; + +#endif // QMITKINTERACTIONSCHEMETOOLBAR_H diff --git a/Modules/QtWidgets/include/QmitkMouseModeSwitcher.h b/Modules/QtWidgets/include/QmitkMouseModeSwitcher.h index 35701cef1c..4f1575c527 100644 --- a/Modules/QtWidgets/include/QmitkMouseModeSwitcher.h +++ b/Modules/QtWidgets/include/QmitkMouseModeSwitcher.h @@ -1,89 +1,88 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkMouseModeSwitcher_h #define QmitkMouseModeSwitcher_h #include "MitkQtWidgetsExports.h" #include "mitkMouseModeSwitcher.h" #include #include /** * \ingroup QmitkModule * \brief Qt toolbar representing mitk::MouseModeSwitcher. * * Provides buttons for the interaction modes defined in mitk::MouseModeSwitcher * and communicates with this non-graphical class. * * Can be used in a GUI to provide a mouse mode selector to the user. */ class MITKQTWIDGETS_EXPORT QmitkMouseModeSwitcher : public QToolBar { Q_OBJECT public: - QmitkMouseModeSwitcher(QWidget *parent = nullptr); + + QmitkMouseModeSwitcher(QWidget* parent = nullptr); ~QmitkMouseModeSwitcher() override; typedef mitk::MouseModeSwitcher::MouseMode MouseMode; public slots: /** \brief Connect to non-GUI class. When a button is pressed, given mitk::MouseModeSwitcher is informed to adapt interactors. \todo QmitkMouseModeSwitcher could be enhanced to actively observe mitk::MouseModeSwitcher and change available actions or visibility appropriately. */ - void setMouseModeSwitcher(mitk::MouseModeSwitcher *); + void setMouseModeSwitcher(mitk::MouseModeSwitcher*); signals: /** \brief Mode activated. This signal is needed for other GUI element to react appropriately. Sadly this is needed to provide "normal" functionality of QmitkStdMultiWidget, because this must enable/disable automatic reaction of SliceNavigationControllers to mouse clicks - depending on which mode is active. */ - void MouseModeSelected(mitk::MouseModeSwitcher::MouseMode id); // TODO change int to enum of MouseModeSwitcher + void MouseModeSelected(MouseMode id); protected slots: - void modeSelectedByUser(); - void addButton(MouseMode id, - const QString &toolName, - const QIcon &icon, - bool on = false); // TODO change int to enum of MouseModeSwitcher + void OnMouseModeChangedViaButton(); + void addButton(MouseMode id, const QString& toolName, const QIcon& icon, bool on = false); protected: - void OnMouseModeChanged(const itk::EventObject &); - QActionGroup *m_ActionGroup; - mitk::MouseModeSwitcher *m_MouseModeSwitcher; + void OnMouseModeChangedViaCommand(const itk::EventObject&); + + QActionGroup* m_ActionGroup; + mitk::MouseModeSwitcher* m_MouseModeSwitcher; unsigned long m_ObserverTag; - bool m_InObservationReaction; + bool m_ActionButtonBlocked; }; #endif diff --git a/Modules/QtWidgets/include/QmitkMultiWidgetConfigurationToolBar.h b/Modules/QtWidgets/include/QmitkMultiWidgetConfigurationToolBar.h new file mode 100644 index 0000000000..980e481fae --- /dev/null +++ b/Modules/QtWidgets/include/QmitkMultiWidgetConfigurationToolBar.h @@ -0,0 +1,69 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QMITKMULTIWIDGETCONFIGURATIONTOOLBAR_H +#define QMITKMULTIWIDGETCONFIGURATIONTOOLBAR_H + +#include "MitkQtWidgetsExports.h" + +#include + +// mitk qtwidgets +#include "QmitkCustomMultiWidget.h" +#include "QmitkMultiWidgetLayoutSelectionWidget.h" + +/** +* @brief +* +* +*/ +class MITKQTWIDGETS_EXPORT QmitkMultiWidgetConfigurationToolBar : public QToolBar +{ + Q_OBJECT + +public: + + using ViewDirection = mitk::SliceNavigationController::ViewDirection; + + QmitkMultiWidgetConfigurationToolBar(QmitkCustomMultiWidget* customMultiWidget); + ~QmitkMultiWidgetConfigurationToolBar() override; + +Q_SIGNALS: + + void LayoutSet(int row, int column); + void Synchronized(bool synchronized); + void ViewDirectionChanged(mitk::SliceNavigationController::ViewDirection); + +protected Q_SLOTS: + + void OnSetLayout(); + void OnSynchronize(); + void OnViewDirectionChanged(); + +private: + + void InitializeToolBar();; + void AddButtons(); + + QmitkCustomMultiWidget* m_CustomMultiWidget; + + QAction* m_SynchronizeAction; + + QmitkMultiWidgetLayoutSelectionWidget* m_LayoutSelectionPopup; + +}; + +#endif // QMITKMULTIWIDGETCONFIGURATIONTOOLBAR_H diff --git a/Modules/QtWidgets/include/QmitkMultiWidgetLayoutSelectionWidget.h b/Modules/QtWidgets/include/QmitkMultiWidgetLayoutSelectionWidget.h new file mode 100644 index 0000000000..65b46e63cf --- /dev/null +++ b/Modules/QtWidgets/include/QmitkMultiWidgetLayoutSelectionWidget.h @@ -0,0 +1,58 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QMITKMULTIWIDGETLAYOUTSELECTIONWIDGET_H +#define QMITKMULTIWIDGETLAYOUTSELECTIONWIDGET_H + +#include "MitkQtWidgetsExports.h" + +#include "ui_QmitkMultiWidgetLayoutSelectionWidget.h" + +// qt +#include "QWidget" + +/** +* @brief +* +* +*/ +class MITKQTWIDGETS_EXPORT QmitkMultiWidgetLayoutSelectionWidget : public QWidget +{ + Q_OBJECT + +public: + + QmitkMultiWidgetLayoutSelectionWidget(QWidget* parent = 0); + +Q_SIGNALS: + + void LayoutSet(int row, int column); + +private Q_SLOTS: + + void OnTableItemSelectionChanged(); + void OnSetLayoutButtonClicked(); + +private: + + void Init(); + + + Ui::QmitkMultiWidgetLayoutSelectionWidget ui; + +}; + +#endif // QMITKMULTIWIDGETLAYOUTSELECTIONWIDGET_H diff --git a/Modules/QtWidgets/include/QmitkRenderWindowWidget.h b/Modules/QtWidgets/include/QmitkRenderWindowWidget.h new file mode 100644 index 0000000000..0aff8a696b --- /dev/null +++ b/Modules/QtWidgets/include/QmitkRenderWindowWidget.h @@ -0,0 +1,117 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QMITKRENDERWINDOWWIDGET_H +#define QMITKRENDERWINDOWWIDGET_H + +// qt widgets module +#include "MitkQtWidgetsExports.h" +#include "QmitkRenderWindow.h" + +// mitk core +#include +#include +#include +#include +#include + +// qt +#include +#include + +/** +* @brief +* +* +*/ +class MITKQTWIDGETS_EXPORT QmitkRenderWindowWidget : public QWidget +{ + Q_OBJECT + +public: + + QmitkRenderWindowWidget( + QWidget* parent = nullptr, + const QString& widgetName = "", + mitk::DataStorage* dataStorage = nullptr, + mitk::BaseRenderer::RenderingMode::Type renderingMode = mitk::BaseRenderer::RenderingMode::Standard + ); + + ~QmitkRenderWindowWidget() override; + + void SetDataStorage(mitk::DataStorage* dataStorage); + + const QString& GetWidgetName() const { return m_WidgetName; }; + QmitkRenderWindow* GetRenderWindow() const { return m_RenderWindow; }; + + mitk::SliceNavigationController* GetSliceNavigationController() const; + + void RequestUpdate(); + void ForceImmediateUpdate(); + + void SetGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower); + void ShowGradientBackground(bool enable); + std::pair GetGradientBackgroundColors() const { return m_GradientBackgroundColors; }; + bool IsGradientBackgroundOn() const; + + void SetDecorationColor(const mitk::Color& color); + mitk::Color GetDecorationColor() const { return m_DecorationColor; }; + + void ShowColoredRectangle(bool show); + bool IsColoredRectangleVisible() const; + + void ShowCornerAnnotation(bool show); + bool IsCornerAnnotationVisible() const; + void SetCornerAnnotationText(const std::string& cornerAnnotation); + std::string GetCornerAnnotationText() const; + + /** + * @brief Create a corner annotation and a colored rectangle for this widget. + * + * @par text The text of the corner annotation. + * @par color The color of the text and the rectangle. + */ + void SetDecorationProperties(std::string text, mitk::Color color); + + bool IsRenderWindowMenuActivated() const; + +private: + + void InitializeGUI(); + void InitializeDecorations(); + + void SetCrosshair(mitk::Point3D selectedPoint); + + QString m_WidgetName; + QHBoxLayout* m_Layout; + + mitk::DataStorage* m_DataStorage; + QmitkRenderWindow* m_RenderWindow; + + mitk::DataNode::Pointer m_PointSetNode; + mitk::PointSet::Pointer m_PointSet; + + mitk::RenderingManager::Pointer m_RenderingManager; + mitk::BaseRenderer::RenderingMode::Type m_RenderingMode; + mitk::SliceNavigationController *m_TimeNavigationController; + + std::pair m_GradientBackgroundColors; + mitk::Color m_DecorationColor; + vtkSmartPointer m_RectangleProp; + vtkSmartPointer m_CornerAnnotation; +}; + +#endif // QMITKRENDERWINDOWWIDGET_H diff --git a/Modules/QtWidgets/resource/Qmitk.qrc b/Modules/QtWidgets/resource/Qmitk.qrc index 2b4501588d..69e5b5704d 100644 --- a/Modules/QtWidgets/resource/Qmitk.qrc +++ b/Modules/QtWidgets/resource/Qmitk.qrc @@ -1,15 +1,18 @@ Binaerbilder_48.png Images_48.png PointSet_48.png Segmentation_48.png Surface_48.png mm_pointer.png mm_scroll.png mm_zoom.png mm_contrast.png mm_pan.png LabelSetImage_48.png + cmwLayout.png + cmwSynchronized.png + cmwDesynchronized.png diff --git a/Modules/QtWidgets/resource/cmwDesynchronized.png b/Modules/QtWidgets/resource/cmwDesynchronized.png new file mode 100644 index 0000000000..81f087582c Binary files /dev/null and b/Modules/QtWidgets/resource/cmwDesynchronized.png differ diff --git a/Modules/QtWidgets/resource/cmwLayout.png b/Modules/QtWidgets/resource/cmwLayout.png new file mode 100644 index 0000000000..e0e584a0e0 Binary files /dev/null and b/Modules/QtWidgets/resource/cmwLayout.png differ diff --git a/Modules/QtWidgets/resource/cmwSynchronized.png b/Modules/QtWidgets/resource/cmwSynchronized.png new file mode 100644 index 0000000000..df4dd212ee Binary files /dev/null and b/Modules/QtWidgets/resource/cmwSynchronized.png differ diff --git a/Modules/QtWidgets/resource/cmwViewDirection.png b/Modules/QtWidgets/resource/cmwViewDirection.png new file mode 100644 index 0000000000..880fab30d6 Binary files /dev/null and b/Modules/QtWidgets/resource/cmwViewDirection.png differ diff --git a/Modules/QtWidgets/src/QmitkCustomMultiWidget.cpp b/Modules/QtWidgets/src/QmitkCustomMultiWidget.cpp new file mode 100644 index 0000000000..80976d7c91 --- /dev/null +++ b/Modules/QtWidgets/src/QmitkCustomMultiWidget.cpp @@ -0,0 +1,577 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkCustomMultiWidget.h" + +#include +#include +#include +#include + +// mitk core +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// qt +#include + +QmitkCustomMultiWidget::QmitkCustomMultiWidget(QWidget* parent, + Qt::WindowFlags f/* = 0*/, + mitk::RenderingManager* renderingManager/* = nullptr*/, + mitk::BaseRenderer::RenderingMode::Type renderingMode/* = mitk::BaseRenderer::RenderingMode::Standard*/, + const QString& multiWidgetName/* = "custommulti"*/) + : QWidget(parent, f) + , m_CustomMultiWidgetLayout(nullptr) + , m_MultiWidgetRows(0) + , m_MultiWidgetColumns(0) + , m_PlaneMode(0) + , m_RenderingManager(renderingManager) + , m_RenderingMode(renderingMode) + , m_MultiWidgetName(multiWidgetName) + , m_DisplayActionEventBroadcast(nullptr) + , m_DisplayActionEventHandler(nullptr) + , m_DataStorage(nullptr) + , m_PendingCrosshairPositionEvent(false) + , m_CrosshairNavigationEnabled(false) +{ + // create widget manually + // create and set layout + InitializeGUI(); + InitializeWidget(); + InitializeDisplayActionEventHandling(); + resize(QSize(364, 477).expandedTo(minimumSizeHint())); +} + +QmitkCustomMultiWidget::~QmitkCustomMultiWidget() +{ + // nothing here +} + +void QmitkCustomMultiWidget::SetDataStorage(mitk::DataStorage* dataStorage) +{ + if (dataStorage == m_DataStorage) + { + return; + } + + m_DataStorage = dataStorage; + // set new data storage for the render window widgets + for (const auto& renderWindowWidget : m_RenderWindowWidgets) + { + renderWindowWidget.second->SetDataStorage(m_DataStorage); + } +} + +void QmitkCustomMultiWidget::InitializeRenderWindowWidgets() +{ + // create render window widget initially + m_MultiWidgetRows = 1; + m_MultiWidgetColumns = 1; + CreateRenderWindowWidget("2015-01-14 - CT"); + InitializeGUI(); +} + +void QmitkCustomMultiWidget::ResetLayout(int row, int column) +{ + m_MultiWidgetRows = row + 1; + m_MultiWidgetColumns = column + 1; + + int requiredRenderWindowWidgets = m_MultiWidgetRows * m_MultiWidgetColumns; + int existingRenderWindowWidgets = m_RenderWindowWidgets.size(); + + int difference = requiredRenderWindowWidgets - existingRenderWindowWidgets; + while(0 < difference) + { + // more render window widgets needed + CreateRenderWindowWidget(); + --difference; + } + while(0 > difference) + { + // less render window widgets needed + DestroyRenderWindowWidget(); + ++difference; + } + + InitializeGUI(); +} + +void QmitkCustomMultiWidget::Synchronize(bool synchronized) +{ + auto allObserverTags = m_DisplayActionEventHandler->GetAllObserverTags(); + for (auto observerTag : allObserverTags) + { + m_DisplayActionEventHandler->DisconnectObserver(observerTag); + } + + if (synchronized) + { + mitk::StdFunctionCommand::ActionFunction actionFunction = mitk::DisplayActionEventFunctions::MoveCameraSynchronizedAction(); + m_DisplayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayMoveEvent(nullptr, mitk::Vector2D()), actionFunction); + + actionFunction = mitk::DisplayActionEventFunctions::SetCrosshairSynchronizedAction(); + m_DisplayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplaySetCrosshairEvent(nullptr, mitk::Point3D()), actionFunction); + + actionFunction = mitk::DisplayActionEventFunctions::ZoomCameraSynchronizedAction(); + m_DisplayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayZoomEvent(nullptr, 0.0, mitk::Point2D()), actionFunction); + + actionFunction = mitk::DisplayActionEventFunctions::ScrollSliceStepperSynchronizedAction(); + m_DisplayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayScrollEvent(nullptr, 0), actionFunction); + } + else + { + mitk::StdFunctionCommand::ActionFunction actionFunction = mitk::DisplayActionEventFunctions::MoveSenderCameraAction(); + m_DisplayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayMoveEvent(nullptr, mitk::Vector2D()), actionFunction); + + actionFunction = mitk::DisplayActionEventFunctions::SetCrosshairAction(); + m_DisplayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplaySetCrosshairEvent(nullptr, mitk::Point3D()), actionFunction); + + actionFunction = mitk::DisplayActionEventFunctions::ZoomSenderCameraAction(); + m_DisplayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayZoomEvent(nullptr, 0.0, mitk::Point2D()), actionFunction); + + actionFunction = mitk::DisplayActionEventFunctions::ScrollSliceStepperAction(); + m_DisplayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplayScrollEvent(nullptr, 0), actionFunction); + } + + // use the standard 'set level window' action for both modes + mitk::StdFunctionCommand::ActionFunction actionFunction = mitk::DisplayActionEventFunctions::SetLevelWindowAction(); + m_DisplayActionEventHandler->ConnectDisplayActionEvent(mitk::DisplaySetLevelWindowEvent(nullptr, mitk::ScalarType(), mitk::ScalarType()), actionFunction); +} + +QmitkCustomMultiWidget::RenderWindowWidgetMap QmitkCustomMultiWidget::GetRenderWindowWidgets() const +{ + return m_RenderWindowWidgets; +} + +QmitkCustomMultiWidget::RenderWindowWidgetPointer QmitkCustomMultiWidget::GetRenderWindowWidget(int row, int column) const +{ + return GetRenderWindowWidget(GetNameFromIndex(row, column)); +} + +QmitkCustomMultiWidget::RenderWindowWidgetPointer QmitkCustomMultiWidget::GetRenderWindowWidget(const QString& widgetName) const +{ + RenderWindowWidgetMap::const_iterator it = m_RenderWindowWidgets.find(widgetName); + if (it != m_RenderWindowWidgets.end()) + { + return it->second; + } + + return nullptr; +} + +QmitkCustomMultiWidget::RenderWindowHash QmitkCustomMultiWidget::GetRenderWindows() const +{ + RenderWindowHash result; + // create QHash on demand + auto renderWindowWidgets = GetRenderWindowWidgets(); + for (const auto& renderWindowWidget : renderWindowWidgets) + { + result.insert(renderWindowWidget.first, renderWindowWidget.second->GetRenderWindow()); + } + + return result; +} + +QmitkRenderWindow* QmitkCustomMultiWidget::GetRenderWindow(int row, int column) const +{ + return GetRenderWindow(GetNameFromIndex(row, column)); +} + +QmitkRenderWindow* QmitkCustomMultiWidget::GetRenderWindow(const QString& widgetName) const +{ + RenderWindowWidgetPointer renderWindowWidget = GetRenderWindowWidget(widgetName); + if (nullptr != renderWindowWidget) + { + return renderWindowWidget->GetRenderWindow(); + } + + return nullptr; +} + +void QmitkCustomMultiWidget::SetActiveRenderWindowWidget(RenderWindowWidgetPointer activeRenderWindowWidget) +{ + m_ActiveRenderWindowWidget = activeRenderWindowWidget; +} + +QmitkCustomMultiWidget::RenderWindowWidgetPointer QmitkCustomMultiWidget::GetActiveRenderWindowWidget() const +{ + return m_ActiveRenderWindowWidget; +} + +QmitkCustomMultiWidget::RenderWindowWidgetPointer QmitkCustomMultiWidget::GetFirstRenderWindowWidget() const +{ + if (!m_RenderWindowWidgets.empty()) + { + return m_RenderWindowWidgets.begin()->second; + } + else + { + return nullptr; + } +} + +QmitkCustomMultiWidget::RenderWindowWidgetPointer QmitkCustomMultiWidget::GetLastRenderWindowWidget() const +{ + if (!m_RenderWindowWidgets.empty()) + { + return m_RenderWindowWidgets.rbegin()->second; + } + else + { + return nullptr; + } +} + +unsigned int QmitkCustomMultiWidget::GetNumberOfRenderWindowWidgets() const +{ + return m_RenderWindowWidgets.size(); +} + +void QmitkCustomMultiWidget::RequestUpdate(const QString& widgetName) +{ + RenderWindowWidgetPointer renderWindowWidget = GetRenderWindowWidget(widgetName); + if (nullptr != renderWindowWidget) + { + return renderWindowWidget->RequestUpdate(); + } +} + +void QmitkCustomMultiWidget::RequestUpdateAll() +{ + for (const auto& renderWindowWidget : m_RenderWindowWidgets) + { + renderWindowWidget.second->RequestUpdate(); + } +} + +void QmitkCustomMultiWidget::ForceImmediateUpdate(const QString& widgetName) +{ + RenderWindowWidgetPointer renderWindowWidget = GetRenderWindowWidget(widgetName); + if (nullptr != renderWindowWidget) + { + renderWindowWidget->ForceImmediateUpdate(); + } +} + +void QmitkCustomMultiWidget::ForceImmediateUpdateAll() +{ + for (const auto& renderWindowWidget : m_RenderWindowWidgets) + { + renderWindowWidget.second->ForceImmediateUpdate(); + } +} + +const mitk::Point3D QmitkCustomMultiWidget::GetSelectedPosition(const QString& widgetName) const +{ + /* + const mitk::PlaneGeometry *plane1 = mitkWidget1->GetSliceNavigationController()->GetCurrentPlaneGeometry(); + const mitk::PlaneGeometry *plane2 = mitkWidget2->GetSliceNavigationController()->GetCurrentPlaneGeometry(); + const mitk::PlaneGeometry *plane3 = mitkWidget3->GetSliceNavigationController()->GetCurrentPlaneGeometry(); + + mitk::Line3D line; + if ((plane1 != NULL) && (plane2 != NULL) && (plane1->IntersectionLine(plane2, line))) + { + mitk::Point3D point; + if ((plane3 != NULL) && (plane3->IntersectionPoint(line, point))) + { + return point; + } + } + // TODO BUG POSITIONTRACKER; + mitk::Point3D p; + return p; + // return m_LastLeftClickPositionSupplier->GetCurrentPoint(); + */ + return mitk::Point3D(); +} + +////////////////////////////////////////////////////////////////////////// +// PUBLIC SLOTS +////////////////////////////////////////////////////////////////////////// +void QmitkCustomMultiWidget::HandleCrosshairPositionEvent() +{ + /* + if (!m_PendingCrosshairPositionEvent) + { + m_PendingCrosshairPositionEvent = true; + QTimer::singleShot(0, this, SLOT(HandleCrosshairPositionEventDelayed())); + } + */ +} + +void QmitkCustomMultiWidget::HandleCrosshairPositionEventDelayed() +{ + /* + m_PendingCrosshairPositionEvent = false; + + // find image with highest layer + mitk::TNodePredicateDataType::Pointer isImageData = mitk::TNodePredicateDataType::New(); + mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->m_DataStorage->GetSubset(isImageData).GetPointer(); + + mitk::DataNode::Pointer node; + mitk::DataNode::Pointer topSourceNode; + mitk::Image::Pointer image; + bool isBinary = false; + node = this->GetTopLayerNode(nodes); + int component = 0; + if (node.IsNotNull()) + { + node->GetBoolProperty("binary", isBinary); + if (isBinary) + { + mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = m_DataStorage->GetSources(node, NULL, true); + if (!sourcenodes->empty()) + { + topSourceNode = this->GetTopLayerNode(sourcenodes); + } + if (topSourceNode.IsNotNull()) + { + image = dynamic_cast(topSourceNode->GetData()); + topSourceNode->GetIntProperty("Image.Displayed Component", component); + } + else + { + image = dynamic_cast(node->GetData()); + node->GetIntProperty("Image.Displayed Component", component); + } + } + else + { + image = dynamic_cast(node->GetData()); + node->GetIntProperty("Image.Displayed Component", component); + } + } + + mitk::Point3D crosshairPos = this->GetCrossPosition(); + std::string statusText; + std::stringstream stream; + itk::Index<3> p; + mitk::BaseRenderer *baseRenderer = GetRenderWindow()->GetSliceNavigationController()->GetRenderer(); + unsigned int timestep = baseRenderer->GetTimeStep(); + + if (image.IsNotNull() && (image->GetTimeSteps() > timestep)) + { + image->GetGeometry()->WorldToIndex(crosshairPos, p); + stream.precision(2); + stream << "Position: <" << std::fixed << crosshairPos[0] << ", " << std::fixed << crosshairPos[1] << ", " + << std::fixed << crosshairPos[2] << "> mm"; + stream << "; Index: <" << p[0] << ", " << p[1] << ", " << p[2] << "> "; + + mitk::ScalarType pixelValue; + + mitkPixelTypeMultiplex5(mitk::FastSinglePixelAccess, + image->GetChannelDescriptor().GetPixelType(), + image, + image->GetVolumeData(baseRenderer->GetTimeStep()), + p, + pixelValue, + component); + + if (fabs(pixelValue) > 1000000 || fabs(pixelValue) < 0.01) + { + stream << "; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: " << std::scientific << pixelValue << " "; + } + else + { + stream << "; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: " << pixelValue << " "; + } + } + else + { + stream << "No image information at this position!"; + } + + statusText = stream.str(); + mitk::StatusBar::GetInstance()->DisplayGreyValueText(statusText.c_str()); + */ +} + +void QmitkCustomMultiWidget::SetSelectedPosition(const QString& widgetName, const mitk::Point3D& newPosition) +{ + RenderWindowWidgetPointer renderWindowWidget; + if (widgetName.isNull()) + { + renderWindowWidget = GetActiveRenderWindowWidget(); + } + else + { + renderWindowWidget = GetRenderWindowWidget(widgetName); + } + + if (nullptr != renderWindowWidget) + { + renderWindowWidget->GetSliceNavigationController()->SelectSliceByPoint(newPosition); + renderWindowWidget->RequestUpdate(); + return; + } + + MITK_ERROR << "Position can not be set for an unknown render window widget."; +} + +void QmitkCustomMultiWidget::ResetCrosshair() +{ + // #TODO: new concept: we do not want to initialize all views; + // we do not want to rely on the geometry planes + /* + if (m_DataStorage.IsNotNull()) + { + m_RenderingManager->InitializeViewsByBoundingObjects(m_DataStorage); + // m_RenderingManager->InitializeViews( m_DataStorage->ComputeVisibleBoundingGeometry3D() ); + // reset interactor to normal slicing + SetWidgetPlaneMode(PLANE_MODE_SLICING); + } + */ +} + +////////////////////////////////////////////////////////////////////////// +// MOUSE EVENTS +////////////////////////////////////////////////////////////////////////// +void QmitkCustomMultiWidget::wheelEvent(QWheelEvent* e) +{ + emit WheelMoved(e); +} + +void QmitkCustomMultiWidget::mousePressEvent(QMouseEvent* e) +{ +} + +void QmitkCustomMultiWidget::moveEvent(QMoveEvent* e) +{ + QWidget::moveEvent(e); + + // it is necessary to readjust the position of the overlays as the MultiWidget has moved + // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here + emit Moved(); +} + +////////////////////////////////////////////////////////////////////////// +// PRIVATE +////////////////////////////////////////////////////////////////////////// +void QmitkCustomMultiWidget::InitializeGUI() +{ + delete m_CustomMultiWidgetLayout; + m_CustomMultiWidgetLayout = new QGridLayout(this); + m_CustomMultiWidgetLayout->setContentsMargins(0, 0, 0, 0); + setLayout(m_CustomMultiWidgetLayout); + + FillMultiWidgetLayout(); +} + +void QmitkCustomMultiWidget::InitializeWidget() +{ + // #TODO: some things have to be handled globally (hold for all render window (widgets) + // analyze those things and design a controlling mechanism +} + +void QmitkCustomMultiWidget::InitializeDisplayActionEventHandling() +{ + m_DisplayActionEventBroadcast = mitk::DisplayActionEventBroadcast::New(); + m_DisplayActionEventBroadcast->LoadStateMachine("DisplayInteraction.xml"); + m_DisplayActionEventBroadcast->SetEventConfig("DisplayConfigPACS.xml"); + + m_DisplayActionEventHandler = std::make_unique(); + m_DisplayActionEventHandler->SetObservableBroadcast(m_DisplayActionEventBroadcast); + + Synchronize(true); +} + +void QmitkCustomMultiWidget::CreateRenderWindowWidget(const std::string& cornerAnnotation/* = ""*/) +{ + // create the render window widget and connect signals / slots + QString renderWindowWidgetName = GetNameFromIndex(m_RenderWindowWidgets.size()); + RenderWindowWidgetPointer renderWindowWidget = std::make_shared(this, renderWindowWidgetName, m_DataStorage); + renderWindowWidget->SetCornerAnnotationText(renderWindowWidgetName.toStdString()/*cornerAnnotation*/); + + // store the newly created render window widget with the UID + m_RenderWindowWidgets.insert(std::make_pair(renderWindowWidgetName, renderWindowWidget)); +} + +void QmitkCustomMultiWidget::DestroyRenderWindowWidget() +{ + auto iterator = m_RenderWindowWidgets.find(GetNameFromIndex(m_RenderWindowWidgets.size() - 1)); + if (iterator == m_RenderWindowWidgets.end()) + { + return; + } + + // disconnect each signal of this render window widget + RenderWindowWidgetPointer renderWindowWidgetToRemove = iterator->second; + disconnect(renderWindowWidgetToRemove.get(), 0, 0, 0); + + // erase the render window from the map + m_RenderWindowWidgets.erase(iterator); +} + +void QmitkCustomMultiWidget::FillMultiWidgetLayout() +{ + for (int row = 0; row < m_MultiWidgetRows; ++row) + { + for (int column = 0; column < m_MultiWidgetColumns; ++column) + { + RenderWindowWidgetPointer renderWindowWidget = GetRenderWindowWidget(row, column); + if (nullptr != renderWindowWidget) + { + m_CustomMultiWidgetLayout->addWidget(renderWindowWidget.get(), row, column); + SetActiveRenderWindowWidget(renderWindowWidget); + } + } + } +} + +QString QmitkCustomMultiWidget::GetNameFromIndex(int row, int column) const +{ + if (0 <= row && m_MultiWidgetRows > row && 0 <= column && m_MultiWidgetColumns > column) + { + return GetNameFromIndex(row * m_MultiWidgetColumns + column); + } + + return QString(); +} + +QString QmitkCustomMultiWidget::GetNameFromIndex(size_t index) const +{ + if (index <= m_RenderWindowWidgets.size()) + { + return m_MultiWidgetName + ".widget" + QString::number(index); + } + + return QString(); +} + +mitk::DataNode::Pointer QmitkCustomMultiWidget::GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes) +{ + // #TODO: see T24173 + return nodes->front(); +} diff --git a/Modules/QtWidgets/src/QmitkInteractionSchemeToolBar.cpp b/Modules/QtWidgets/src/QmitkInteractionSchemeToolBar.cpp new file mode 100644 index 0000000000..98d34e39f5 --- /dev/null +++ b/Modules/QtWidgets/src/QmitkInteractionSchemeToolBar.cpp @@ -0,0 +1,82 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkInteractionSchemeToolBar.h" + +#include + +QmitkInteractionSchemeToolBar::QmitkInteractionSchemeToolBar(QWidget* parent/* = nullptr*/) + : QToolBar(parent) + , m_ActionGroup(new QActionGroup(this)) + , m_InteractionSchemeSwitcher(nullptr) + , m_InteractionEventHandler(nullptr) +{ + QToolBar::setOrientation(Qt::Vertical); + QToolBar::setIconSize(QSize(17, 17)); + m_ActionGroup->setExclusive(true); // only one selectable action + + AddButton(InteractionScheme::PACSStandard, tr("Pointer"), QIcon(":/Qmitk/mm_pointer.png"), true); + AddButton(InteractionScheme::PACSLevelWindow, tr("Level/Window"), QIcon(":/Qmitk/mm_contrast.png")); + AddButton(InteractionScheme::PACSPan, tr("Pan"), QIcon(":/Qmitk/mm_pan.png")); + AddButton(InteractionScheme::PACSScroll, tr("Scroll"), QIcon(":/Qmitk/mm_scroll.png")); + AddButton(InteractionScheme::PACSZoom, tr("Zoom"), QIcon(":/Qmitk/mm_zoom.png")); + + m_InteractionSchemeSwitcher = mitk::InteractionSchemeSwitcher::New(); +} + +void QmitkInteractionSchemeToolBar::SetInteractionEventHandler(mitk::InteractionEventHandler::Pointer interactionEventHandler) +{ + if (interactionEventHandler == m_InteractionEventHandler) + { + return; + } + + m_InteractionEventHandler = interactionEventHandler; + if (nullptr != m_InteractionEventHandler) + { + m_InteractionSchemeSwitcher->SetInteractionScheme(m_InteractionEventHandler, InteractionScheme::PACSStandard); + } +} + +void QmitkInteractionSchemeToolBar::AddButton(InteractionScheme interactionScheme, const QString& toolName, const QIcon& icon, bool on) +{ + QAction* action = new QAction(icon, toolName, this); + action->setCheckable(true); + action->setActionGroup(m_ActionGroup); + action->setChecked(on); + action->setData(interactionScheme); + connect(action, SIGNAL(triggered()), this, SLOT(OnInteractionSchemeChanged())); + QToolBar::addAction(action); +} + +QmitkInteractionSchemeToolBar::~QmitkInteractionSchemeToolBar() +{ + // nothing here +} + +void QmitkInteractionSchemeToolBar::OnInteractionSchemeChanged() +{ + QAction* action = dynamic_cast(sender()); + if (action) + { + InteractionScheme interactionScheme = static_cast(action->data().toInt()); + + if (m_InteractionEventHandler) + { + m_InteractionSchemeSwitcher->SetInteractionScheme(m_InteractionEventHandler, interactionScheme); + } + } +} diff --git a/Modules/QtWidgets/src/QmitkMouseModeSwitcher.cpp b/Modules/QtWidgets/src/QmitkMouseModeSwitcher.cpp index 3a909c39cd..b3b91efd71 100644 --- a/Modules/QtWidgets/src/QmitkMouseModeSwitcher.cpp +++ b/Modules/QtWidgets/src/QmitkMouseModeSwitcher.cpp @@ -1,115 +1,112 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkMouseModeSwitcher.h" #include -QmitkMouseModeSwitcher::QmitkMouseModeSwitcher(QWidget *parent) - : QToolBar(parent), - m_ActionGroup(new QActionGroup(this)), - m_MouseModeSwitcher(nullptr), - m_ObserverTag(0), - m_InObservationReaction(false) +QmitkMouseModeSwitcher::QmitkMouseModeSwitcher(QWidget* parent/* = nullptr*/) + : QToolBar(parent) + , m_ActionGroup(new QActionGroup(this)) + , m_MouseModeSwitcher(nullptr) + , m_ObserverTag(0) + , m_ActionButtonBlocked(false) { QToolBar::setOrientation(Qt::Vertical); QToolBar::setIconSize(QSize(17, 17)); - m_ActionGroup->setExclusive(true); // only one selectable + m_ActionGroup->setExclusive(true); // only one selectable action addButton(mitk::MouseModeSwitcher::MousePointer, tr("Pointer"), QIcon(":/Qmitk/mm_pointer.png"), true); // toggle ON addButton(mitk::MouseModeSwitcher::Scroll, tr("Scroll"), QIcon(":/Qmitk/mm_scroll.png")); addButton(mitk::MouseModeSwitcher::LevelWindow, tr("Level/Window"), QIcon(":/Qmitk/mm_contrast.png")); addButton(mitk::MouseModeSwitcher::Zoom, tr("Zoom"), QIcon(":/Qmitk/mm_zoom.png")); addButton(mitk::MouseModeSwitcher::Pan, tr("Pan"), QIcon(":/Qmitk/mm_pan.png")); } void QmitkMouseModeSwitcher::addButton(MouseMode id, const QString &toolName, const QIcon &icon, bool on) { - QAction *action = new QAction(icon, toolName, this); + QAction* action = new QAction(icon, toolName, this); action->setCheckable(true); action->setActionGroup(m_ActionGroup); action->setChecked(on); action->setData(id); - connect(action, SIGNAL(triggered()), this, SLOT(modeSelectedByUser())); + connect(action, SIGNAL(triggered()), this, SLOT(OnMouseModeChangedViaButton())); QToolBar::addAction(action); } QmitkMouseModeSwitcher::~QmitkMouseModeSwitcher() { - if (m_MouseModeSwitcher) + if (nullptr != m_MouseModeSwitcher) { m_MouseModeSwitcher->RemoveObserver(m_ObserverTag); } } void QmitkMouseModeSwitcher::setMouseModeSwitcher(mitk::MouseModeSwitcher *mms) { // goodbye / welcome ceremonies - if (m_MouseModeSwitcher) + if (nullptr != m_MouseModeSwitcher) { m_MouseModeSwitcher->RemoveObserver(m_ObserverTag); } m_MouseModeSwitcher = mms; if (m_MouseModeSwitcher) { - itk::ReceptorMemberCommand::Pointer command = - itk::ReceptorMemberCommand::New(); - command->SetCallbackFunction(this, &QmitkMouseModeSwitcher::OnMouseModeChanged); + itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); + command->SetCallbackFunction(this, &QmitkMouseModeSwitcher::OnMouseModeChangedViaCommand); m_ObserverTag = m_MouseModeSwitcher->AddObserver(mitk::MouseModeSwitcher::MouseModeChangedEvent(), command); } } -void QmitkMouseModeSwitcher::modeSelectedByUser() +void QmitkMouseModeSwitcher::OnMouseModeChangedViaButton() { - if (m_InObservationReaction) - return; // this was NOT actually by the user but by ourselves - - QAction *action = dynamic_cast(sender()); + if (m_ActionButtonBlocked) + { + return; // blocked, since we change the checked state of the buttons in the callback function + } + QAction* action = dynamic_cast(sender()); if (action) { MouseMode id = static_cast(action->data().toInt()); // qDebug() << "Mouse mode '" << qPrintable(action->text()) << "' selected, trigger mode id " << id; if (m_MouseModeSwitcher) { m_MouseModeSwitcher->SetInteractionScheme(mitk::MouseModeSwitcher::InteractionScheme::PACS); m_MouseModeSwitcher->SelectMouseMode(id); } emit MouseModeSelected(id); } } -void QmitkMouseModeSwitcher::OnMouseModeChanged(const itk::EventObject &) +void QmitkMouseModeSwitcher::OnMouseModeChangedViaCommand(const itk::EventObject &) { - m_InObservationReaction = true; + m_ActionButtonBlocked = true; - // push button graphically assert(m_MouseModeSwitcher); - MouseMode activeMode = m_MouseModeSwitcher->GetCurrentMouseMode(); - - foreach (QAction *action, m_ActionGroup->actions()) + foreach(QAction* action, m_ActionGroup->actions()) { if (action->data().toInt() == activeMode) { action->setChecked(true); } } - m_InObservationReaction = false; + m_ActionButtonBlocked = false; } diff --git a/Modules/QtWidgets/src/QmitkMultiWidgetConfigurationToolBar.cpp b/Modules/QtWidgets/src/QmitkMultiWidgetConfigurationToolBar.cpp new file mode 100644 index 0000000000..1456098c72 --- /dev/null +++ b/Modules/QtWidgets/src/QmitkMultiWidgetConfigurationToolBar.cpp @@ -0,0 +1,122 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkMultiWidgetConfigurationToolBar.h" + +QmitkMultiWidgetConfigurationToolBar::QmitkMultiWidgetConfigurationToolBar(QmitkCustomMultiWidget* customMultiWidget) + : QToolBar(customMultiWidget) + , m_CustomMultiWidget(customMultiWidget) +{ + QToolBar::setOrientation(Qt::Vertical); + QToolBar::setIconSize(QSize(17, 17)); + + InitializeToolBar(); +} + +QmitkMultiWidgetConfigurationToolBar::~QmitkMultiWidgetConfigurationToolBar() +{ + // nothing here +} + +void QmitkMultiWidgetConfigurationToolBar::InitializeToolBar() +{ + // create popup to show a widget to modify the multi widget layout + m_LayoutSelectionPopup = new QmitkMultiWidgetLayoutSelectionWidget(this); + m_LayoutSelectionPopup->hide(); + + AddButtons(); + + connect(m_LayoutSelectionPopup, &QmitkMultiWidgetLayoutSelectionWidget::LayoutSet, this, &QmitkMultiWidgetConfigurationToolBar::LayoutSet); +} + +void QmitkMultiWidgetConfigurationToolBar::AddButtons() +{ + QAction* setLayoutAction = new QAction(QIcon(":/Qmitk/cmwLayout.png"), tr("Set multi widget layout"), this); + connect(setLayoutAction, &QAction::triggered, this, &QmitkMultiWidgetConfigurationToolBar::OnSetLayout); + + QToolBar::addAction(setLayoutAction); + + m_SynchronizeAction = new QAction(QIcon(":/Qmitk/cmwSynchronized.png"), tr("Desynchronize render windows"), this); + m_SynchronizeAction->setCheckable(true); + m_SynchronizeAction->setChecked(true); + connect(m_SynchronizeAction, &QAction::triggered, this, &QmitkMultiWidgetConfigurationToolBar::OnSynchronize); + + QToolBar::addAction(m_SynchronizeAction); + + QAction* setAxialViewDirectionAction = new QAction(tr("Set axial view direction"), this); + setAxialViewDirectionAction->setData(ViewDirection::Axial); + connect(setAxialViewDirectionAction, &QAction::triggered, this, &QmitkMultiWidgetConfigurationToolBar::OnViewDirectionChanged); + + QAction* setSagittalViewDirectionAction = new QAction(tr("Set sagittal view direction"), this); + setSagittalViewDirectionAction->setData(ViewDirection::Sagittal); + connect(setSagittalViewDirectionAction, &QAction::triggered, this, &QmitkMultiWidgetConfigurationToolBar::OnViewDirectionChanged); + + QAction* setCoronalViewDirectionAction = new QAction(tr("Set coronal view direction"), this); + setCoronalViewDirectionAction->setData(ViewDirection::Frontal); + connect(setCoronalViewDirectionAction, &QAction::triggered, this, &QmitkMultiWidgetConfigurationToolBar::OnViewDirectionChanged); + + QToolButton* setViewDirectionToolButton = new QToolButton(this); + setViewDirectionToolButton->setIcon(QIcon(":/Qmitk/cmwViewDirection.png")); + setViewDirectionToolButton->setText(tr("Set synchronized render window view direction")); + + QMenu* menu = new QMenu(); + menu->addAction(setAxialViewDirectionAction); + menu->addAction(setSagittalViewDirectionAction); + menu->addAction(setCoronalViewDirectionAction); + setViewDirectionToolButton->setMenu(menu); + setViewDirectionToolButton->setPopupMode(QToolButton::InstantPopup); + + this->addWidget(setViewDirectionToolButton); +} + +void QmitkMultiWidgetConfigurationToolBar::OnSetLayout() +{ + if (nullptr != m_CustomMultiWidget) + { + m_LayoutSelectionPopup->setWindowFlags(Qt::Popup); + m_LayoutSelectionPopup->move(this->cursor().pos()); + m_LayoutSelectionPopup->show(); + } +} + +void QmitkMultiWidgetConfigurationToolBar::OnSynchronize() +{ + bool synchronized = m_SynchronizeAction->isChecked(); + if (synchronized) + { + m_SynchronizeAction->setIcon(QIcon(":/Qmitk/cmwSynchronized.png")); + m_SynchronizeAction->setText(tr("Desynchronize render windows")); + + } + else + { + m_SynchronizeAction->setIcon(QIcon(":/Qmitk/cmwDesynchronized.png")); + m_SynchronizeAction->setText(tr("Synchronize render windows")); + } + + m_SynchronizeAction->setChecked(synchronized); + emit Synchronized(synchronized); +} + +void QmitkMultiWidgetConfigurationToolBar::OnViewDirectionChanged() +{ + QAction* action = dynamic_cast(sender()); + if (action) + { + ViewDirection viewDirection = static_cast(action->data().toInt()); + emit ViewDirectionChanged(viewDirection); + } +} diff --git a/Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.cpp b/Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.cpp new file mode 100644 index 0000000000..89d05d28b7 --- /dev/null +++ b/Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.cpp @@ -0,0 +1,76 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkMultiWidgetLayoutSelectionWidget.h" + +QmitkMultiWidgetLayoutSelectionWidget::QmitkMultiWidgetLayoutSelectionWidget(QWidget* parent/* = 0*/) + : QWidget(parent) +{ + Init(); +} + +void QmitkMultiWidgetLayoutSelectionWidget::Init() +{ + ui.setupUi(this); + connect(ui.tableWidget, &QTableWidget::itemSelectionChanged, this, &QmitkMultiWidgetLayoutSelectionWidget::OnTableItemSelectionChanged); + connect(ui.setLayoutPushButton, &QPushButton::clicked, this, &QmitkMultiWidgetLayoutSelectionWidget::OnSetLayoutButtonClicked); +} + +void QmitkMultiWidgetLayoutSelectionWidget::OnTableItemSelectionChanged() +{ + QItemSelectionModel* selectionModel = ui.tableWidget->selectionModel(); + + int row = 0; + int column = 0; + QModelIndexList indices = selectionModel->selectedIndexes(); + if (indices.size() > 0) + { + row = indices[0].row(); + column = indices[0].column(); + + QModelIndex topLeft = ui.tableWidget->model()->index(0, 0, QModelIndex()); + QModelIndex bottomRight = ui.tableWidget->model()->index(row, column, QModelIndex()); + + QItemSelection cellSelection; + cellSelection.select(topLeft, bottomRight); + selectionModel->select(cellSelection, QItemSelectionModel::Select); + } +} + +void QmitkMultiWidgetLayoutSelectionWidget::OnSetLayoutButtonClicked() +{ + int row = 0; + int column = 0; + QModelIndexList indices = ui.tableWidget->selectionModel()->selectedIndexes(); + if (indices.size() > 0) + { + // find largest row and column + for (const QModelIndex modelIndex : indices) + { + if (modelIndex.row() > row) + { + row = modelIndex.row(); + } + if (modelIndex.column() > column) + { + column = modelIndex.column(); + } + } + + close(); + emit LayoutSet(row, column); + } +} diff --git a/Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.ui b/Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.ui new file mode 100644 index 0000000000..34d3e62dfd --- /dev/null +++ b/Modules/QtWidgets/src/QmitkMultiWidgetLayoutSelectionWidget.ui @@ -0,0 +1,72 @@ + + + QmitkMultiWidgetLayoutSelectionWidget + + + + 0 + 0 + 230 + 230 + + + + + + + QAbstractItemView::NoEditTriggers + + + false + + + false + + + 3 + + + 4 + + + false + + + 50 + + + 50 + + + false + + + 50 + + + 50 + + + + + + + + + + + + + + + + Set multi widget layout + + + + + + + + + diff --git a/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp b/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp new file mode 100644 index 0000000000..61d48df0da --- /dev/null +++ b/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp @@ -0,0 +1,226 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkRenderWindowWidget.h" + +// mitk qt widgets +#include + +// vtk +#include + +QmitkRenderWindowWidget::QmitkRenderWindowWidget(QWidget* parent/* = nullptr*/, + const QString& widgetName/* = ""*/, + mitk::DataStorage* dataStorage/* = nullptr*/, + mitk::BaseRenderer::RenderingMode::Type renderingMode/* = mitk::BaseRenderer::RenderingMode::Standard*/) + : QWidget(parent) + , m_WidgetName(widgetName) + , m_DataStorage(dataStorage) + , m_RenderingMode(renderingMode) + , m_RenderWindow(nullptr) + , m_PointSetNode(nullptr) + , m_PointSet(nullptr) +{ + InitializeGUI(); +} + +QmitkRenderWindowWidget::~QmitkRenderWindowWidget() +{ + auto sliceNavigationController = m_RenderWindow->GetSliceNavigationController(); + if (nullptr != sliceNavigationController) + { + sliceNavigationController->SetCrosshairEvent.RemoveListener(mitk::MessageDelegate1(this, &QmitkRenderWindowWidget::SetCrosshair)); + } + if (nullptr != m_DataStorage) + { + m_DataStorage->Remove(m_PointSetNode); + } +} + +void QmitkRenderWindowWidget::SetDataStorage(mitk::DataStorage* dataStorage) +{ + if (dataStorage == m_DataStorage) + { + return; + } + + m_DataStorage = dataStorage; + if (nullptr != m_RenderWindow) + { + mitk::BaseRenderer::GetInstance(m_RenderWindow->GetRenderWindow())->SetDataStorage(dataStorage); + } +} + +mitk::SliceNavigationController* QmitkRenderWindowWidget::GetSliceNavigationController() const +{ + return m_RenderWindow->GetSliceNavigationController(); +} + +void QmitkRenderWindowWidget::RequestUpdate() +{ + m_RenderingManager->RequestUpdate(m_RenderWindow->GetRenderWindow()); +} + +void QmitkRenderWindowWidget::ForceImmediateUpdate() +{ + m_RenderingManager->ForceImmediateUpdate(m_RenderWindow->GetRenderWindow()); +} + +void QmitkRenderWindowWidget::SetGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower) +{ + vtkRenderer* vtkRenderer = m_RenderWindow->GetRenderer()->GetVtkRenderer(); + if (nullptr == vtkRenderer) + { + return; + } + + m_GradientBackgroundColors.first = upper; + m_GradientBackgroundColors.second = lower; + vtkRenderer->SetBackground(lower[0], lower[1], lower[2]); + vtkRenderer->SetBackground2(upper[0], upper[1], upper[2]); + + ShowGradientBackground(true); +} + +void QmitkRenderWindowWidget::ShowGradientBackground(bool show) +{ + m_RenderWindow->GetRenderer()->GetVtkRenderer()->SetGradientBackground(show); +} + +bool QmitkRenderWindowWidget::IsGradientBackgroundOn() const +{ + return m_RenderWindow->GetRenderer()->GetVtkRenderer()->GetGradientBackground(); +} + +void QmitkRenderWindowWidget::SetDecorationColor(const mitk::Color& color) +{ + m_DecorationColor = color; + m_RectangleProp->SetColor(m_DecorationColor[0], m_DecorationColor[1], m_DecorationColor[2]); + m_CornerAnnotation->GetTextProperty()->SetColor(color[0], color[1], color[2]); +} + +void QmitkRenderWindowWidget::ShowColoredRectangle(bool show) +{ + m_RectangleProp->SetVisibility(show); +} + +bool QmitkRenderWindowWidget::IsColoredRectangleVisible() const +{ + return m_RectangleProp->GetVisibility() > 0; +} + +void QmitkRenderWindowWidget::ShowCornerAnnotation(bool show) +{ + m_CornerAnnotation->SetVisibility(show); +} + +bool QmitkRenderWindowWidget::IsCornerAnnotationVisible() const +{ + return m_CornerAnnotation->GetVisibility() > 0; +} + +void QmitkRenderWindowWidget::SetCornerAnnotationText(const std::string& cornerAnnotation) +{ + m_CornerAnnotation->SetText(0, cornerAnnotation.c_str()); +} + +std::string QmitkRenderWindowWidget::GetCornerAnnotationText() const +{ + return std::string(m_CornerAnnotation->GetText(0)); +} + +bool QmitkRenderWindowWidget::IsRenderWindowMenuActivated() const +{ + return m_RenderWindow->GetActivateMenuWidgetFlag(); +} + +void QmitkRenderWindowWidget::InitializeGUI() +{ + m_Layout = new QHBoxLayout(this); + m_Layout->setMargin(0); + setLayout(m_Layout); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + // create render window for this render window widget + m_RenderingManager = mitk::RenderingManager::GetInstance(); + //m_RenderingManager = mitk::RenderingManager::New(); + m_RenderingManager->SetDataStorage(m_DataStorage); + + m_RenderWindow = new QmitkRenderWindow(this, m_WidgetName, nullptr, m_RenderingManager, m_RenderingMode); + m_RenderWindow->SetLayoutIndex(QmitkCustomMultiWidget::SAGITTAL); // TODO: allow to change layout type later + m_RenderWindow->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Sagittal); + m_RenderWindow->GetSliceNavigationController()->SetRenderingManager(m_RenderingManager); + m_RenderWindow->GetSliceNavigationController()->SetCrosshairEvent.AddListener(mitk::MessageDelegate1(this, &QmitkRenderWindowWidget::SetCrosshair)); + + mitk::TimeGeometry::Pointer timeGeometry = m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll()); + m_RenderingManager->InitializeViews(timeGeometry); + m_Layout->addWidget(m_RenderWindow); + + // add point set as a crosshair + m_PointSetNode = mitk::DataNode::New(); + m_PointSetNode->SetProperty("name", mitk::StringProperty::New("Crosshair of render window " + m_WidgetName.toStdString())); + m_PointSetNode->SetProperty("helper object", mitk::BoolProperty::New(true)); // crosshair-node should typically be invisible + + // set the crosshair only visible for this specific renderer + m_PointSetNode->SetBoolProperty("fixedLayer", true, m_RenderWindow->GetRenderer()); + m_PointSetNode->SetVisibility(true, m_RenderWindow->GetRenderer()); + m_PointSetNode->SetVisibility(false); + + m_PointSet = mitk::PointSet::New(); + m_PointSetNode->SetData(m_PointSet); + //m_DataStorage->Add(m_PointSetNode); + + // set colors and corner annotation + InitializeDecorations(); +} + +void QmitkRenderWindowWidget::InitializeDecorations() +{ + vtkRenderer* vtkRenderer = m_RenderWindow->GetRenderer()->GetVtkRenderer(); + if (nullptr == vtkRenderer) + { + return; + } + + // initialize background color gradients + float black[3] = { 0.0f, 0.0f, 0.0f }; + SetGradientBackgroundColors(black, black); + + // initialize decoration color, rectangle and annotation text + float white[3] = { 1.0f, 1.0f, 1.0f }; + m_DecorationColor = white; + m_RectangleProp = vtkSmartPointer::New(); + m_RectangleProp->SetColor(m_DecorationColor[0], m_DecorationColor[1], m_DecorationColor[2]); + if (0 == vtkRenderer->HasViewProp(m_RectangleProp)) + { + vtkRenderer->AddViewProp(m_RectangleProp); + } + + m_CornerAnnotation = vtkSmartPointer::New(); + m_CornerAnnotation->SetText(0, "Sagittal"); + m_CornerAnnotation->SetMaximumFontSize(12); + m_CornerAnnotation->GetTextProperty()->SetColor(m_DecorationColor[0], m_DecorationColor[1], m_DecorationColor[2]); + if (0 == vtkRenderer->HasViewProp(m_CornerAnnotation)) + { + vtkRenderer->AddViewProp(m_CornerAnnotation); + } +} + +void QmitkRenderWindowWidget::SetCrosshair(mitk::Point3D selectedPoint) +{ + m_PointSet->SetPoint(1, selectedPoint, 0); + mitk::RenderingManager::GetInstance()->RequestUpdate(m_RenderWindow->GetRenderWindow()); +} diff --git a/Modules/RenderWindowManager/include/mitkRenderWindowViewDirectionController.h b/Modules/RenderWindowManager/include/mitkRenderWindowViewDirectionController.h index 40752c35d4..95007119b3 100644 --- a/Modules/RenderWindowManager/include/mitkRenderWindowViewDirectionController.h +++ b/Modules/RenderWindowManager/include/mitkRenderWindowViewDirectionController.h @@ -1,74 +1,86 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKRENDERWINDOWVIEWDIRECTIONCONTROLLER_H #define MITKRENDERWINDOWVIEWDIRECTIONCONTROLLER_H // render window manager module #include "MitkRenderWindowManagerExports.h" #include "mitkRenderWindowLayerUtilities.h" // mitk core #include #include namespace mitk { /** * The RenderWindowViewDirectionController is used to manipulate the 'sliceNavigationController' of a given base renderer. * The 'sliceNavigationController' is used to set the view direction / camera perspective of a base renderer. * The view direction can changed to 'mitk::SliceNavigationController::Axial', 'mitk::SliceNavigationController::Frontal' * or 'mitk::SliceNavigationController::Sagittal'. * * Functions with 'mitk::BaseRenderer* renderer' have 'nullptr' as their default argument. Using the nullptr * these functions operate on all base renderer. Giving a specific base renderer will modify the node only for the given renderer. */ class MITKRENDERWINDOWMANAGER_EXPORT RenderWindowViewDirectionController { public: + using ViewDirection = mitk::SliceNavigationController::ViewDirection; + RenderWindowViewDirectionController(); /** * @brief Set the data storage on which to work. */ void SetDataStorage(DataStorage::Pointer dataStorage); /** * @brief Set the controlled base renderer. */ void SetControlledRenderer(RenderWindowLayerUtilities::RendererVector controlledRenderer); // wrapper functions to modify the view direction /** * @brief Set the view direction for the given renderer (nullptr = all renderer) - * @param viewDirection The view direction that should be used for this renderer. + * @param viewDirection The view direction that should be used for this renderer as a string. * Currently "axial", "coronal" and "sagittal" is supported. * @param renderer Pointer to the renderer instance for which the view direction should be changed. - * If it is a nullptr (default) nothing happens. The view direction can not be changed - * for all controlled renderer at the moment. + * If it is a nullptr (default) nothing happens. */ void SetViewDirectionOfRenderer(const std::string &viewDirection, BaseRenderer* renderer = nullptr); + /** + * @brief Set the view direction for the given renderer (nullptr = all renderer) + * @param viewDirection The view direction that should be used for this renderer. + * @param renderer Pointer to the renderer instance for which the view direction should be changed. + * If it is a nullptr (default) nothing happens. + */ + void SetViewDirectionOfRenderer(ViewDirection viewDirection, BaseRenderer* renderer = nullptr); + /** + * @brief Reinitialize the given renderer with the currently visible nodes. + * @param renderer Pointer to the renderer instance for which the view direction should be changed. + * If it is a nullptr (default) nothing happens. + */ + void InitializeViewByBoundingObjects(const BaseRenderer* renderer); private: - void InitializeViewByBoundingObjects(const BaseRenderer* renderer); - DataStorage::Pointer m_DataStorage; RenderWindowLayerUtilities::RendererVector m_ControlledRenderer; }; } // namespace mitk #endif // MITKRENDERWINDOWVIEWDIRECTIONCONTROLLER_H diff --git a/Modules/RenderWindowManager/src/mitkRenderWindowViewDirectionController.cpp b/Modules/RenderWindowManager/src/mitkRenderWindowViewDirectionController.cpp index c149b2d814..7768e90cd7 100644 --- a/Modules/RenderWindowManager/src/mitkRenderWindowViewDirectionController.cpp +++ b/Modules/RenderWindowManager/src/mitkRenderWindowViewDirectionController.cpp @@ -1,110 +1,132 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkRenderWindowViewDirectionController.h" // mitk #include #include #include mitk::RenderWindowViewDirectionController::RenderWindowViewDirectionController() : m_DataStorage(nullptr) { // nothing here } void mitk::RenderWindowViewDirectionController::SetDataStorage(DataStorage::Pointer dataStorage) { if (m_DataStorage != dataStorage) { // set the new data storage m_DataStorage = dataStorage; } } void mitk::RenderWindowViewDirectionController::SetControlledRenderer(RenderWindowLayerUtilities::RendererVector controlledRenderer) { if (m_ControlledRenderer != controlledRenderer) { // set the new set of controlled renderer m_ControlledRenderer = controlledRenderer; } } -void mitk::RenderWindowViewDirectionController::SetViewDirectionOfRenderer(const std::string &viewDirection, BaseRenderer* renderer /*=nullptr*/) +void mitk::RenderWindowViewDirectionController::SetViewDirectionOfRenderer(const std::string &viewDirection, BaseRenderer* renderer/* =nullptr*/) { if (nullptr == renderer) { - // set visibility of data node in all controlled renderer for (auto& renderer : m_ControlledRenderer) { if (renderer.IsNotNull()) { - //SetViewDirectionOfRenderer(viewDirection, renderer); + SetViewDirectionOfRenderer(viewDirection, renderer); } } } else { mitk::SliceNavigationController* sliceNavigationController = renderer->GetSliceNavigationController(); if ("axial" == viewDirection) { - sliceNavigationController->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); + sliceNavigationController->SetDefaultViewDirection(ViewDirection::Axial); } else if ("coronal" == viewDirection) { - sliceNavigationController->SetDefaultViewDirection(mitk::SliceNavigationController::Frontal); + sliceNavigationController->SetDefaultViewDirection(ViewDirection::Frontal); } else if ("sagittal" == viewDirection) { - sliceNavigationController->SetDefaultViewDirection(mitk::SliceNavigationController::Sagittal); + sliceNavigationController->SetDefaultViewDirection(ViewDirection::Sagittal); } // initialize the views to the bounding geometry InitializeViewByBoundingObjects(renderer); } } +void mitk::RenderWindowViewDirectionController::SetViewDirectionOfRenderer(ViewDirection viewDirection , BaseRenderer* renderer/* =nullptr*/) +{ + if (nullptr == renderer) + { + // set visibility of data node in all controlled renderer + for (auto& renderer : m_ControlledRenderer) + { + if (renderer.IsNotNull()) + { + SetViewDirectionOfRenderer(viewDirection, renderer); + } + } + } + else + { + mitk::SliceNavigationController* sliceNavigationController = renderer->GetSliceNavigationController(); + sliceNavigationController->SetDefaultViewDirection(viewDirection); + + // initialize the views to the bounding geometry + InitializeViewByBoundingObjects(renderer); + } +} + void mitk::RenderWindowViewDirectionController::InitializeViewByBoundingObjects(const BaseRenderer* renderer) { if (nullptr == m_DataStorage || nullptr == renderer) { return; } // get all nodes that have not set "includeInBoundingBox" to false mitk::NodePredicateProperty::Pointer includeInBoundingBox = mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false)); mitk::NodePredicateNot::Pointer notIncludeInBoundingBox = mitk::NodePredicateNot::New(includeInBoundingBox); // get all non-helper objects mitk::NodePredicateProperty::Pointer helperObject = mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true)); mitk::NodePredicateNot::Pointer notAHelperObject = mitk::NodePredicateNot::New(helperObject); // get all nodes that have a fixed layer for the given renderer mitk::NodePredicateProperty::Pointer fixedLayer = mitk::NodePredicateProperty::New("fixedLayer", mitk::BoolProperty::New(true), renderer); // combine node predicates mitk::NodePredicateAnd::Pointer combinedNodePredicate = mitk::NodePredicateAnd::New(notIncludeInBoundingBox, notAHelperObject, fixedLayer); mitk::DataStorage::SetOfObjects::ConstPointer filteredDataNodes = m_DataStorage->GetSubset(combinedNodePredicate); // calculate bounding geometry of these nodes auto bounds = m_DataStorage->ComputeBoundingGeometry3D(filteredDataNodes, "visible", renderer); // initialize the views to the bounding geometry mitk::RenderingManager::GetInstance()->InitializeView(renderer->GetRenderWindow(), bounds); } \ No newline at end of file diff --git a/Plugins/PluginList.cmake b/Plugins/PluginList.cmake index ecfb25c700..42e36efbf9 100644 --- a/Plugins/PluginList.cmake +++ b/Plugins/PluginList.cmake @@ -1,103 +1,104 @@ # Plug-ins must be ordered according to their dependencies set(MITK_PLUGINS org.blueberry.core.runtime:ON org.blueberry.core.expressions:OFF org.blueberry.core.commands:OFF org.blueberry.core.jobs:OFF org.blueberry.ui.qt:OFF org.blueberry.ui.qt.help:ON org.blueberry.ui.qt.log:ON org.blueberry.ui.qt.objectinspector:OFF #org.blueberry.test:ON #org.blueberry.uitest:ON #Testing/org.blueberry.core.runtime.tests:ON #Testing/org.blueberry.osgi.tests:ON org.mitk.core.services:ON org.mitk.gui.common:ON org.mitk.planarfigure:ON org.mitk.core.ext:OFF org.mitk.core.jobs:OFF org.mitk.gui.qt.application:ON org.mitk.gui.qt.coreapplication:OFF org.mitk.gui.qt.ext:OFF org.mitk.gui.qt.extapplication:OFF org.mitk.gui.qt.common:ON org.mitk.gui.qt.stdmultiwidgeteditor:ON + org.mitk.gui.qt.custommultiwidgeteditor:OFF org.mitk.gui.qt.common.legacy:OFF org.mitk.gui.qt.cmdlinemodules:OFF org.mitk.gui.qt.diffusionimagingapp:OFF org.mitk.gui.qt.datamanager:ON org.mitk.gui.qt.datamanagerlight:OFF org.mitk.gui.qt.datastorageviewertest:OFF org.mitk.gui.qt.properties:ON org.mitk.gui.qt.basicimageprocessing:OFF org.mitk.gui.qt.dicom:OFF org.mitk.gui.qt.dicominspector:OFF org.mitk.gui.qt.diffusionimaging:OFF org.mitk.gui.qt.diffusionimaging.connectomics:OFF org.mitk.gui.qt.diffusionimaging.denoising:OFF org.mitk.gui.qt.diffusionimaging.fiberfox:OFF org.mitk.gui.qt.diffusionimaging.fiberprocessing:OFF org.mitk.gui.qt.diffusionimaging.ivim:OFF org.mitk.gui.qt.diffusionimaging.odfpeaks:OFF org.mitk.gui.qt.diffusionimaging.partialvolume:OFF org.mitk.gui.qt.diffusionimaging.preprocessing:OFF org.mitk.gui.qt.diffusionimaging.reconstruction:OFF org.mitk.gui.qt.diffusionimaging.registration:OFF org.mitk.gui.qt.diffusionimaging.tbss:OFF org.mitk.gui.qt.diffusionimaging.tractography:OFF org.mitk.gui.qt.diffusionimaging.python:OFF org.mitk.gui.qt.dosevisualization:OFF org.mitk.gui.qt.geometrytools:OFF org.mitk.gui.qt.igtexamples:OFF org.mitk.gui.qt.igttracking:OFF org.mitk.gui.qt.lasercontrol:OFF org.mitk.gui.qt.openigtlink:OFF org.mitk.gui.qt.imagecropper:OFF org.mitk.gui.qt.imagenavigator:ON org.mitk.gui.qt.viewnavigator:OFF org.mitk.gui.qt.materialeditor:OFF org.mitk.gui.qt.measurementtoolbox:OFF org.mitk.gui.qt.moviemaker:OFF org.mitk.gui.qt.pointsetinteraction:OFF org.mitk.gui.qt.pointsetinteractionmultispectrum:OFF org.mitk.gui.qt.python:OFF org.mitk.gui.qt.remeshing:OFF org.mitk.gui.qt.segmentation:OFF org.mitk.gui.qt.aicpregistration:OFF org.mitk.gui.qt.renderwindowmanager:OFF org.mitk.gui.qt.toftutorial:OFF org.mitk.gui.qt.tofutil:OFF org.mitk.gui.qt.tubegraph:OFF org.mitk.gui.qt.ugvisualization:OFF org.mitk.gui.qt.photoacoustics.pausviewer:OFF org.mitk.gui.qt.photoacoustics.imageprocessing:OFF org.mitk.gui.qt.photoacoustics.simulation:OFF org.mitk.gui.qt.ultrasound:OFF org.mitk.gui.qt.volumevisualization:OFF org.mitk.gui.qt.eventrecorder:OFF org.mitk.gui.qt.xnat:OFF org.mitk.gui.qt.igt.app.echotrack:OFF org.mitk.gui.qt.spectrocamrecorder:OFF org.mitk.gui.qt.classificationsegmentation:OFF org.mitk.gui.qt.overlaymanager:OFF org.mitk.gui.qt.igt.app.hummelprotocolmeasurements:OFF org.mitk.gui.qt.multilabelsegmentation:OFF org.mitk.matchpoint.core.helper:OFF org.mitk.gui.qt.matchpoint.algorithm.browser:OFF org.mitk.gui.qt.matchpoint.algorithm.control:OFF org.mitk.gui.qt.matchpoint.algorithm.batch:OFF org.mitk.gui.qt.matchpoint.mapper:OFF org.mitk.gui.qt.matchpoint.framereg:OFF org.mitk.gui.qt.matchpoint.visualizer:OFF org.mitk.gui.qt.matchpoint.evaluator:OFF org.mitk.gui.qt.matchpoint.manipulator:OFF org.mitk.gui.qt.preprocessing.resampling:OFF org.mitk.gui.qt.cest:OFF ) diff --git a/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPartListener.h b/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPartListener.h index afd7f19561..8f72eb2cb5 100644 --- a/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPartListener.h +++ b/Plugins/org.mitk.gui.common/src/mitkIRenderWindowPartListener.h @@ -1,60 +1,65 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ - #ifndef MITKIRENDERWINDOWPARTLISTENER_H #define MITKIRENDERWINDOWPARTLISTENER_H #include namespace mitk { -struct IRenderWindowPart; - -/** - * \ingroup org_mitk_gui_common - * - * \brief Interface for berry::IViewPart implementations to be notified about mitk::IRenderWindowPart lifecycle changes. - * - * This interface is intended to be implemented by subclasses of berry::IWorkbenchPart. If implemented, - * the interface methods are called automatically if a Workbench part which implementes mitk::IRenderWindowPart - * is activated or deactivated. - * - * The notion of activated and deactivated is slightly different from the usual Workbench part lifecycle. - */ -struct MITK_GUI_COMMON_PLUGIN IRenderWindowPartListener -{ - virtual ~IRenderWindowPartListener(); + struct IRenderWindowPart; /** - * Called when a IRenderWindowPart is activated or if it becomes visible and no - * other IRenderWindowPart was activated before. + * \ingroup org_mitk_gui_common * - * \param renderWindowPart The newly activated IRenderWindowPart. - */ - virtual void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) = 0; - - /** - * Called when a IRenderWindowPart becomes invisible and if it was active before. + * \brief Interface for berry::IViewPart implementations to be notified about mitk::IRenderWindowPart lifecycle changes. * - * \param renderWindowPart The deactivated IRenderWindowPart. + * This interface is intended to be implemented by subclasses of berry::IWorkbenchPart. If implemented, + * the interface methods are called automatically if a Workbench part which implementes mitk::IRenderWindowPart + * is activated or deactivated. + * + * The notion of activated and deactivated is slightly different from the usual Workbench part lifecycle. */ - virtual void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) = 0; -}; + struct MITK_GUI_COMMON_PLUGIN IRenderWindowPartListener + { + virtual ~IRenderWindowPartListener(); + + /** + * Called when an IRenderWindowPart is activated or if it becomes visible and no + * other IRenderWindowPart was activated before. + * + * \param renderWindowPart The newly activated IRenderWindowPart. + */ + virtual void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) = 0; + + /** + * Called when an IRenderWindowPart becomes invisible and if it was active before. + * + * \param renderWindowPart The deactivated IRenderWindowPart. + */ + virtual void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) = 0; + /** + * Called when an IRenderWindowPart changes and if it was active before. + * + * \param renderWindowPart The modified IRenderWindowPart. + */ + virtual void RenderWindowPartInputChanged(mitk::IRenderWindowPart* /*renderWindowPart*/) {}; + }; } #endif // MITKIRENDERWINDOWPARTLISTENER_H diff --git a/Plugins/org.mitk.gui.qt.common/src/internal/QmitkViewCoordinator.cpp b/Plugins/org.mitk.gui.qt.common/src/internal/QmitkViewCoordinator.cpp index 513edecd85..ec3c8cc14e 100644 --- a/Plugins/org.mitk.gui.qt.common/src/internal/QmitkViewCoordinator.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/internal/QmitkViewCoordinator.cpp @@ -1,217 +1,238 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkViewCoordinator.h" #include "QmitkAbstractView.h" #include #include #include #include #include QmitkViewCoordinator::QmitkViewCoordinator() : m_ActiveZombieView(nullptr) , m_ActiveRenderWindowPart(nullptr) , m_VisibleRenderWindowPart(nullptr) { } QmitkViewCoordinator::~QmitkViewCoordinator() { } void QmitkViewCoordinator::Start() { berry::PlatformUI::GetWorkbench()->AddWindowListener(this); QList wnds(berry::PlatformUI::GetWorkbench()->GetWorkbenchWindows()); - for (QList::iterator i = wnds.begin(); - i != wnds.end(); ++i) + for (QList::iterator i = wnds.begin(); i != wnds.end(); ++i) { (*i)->GetPartService()->AddPartListener(this); } } void QmitkViewCoordinator::Stop() { if (!berry::PlatformUI::IsWorkbenchRunning()) return; berry::PlatformUI::GetWorkbench()->RemoveWindowListener(this); QList wnds(berry::PlatformUI::GetWorkbench()->GetWorkbenchWindows()); - for (QList::iterator i = wnds.begin(); - i != wnds.end(); ++i) + for (QList::iterator i = wnds.begin(); i != wnds.end(); ++i) { (*i)->GetPartService()->RemovePartListener(this); } } berry::IPartListener::Events::Types QmitkViewCoordinator::GetPartEventTypes() const { return berry::IPartListener::Events::ACTIVATED | berry::IPartListener::Events::DEACTIVATED | berry::IPartListener::Events::CLOSED | berry::IPartListener::Events::HIDDEN - | berry::IPartListener::Events::VISIBLE | berry::IPartListener::Events::OPENED; + | berry::IPartListener::Events::VISIBLE | berry::IPartListener::Events::OPENED + | berry::IPartListener::Events::INPUT_CHANGED; } -void QmitkViewCoordinator::PartActivated(const berry::IWorkbenchPartReference::Pointer& partRef ) +void QmitkViewCoordinator::PartActivated(const berry::IWorkbenchPartReference::Pointer& partRef) { //MITK_INFO << "*** PartActivated (" << partRef->GetPart(false)->GetPartName() << ")"; berry::IWorkbenchPart* part = partRef->GetPart(false).GetPointer(); // Check for a render window part and inform IRenderWindowPartListener views // that it was activated - if ( mitk::IRenderWindowPart* renderPart = dynamic_cast(part) ) + if (mitk::IRenderWindowPart* renderPart = dynamic_cast(part)) { if (m_VisibleRenderWindowPart != renderPart) { RenderWindowPartActivated(renderPart); m_ActiveRenderWindowPart = renderPart; m_VisibleRenderWindowPart = renderPart; } } // Check if the activated part wants to be notified if (mitk::ILifecycleAwarePart* lifecycleAwarePart = dynamic_cast(part)) { lifecycleAwarePart->Activated(); } // Check if a zombie view has been activated. if (mitk::IZombieViewPart* zombieView = dynamic_cast(part)) { if (m_ActiveZombieView && (m_ActiveZombieView != zombieView)) { // Another zombie view has been activated. Tell the old one about it. m_ActiveZombieView->ActivatedZombieView(partRef); m_ActiveZombieView = zombieView; } } } -void QmitkViewCoordinator::PartDeactivated(const berry::IWorkbenchPartReference::Pointer& partRef ) +void QmitkViewCoordinator::PartDeactivated(const berry::IWorkbenchPartReference::Pointer& partRef) { //MITK_INFO << "*** PartDeactivated (" << partRef->GetPart(false)->GetPartName() << ")"; berry::IWorkbenchPart* part = partRef->GetPart(false).GetPointer(); // Check for a render window part and inform IRenderWindowPartListener views // that it was deactivated if (mitk::IRenderWindowPart* renderPart = dynamic_cast(part)) { if (m_ActiveRenderWindowPart == renderPart) { this->RenderWindowPartDeactivated(renderPart); m_ActiveRenderWindowPart = nullptr; m_VisibleRenderWindowPart = nullptr; } } if (mitk::ILifecycleAwarePart* lifecycleAwarePart = dynamic_cast(part)) { lifecycleAwarePart->Deactivated(); } } -void QmitkViewCoordinator::PartOpened(const berry::IWorkbenchPartReference::Pointer& partRef ) +void QmitkViewCoordinator::PartOpened(const berry::IWorkbenchPartReference::Pointer& partRef) { //MITK_INFO << "*** PartOpened (" << partRef->GetPart(false)->GetPartName() << ")"; berry::IWorkbenchPart* part = partRef->GetPart(false).GetPointer(); if (mitk::IRenderWindowPartListener* renderWindowListener = dynamic_cast(part)) { m_RenderWindowListeners.insert(renderWindowListener); } } -void QmitkViewCoordinator::PartClosed(const berry::IWorkbenchPartReference::Pointer& partRef ) +void QmitkViewCoordinator::PartClosed(const berry::IWorkbenchPartReference::Pointer& partRef) { //MITK_INFO << "*** PartClosed (" << partRef->GetPart(false)->GetPartName() << ")"; berry::IWorkbenchPart* part = partRef->GetPart(false).GetPointer(); if (mitk::IRenderWindowPartListener* renderWindowListener = dynamic_cast(part)) { m_RenderWindowListeners.remove(renderWindowListener); } } -void QmitkViewCoordinator::PartHidden(const berry::IWorkbenchPartReference::Pointer& partRef ) +void QmitkViewCoordinator::PartHidden(const berry::IWorkbenchPartReference::Pointer& partRef) { //MITK_INFO << "*** PartHidden (" << partRef->GetPart(false)->GetPartName() << ")"; berry::IWorkbenchPart* part = partRef->GetPart(false).GetPointer(); // Check for a render window part and if it is the currently active on. // Inform IRenderWindowPartListener views that it has been hidden. - if ( mitk::IRenderWindowPart* renderPart = dynamic_cast(part) ) + if (mitk::IRenderWindowPart* renderPart = dynamic_cast(part)) { if (!m_ActiveRenderWindowPart && m_VisibleRenderWindowPart == renderPart) { RenderWindowPartDeactivated(renderPart); m_VisibleRenderWindowPart = nullptr; } } if (mitk::ILifecycleAwarePart* lifecycleAwarePart = dynamic_cast(part)) { lifecycleAwarePart->Hidden(); } } -void QmitkViewCoordinator::PartVisible(const berry::IWorkbenchPartReference::Pointer& partRef ) +void QmitkViewCoordinator::PartVisible(const berry::IWorkbenchPartReference::Pointer& partRef) { //MITK_INFO << "*** PartVisible (" << partRef->GetPart(false)->GetPartName() << ")"; berry::IWorkbenchPart* part = partRef->GetPart(false).GetPointer(); // Check for a render window part and inform IRenderWindowPartListener views // that it was activated - if ( mitk::IRenderWindowPart* renderPart = dynamic_cast(part) ) + if (mitk::IRenderWindowPart* renderPart = dynamic_cast(part)) { if (!m_ActiveRenderWindowPart) { RenderWindowPartActivated(renderPart); m_VisibleRenderWindowPart = renderPart; } } if (mitk::ILifecycleAwarePart* lifecycleAwarePart = dynamic_cast(part)) { lifecycleAwarePart->Visible(); } } -void QmitkViewCoordinator::RenderWindowPartActivated(mitk::IRenderWindowPart* renderPart) +void QmitkViewCoordinator::PartInputChanged(const berry::IWorkbenchPartReference::Pointer& partRef) { - foreach (mitk::IRenderWindowPartListener* l, m_RenderWindowListeners) + berry::IWorkbenchPart* part = partRef->GetPart(false).GetPointer(); + + // Check for a render window part and inform IRenderWindowPartListener views + // that it was changed + if (mitk::IRenderWindowPart* renderPart = dynamic_cast(part)) { - l->RenderWindowPartActivated(renderPart); + if (!m_ActiveRenderWindowPart) + { + RenderWindowPartInputChanged(renderPart); + } } } -void QmitkViewCoordinator::RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderPart) +void QmitkViewCoordinator::WindowOpened(const berry::IWorkbenchWindow::Pointer& window) { - foreach (mitk::IRenderWindowPartListener* l, m_RenderWindowListeners) + window->GetPartService()->AddPartListener(this); +} + +void QmitkViewCoordinator::WindowClosed(const berry::IWorkbenchWindow::Pointer& /*window*/) +{ +} + +void QmitkViewCoordinator::RenderWindowPartActivated(mitk::IRenderWindowPart* renderPart) +{ + for (auto& listener : m_RenderWindowListeners) { - l->RenderWindowPartDeactivated(renderPart); + listener->RenderWindowPartActivated(renderPart); } } -void QmitkViewCoordinator::WindowClosed(const berry::IWorkbenchWindow::Pointer& /*window*/ ) +void QmitkViewCoordinator::RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderPart) { + for (auto& listener : m_RenderWindowListeners) + { + listener->RenderWindowPartDeactivated(renderPart); + } } -void QmitkViewCoordinator::WindowOpened(const berry::IWorkbenchWindow::Pointer& window ) +void QmitkViewCoordinator::RenderWindowPartInputChanged(mitk::IRenderWindowPart* renderPart) { - window->GetPartService()->AddPartListener(this); + for (auto& listener : m_RenderWindowListeners) + { + listener->RenderWindowPartInputChanged(renderPart); + } } - diff --git a/Plugins/org.mitk.gui.qt.common/src/internal/QmitkViewCoordinator.h b/Plugins/org.mitk.gui.qt.common/src/internal/QmitkViewCoordinator.h index 398ee82f21..1be33f4a81 100644 --- a/Plugins/org.mitk.gui.qt.common/src/internal/QmitkViewCoordinator.h +++ b/Plugins/org.mitk.gui.qt.common/src/internal/QmitkViewCoordinator.h @@ -1,122 +1,128 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkViewCoordinator_h #define QmitkViewCoordinator_h #include #include #include #include #include namespace mitk { struct IRenderWindowPart; struct IRenderWindowPartListener; struct IZombieViewPart; } class QmitkAbstractView; /** * A class which coordinates active QmitkAbstractView s, e.g. calling activated and hidden on them. */ class QmitkViewCoordinator : private berry::IPartListener, private berry::IWindowListener { public: /** * Add listener */ QmitkViewCoordinator(); /** * Remove listener */ ~QmitkViewCoordinator() override; void Start(); void Stop(); //#IPartListener methods (these methods internally call Activated() or other similar methods) /** * \see IPartListener::GetPartEventTypes() */ berry::IPartListener::Events::Types GetPartEventTypes() const override; /** * \see IPartListener::PartActivated() */ void PartActivated (const berry::IWorkbenchPartReference::Pointer& partRef) override; /** * \see IPartListener::PartDeactivated() */ void PartDeactivated(const berry::IWorkbenchPartReference::Pointer& /*partRef*/) override; /** * \see IPartListener::PartOpened() */ void PartOpened(const berry::IWorkbenchPartReference::Pointer& partRef) override; /** * \see IPartListener::PartClosed() */ void PartClosed (const berry::IWorkbenchPartReference::Pointer& partRef) override; /** * \see IPartListener::PartHidden() */ void PartHidden(const berry::IWorkbenchPartReference::Pointer& partRef) override; /** * \see IPartListener::PartVisible() */ void PartVisible(const berry::IWorkbenchPartReference::Pointer& partRef) override; /** - * Notifies this listener that the given window has been closed. - */ - void WindowClosed(const berry::IWorkbenchWindow::Pointer& window) override; + * \see IPartListener::PartInputChanged() + */ + void PartInputChanged(const berry::IWorkbenchPartReference::Pointer& partRef) override; /** - * Notifies this listener that the given window has been opened. + * Notifies this listener that the given window has been opened. + */ + void WindowOpened(const berry::IWorkbenchWindow::Pointer& window) override; + + /** + * Notifies this listener that the given window has been closed. */ - void WindowOpened(const berry::IWorkbenchWindow::Pointer& /*window*/) override; + void WindowClosed(const berry::IWorkbenchWindow::Pointer& window) override; private: void RenderWindowPartActivated(mitk::IRenderWindowPart* renderPart); void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderPart); + void RenderWindowPartInputChanged(mitk::IRenderWindowPart* renderPart); private: mitk::IZombieViewPart* m_ActiveZombieView; mitk::IRenderWindowPart* m_ActiveRenderWindowPart; mitk::IRenderWindowPart* m_VisibleRenderWindowPart; QSet m_RenderWindowListeners; }; #endif // QmitkViewCoordinator_h diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/CMakeLists.txt b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/CMakeLists.txt new file mode 100644 index 0000000000..308d5992c8 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/CMakeLists.txt @@ -0,0 +1,7 @@ +project(org_mitk_gui_qt_custommultiwidgeteditor) + +mitk_create_plugin( + EXPORT_DIRECTIVE CUSTOMMULTIWIDGETEDITOR_EXPORT + EXPORTED_INCLUDE_SUFFIXES src + MODULE_DEPENDS MitkQtWidgets MitkRenderWindowManager +) diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/files.cmake b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/files.cmake new file mode 100644 index 0000000000..79481c4266 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/files.cmake @@ -0,0 +1,36 @@ +set(SRC_CPP_FILES + QmitkCustomMultiWidgetEditor.cpp + QmitkMultiWidgetDecorationManager.cpp +) + +set(INTERNAL_CPP_FILES + mitkPluginActivator.cpp + QmitkCustomMultiWidgetEditorPreferencePage.cpp +) + +set(UI_FILES + src/internal/QmitkCustomMultiWidgetEditorPreferencePage.ui +) + +set(MOC_H_FILES + src/QmitkCustomMultiWidgetEditor.h + + src/internal/mitkPluginActivator.h + src/internal/QmitkCustomMultiWidgetEditorPreferencePage.h +) + +set(CACHED_RESOURCE_FILES + resources/CustomMultiWidgetEditor.svg + plugin.xml +) + +set(QRC_FILES +) + +foreach(file ${SRC_CPP_FILES}) + set(CPP_FILES ${CPP_FILES} src/${file}) +endforeach(file ${SRC_CPP_FILES}) + +foreach(file ${INTERNAL_CPP_FILES}) + set(CPP_FILES ${CPP_FILES} src/internal/${file}) +endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/manifest_headers.cmake new file mode 100644 index 0000000000..a9b763fa3a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/manifest_headers.cmake @@ -0,0 +1,6 @@ +set(Plugin-Name "MITK Custom Multi Widget Editor") +set(Plugin-Version "1.0.0") +set(Plugin-Vendor "DKFZ, Division of Medical Image Computing") +set(Plugin-ContactAddress "http://www.mitk.org") +set(Require-Plugin org.mitk.gui.qt.common) + diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/plugin.xml b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/plugin.xml new file mode 100644 index 0000000000..606705c432 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/plugin.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/resources/CustomMultiWidgetEditor.svg b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/resources/CustomMultiWidgetEditor.svg new file mode 100644 index 0000000000..31d6ae02ac --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/resources/CustomMultiWidgetEditor.svg @@ -0,0 +1,55 @@ + + + + + + + image/svg+xml + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/resources/QmitkCustomMultiWidgetEditor.qrc b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/resources/QmitkCustomMultiWidgetEditor.qrc new file mode 100644 index 0000000000..64a1a29379 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/resources/QmitkCustomMultiWidgetEditor.qrc @@ -0,0 +1,5 @@ + + + defaultWatermark.png + + diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/resources/defaultWatermark.png b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/resources/defaultWatermark.png new file mode 100644 index 0000000000..927adce68d Binary files /dev/null and b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/resources/defaultWatermark.png differ diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/QmitkCustomMultiWidgetEditor.cpp b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/QmitkCustomMultiWidgetEditor.cpp new file mode 100644 index 0000000000..beba8e70f8 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/QmitkCustomMultiWidgetEditor.cpp @@ -0,0 +1,279 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkCustomMultiWidgetEditor.h" + +#include +#include +#include +#include +#include + +// custom multi widget editor plugin +#include "QmitkMultiWidgetDecorationManager.h" + +// mitk qt widgets module +#include +#include +#include + +const QString QmitkCustomMultiWidgetEditor::EDITOR_ID = "org.mitk.editors.custommultiwidget"; + +class QmitkCustomMultiWidgetEditor::Impl final +{ + +public: + + Impl(); + ~Impl(); + + void SetControlledRenderer(); + + QmitkCustomMultiWidget* m_CustomMultiWidget; + QmitkInteractionSchemeToolBar* m_InteractionSchemeToolBar; + QmitkMultiWidgetConfigurationToolBar* m_ConfigurationToolBar; + + std::unique_ptr m_MultiWidgetDecorationManager; + + std::unique_ptr m_RenderWindowViewDirectionController; +}; + +QmitkCustomMultiWidgetEditor::Impl::Impl() + : m_CustomMultiWidget(nullptr) + , m_InteractionSchemeToolBar(nullptr) + , m_ConfigurationToolBar(nullptr) +{ + // nothing here +} + +QmitkCustomMultiWidgetEditor::Impl::~Impl() +{ + // nothing here +} + +void QmitkCustomMultiWidgetEditor::Impl::SetControlledRenderer() +{ + if (nullptr == m_RenderWindowViewDirectionController || nullptr == m_CustomMultiWidget) + { + return; + } + + RenderWindowLayerUtilities::RendererVector controlledRenderer; + auto renderWindowWidgets = m_CustomMultiWidget->GetRenderWindowWidgets(); + for (const auto& renderWindowWidget : renderWindowWidgets) + { + auto renderWindow = renderWindowWidget.second->GetRenderWindow(); + auto vtkRenderWindow = renderWindow->GetRenderWindow(); + mitk::BaseRenderer* baseRenderer = mitk::BaseRenderer::GetInstance(vtkRenderWindow); + if (nullptr != baseRenderer) + { + controlledRenderer.push_back(baseRenderer); + } + } + + m_RenderWindowViewDirectionController->SetControlledRenderer(controlledRenderer); +} + +////////////////////////////////////////////////////////////////////////// +// QmitkCustomMultiWidgetEditor +////////////////////////////////////////////////////////////////////////// +QmitkCustomMultiWidgetEditor::QmitkCustomMultiWidgetEditor() + : m_Impl(new Impl()) +{ + // nothing here +} + +QmitkCustomMultiWidgetEditor::~QmitkCustomMultiWidgetEditor() +{ + // nothing here +} + +QmitkRenderWindow* QmitkCustomMultiWidgetEditor::GetActiveQmitkRenderWindow() const +{ + if (nullptr != m_Impl->m_CustomMultiWidget) + { + auto activeRenderWindowWidget = m_Impl->m_CustomMultiWidget->GetActiveRenderWindowWidget(); + if (nullptr != activeRenderWindowWidget) + { + return activeRenderWindowWidget->GetRenderWindow(); + } + } + + return nullptr; +} + +QHash QmitkCustomMultiWidgetEditor::GetQmitkRenderWindows() const +{ + QHash result; + if (nullptr == m_Impl->m_CustomMultiWidget) + { + return result; + } + + result = m_Impl->m_CustomMultiWidget->GetRenderWindows(); + return result; +} + +QmitkRenderWindow* QmitkCustomMultiWidgetEditor::GetQmitkRenderWindow(const QString& id) const +{ + if (nullptr == m_Impl->m_CustomMultiWidget) + { + return nullptr; + } + + return m_Impl->m_CustomMultiWidget->GetRenderWindow(id); +} + +mitk::Point3D QmitkCustomMultiWidgetEditor::GetSelectedPosition(const QString& id) const +{ + if (nullptr == m_Impl->m_CustomMultiWidget) + { + return mitk::Point3D(); + } + + return m_Impl->m_CustomMultiWidget->GetSelectedPosition(id); +} + +void QmitkCustomMultiWidgetEditor::SetSelectedPosition(const mitk::Point3D& pos, const QString& id) +{ + if (nullptr != m_Impl->m_CustomMultiWidget) + { + m_Impl->m_CustomMultiWidget->SetSelectedPosition(id, pos); + } +} + +void QmitkCustomMultiWidgetEditor::EnableDecorations(bool enable, const QStringList& decorations) +{ + m_Impl->m_MultiWidgetDecorationManager->ShowDecorations(enable, decorations); +} + +bool QmitkCustomMultiWidgetEditor::IsDecorationEnabled(const QString& decoration) const +{ + return m_Impl->m_MultiWidgetDecorationManager->IsDecorationVisible(decoration); +} + +QStringList QmitkCustomMultiWidgetEditor::GetDecorations() const +{ + return m_Impl->m_MultiWidgetDecorationManager->GetDecorations(); +} + +void QmitkCustomMultiWidgetEditor::EnableSlicingPlanes(bool /*enable*/) +{ + // nothing here +} + +bool QmitkCustomMultiWidgetEditor::IsSlicingPlanesEnabled() const +{ + // nothing here + return false; +} + +QmitkCustomMultiWidget* QmitkCustomMultiWidgetEditor::GetCustomMultiWidget() +{ + return m_Impl->m_CustomMultiWidget; +} + +void QmitkCustomMultiWidgetEditor::OnLayoutSet(int row, int column) +{ + m_Impl->m_CustomMultiWidget->ResetLayout(row, column); + m_Impl->SetControlledRenderer(); + FirePropertyChange(berry::IWorkbenchPartConstants::PROP_INPUT); +} + +void QmitkCustomMultiWidgetEditor::OnSynchronize(bool synchronized) +{ + m_Impl->m_CustomMultiWidget->Synchronize(synchronized); +} + +void QmitkCustomMultiWidgetEditor::OnViewDirectionChanged(ViewDirection viewDirection) +{ + m_Impl->m_RenderWindowViewDirectionController->SetViewDirectionOfRenderer(viewDirection); +} + +////////////////////////////////////////////////////////////////////////// +// PRIVATE +////////////////////////////////////////////////////////////////////////// +void QmitkCustomMultiWidgetEditor::SetFocus() +{ + if (nullptr != m_Impl->m_CustomMultiWidget) + { + m_Impl->m_CustomMultiWidget->setFocus(); + } +} + +void QmitkCustomMultiWidgetEditor::CreateQtPartControl(QWidget* parent) +{ + if (nullptr == m_Impl->m_CustomMultiWidget) + { + QHBoxLayout* layout = new QHBoxLayout(parent); + layout->setContentsMargins(0, 0, 0, 0); + + berry::IBerryPreferences* preferences = dynamic_cast(GetPreferences().GetPointer()); + mitk::BaseRenderer::RenderingMode::Type renderingMode = static_cast(preferences->GetInt("Rendering Mode", 0)); + + m_Impl->m_CustomMultiWidget = new QmitkCustomMultiWidget(parent, 0, 0, renderingMode); + + // create left toolbar: interaction scheme toolbar to switch how the render window navigation behaves + if (nullptr == m_Impl->m_InteractionSchemeToolBar) + { + m_Impl->m_InteractionSchemeToolBar = new QmitkInteractionSchemeToolBar(parent); + layout->addWidget(m_Impl->m_InteractionSchemeToolBar); + } + m_Impl->m_InteractionSchemeToolBar->SetInteractionEventHandler(m_Impl->m_CustomMultiWidget->GetInteractionEventHandler()); + + // add center widget: the custom multi widget + layout->addWidget(m_Impl->m_CustomMultiWidget); + + m_Impl->m_CustomMultiWidget->SetDataStorage(GetDataStorage()); + m_Impl->m_CustomMultiWidget->InitializeRenderWindowWidgets(); + + // create right toolbar: configuration toolbar to change the render window widget layout + if (nullptr == m_Impl->m_ConfigurationToolBar) + { + m_Impl->m_ConfigurationToolBar = new QmitkMultiWidgetConfigurationToolBar(m_Impl->m_CustomMultiWidget); + layout->addWidget(m_Impl->m_ConfigurationToolBar); + } + + connect(m_Impl->m_ConfigurationToolBar, &QmitkMultiWidgetConfigurationToolBar::LayoutSet, this, &QmitkCustomMultiWidgetEditor::OnLayoutSet); + connect(m_Impl->m_ConfigurationToolBar, &QmitkMultiWidgetConfigurationToolBar::Synchronized, this, &QmitkCustomMultiWidgetEditor::OnSynchronize); + connect(m_Impl->m_ConfigurationToolBar, &QmitkMultiWidgetConfigurationToolBar::ViewDirectionChanged, this, &QmitkCustomMultiWidgetEditor::OnViewDirectionChanged); + + m_Impl->m_MultiWidgetDecorationManager = std::make_unique(m_Impl->m_CustomMultiWidget); + + m_Impl->m_RenderWindowViewDirectionController = std::make_unique(); + m_Impl->m_RenderWindowViewDirectionController->SetDataStorage(GetDataStorage()); + m_Impl->SetControlledRenderer(); + + OnPreferencesChanged(preferences); + } +} + +void QmitkCustomMultiWidgetEditor::OnPreferencesChanged(const berry::IBerryPreferences* preferences) +{ + if (m_Impl->m_CustomMultiWidget->GetRenderWindowWidgets().empty()) + { + return; + } + // update decoration preferences + m_Impl->m_MultiWidgetDecorationManager->DecorationPreferencesChanged(preferences); + + // zooming and panning preferences + bool constrainedZooming = preferences->GetBool("Use constrained zooming and panning", true); + mitk::RenderingManager::GetInstance()->SetConstrainedPanningZooming(constrainedZooming); + + mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(GetDataStorage()); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/QmitkCustomMultiWidgetEditor.h b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/QmitkCustomMultiWidgetEditor.h new file mode 100644 index 0000000000..571a7a9571 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/QmitkCustomMultiWidgetEditor.h @@ -0,0 +1,117 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QMITKCUSTOMMULTIWIDGETEDITOR_H +#define QMITKCUSTOMMULTIWIDGETEDITOR_H + +#include +#include + +// custom multi widget editor +#include + +// mitk render window manager +#include + +#include + +class QmitkCustomMultiWidget; + +class CUSTOMMULTIWIDGETEDITOR_EXPORT QmitkCustomMultiWidgetEditor final : public QmitkAbstractRenderEditor, public mitk::ILinkedRenderWindowPart +{ + Q_OBJECT + +public: + + using ViewDirection = mitk::SliceNavigationController::ViewDirection; + + berryObjectMacro(QmitkCustomMultiWidgetEditor) + + static const QString EDITOR_ID; + + QmitkCustomMultiWidgetEditor(); + ~QmitkCustomMultiWidgetEditor(); + + /** + * @brief Overridden from mitk::ILinkedRenderWindowPart : IRenderWindowPart + */ + virtual QmitkRenderWindow* GetActiveQmitkRenderWindow() const override; + /** + * @brief Overridden from mitk::ILinkedRenderWindowPart : IRenderWindowPart + */ + virtual QHash GetQmitkRenderWindows() const override; + /** + * @brief Overridden from mitk::ILinkedRenderWindowPart : IRenderWindowPart + */ + virtual QmitkRenderWindow* GetQmitkRenderWindow(const QString& id) const override; + /** + * @brief Overridden from mitk::ILinkedRenderWindowPart : IRenderWindowPart + */ + virtual mitk::Point3D GetSelectedPosition(const QString& id = QString()) const override; + /** + * @brief Overridden from mitk::ILinkedRenderWindowPart : IRenderWindowPart + */ + virtual void SetSelectedPosition(const mitk::Point3D& pos, const QString& id = QString()) override; + /** + * @brief Overridden from mitk::ILinkedRenderWindowPart : IRenderWindowPart + */ + virtual void EnableDecorations(bool enable, const QStringList& decorations = QStringList()) override; + /** + * @brief Overridden from mitk::ILinkedRenderWindowPart : IRenderWindowPart + */ + virtual bool IsDecorationEnabled(const QString& decoration) const override; + /** + * @brief Overridden from mitk::ILinkedRenderWindowPart : IRenderWindowPart + */ + virtual QStringList GetDecorations() const override; + /** + * @brief Overridden from mitk::ILinkedRenderWindowPart + */ + virtual void EnableSlicingPlanes(bool enable) override; + /** + * @brief Overridden from mitk::ILinkedRenderWindowPart + */ + virtual bool IsSlicingPlanesEnabled() const override; + /** + * @brief Return the current custom multi widget of this editor. + */ + QmitkCustomMultiWidget* GetCustomMultiWidget(); + +private Q_SLOTS: + + void OnLayoutSet(int row, int column); + void OnSynchronize(bool synchronized); + void OnViewDirectionChanged(ViewDirection viewDirection); + +private: + /** + * @brief Overridden from QmitkAbstractRenderEditor + */ + virtual void SetFocus() override; + /** + * @brief Overridden from QmitkAbstractRenderEditor + */ + virtual void CreateQtPartControl(QWidget* parent) override; + /** + * @brief Overridden from QmitkAbstractRenderEditor + */ + virtual void OnPreferencesChanged(const berry::IBerryPreferences* preferences) override; + + class Impl; + const std::unique_ptr m_Impl; +}; + +#endif // QMITKCUSTOMMULTIWIDGETEDITOR_H diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/QmitkMultiWidgetDecorationManager.cpp b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/QmitkMultiWidgetDecorationManager.cpp new file mode 100644 index 0000000000..6ef9f3d0c8 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/QmitkMultiWidgetDecorationManager.cpp @@ -0,0 +1,458 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkMultiWidgetDecorationManager.h" + +// org_mitk_gui_common +#include + +// mitk annotation +#include + +// vtk +#include + +// qt +#include + +QmitkMultiWidgetDecorationManager::QmitkMultiWidgetDecorationManager(QmitkCustomMultiWidget* customMultiWidget) + : m_CustomMultiWidget(customMultiWidget) + , m_LogoAnnotation(mitk::LogoAnnotation::New()) +{ + // nothing here +} + +void QmitkMultiWidgetDecorationManager::DecorationPreferencesChanged(const berry::IBerryPreferences* preferences) +{ + // Enable change of logo. If no DepartmentLogo was set explicitly, MBILogo is used. + // Set new department logo by prefs->Set("DepartmentLogo", "PathToImage"); + + // If no logo was set for this plug-in specifically, walk the parent preference nodes + // and lookup a logo value there. + + // Disable the logo first, otherwise setting a new logo will have no effect due to how mitkManufacturerLogo works + ShowLogo(false); + SetupLogo(qPrintable(":/org.mitk.gui.qt.stdmultiwidgeteditor/defaultWatermark.png")); + ShowLogo(true); + + const berry::IPreferences* currentNode = preferences; + while (currentNode) + { + bool logoFound = false; + foreach(const QString& key, currentNode->Keys()) + { + if (key == "DepartmentLogo") + { + ShowLogo(false); + QString departmentLogoLocation = currentNode->Get("DepartmentLogo", ""); + if (!departmentLogoLocation.isEmpty()) + { + SetupLogo(qPrintable(departmentLogoLocation)); + ShowLogo(true); + } + logoFound = true; + break; + } + } + + if (logoFound) + { + break; + } + currentNode = currentNode->Parent().GetPointer(); + } + + QmitkMultiWidgetDecorationManager::Colormap colormap = static_cast(preferences->GetInt("Render window widget colormap", 0)); + SetColormap(colormap); + + // show colored rectangle + ShowAllColoredRectangles(true); + + // show corner annotations + ShowAllCornerAnnotations(true); +} + +void QmitkMultiWidgetDecorationManager::ShowDecorations(bool show, const QStringList& decorations) +{ + if (nullptr != m_CustomMultiWidget) + { + return; + } + + if (decorations.isEmpty() || decorations.contains(mitk::IRenderWindowPart::DECORATION_BORDER)) + { + ShowAllColoredRectangles(show); + } + if (decorations.isEmpty() || decorations.contains(mitk::IRenderWindowPart::DECORATION_LOGO)) + { + ShowLogo(show); + } + if (decorations.isEmpty() || decorations.contains(mitk::IRenderWindowPart::DECORATION_MENU)) + { + //m_CustomMultiWidget->ActivateAllRenderWindowMenus(show); + } + if (decorations.isEmpty() || decorations.contains(mitk::IRenderWindowPart::DECORATION_BACKGROUND)) + { + ShowAllGradientBackgrounds(show); + } + if (decorations.isEmpty() || decorations.contains(mitk::IRenderWindowPart::DECORATION_CORNER_ANNOTATION)) + { + ShowAllCornerAnnotations(show); + } +} + +bool QmitkMultiWidgetDecorationManager::IsDecorationVisible(const QString& decoration) const +{ + if (mitk::IRenderWindowPart::DECORATION_BORDER == decoration) + { + return AreAllColoredRectanglesVisible(); + } + else if (mitk::IRenderWindowPart::DECORATION_LOGO == decoration) + { + return IsLogoVisible(); + } + else if (mitk::IRenderWindowPart::DECORATION_MENU == decoration) + { + //return IsMenuWidgetEnabled(); + } + else if (mitk::IRenderWindowPart::DECORATION_BACKGROUND == decoration) + { + return AreAllGradientBackgroundsOn(); + } + else if (mitk::IRenderWindowPart::DECORATION_CORNER_ANNOTATION == decoration) + { + return AreAllCornerAnnotationsVisible(); + } + + return false; +} + +QStringList QmitkMultiWidgetDecorationManager::GetDecorations() const +{ + QStringList decorations; + decorations << mitk::IRenderWindowPart::DECORATION_BORDER << mitk::IRenderWindowPart::DECORATION_LOGO << mitk::IRenderWindowPart::DECORATION_MENU + << mitk::IRenderWindowPart::DECORATION_BACKGROUND << mitk::IRenderWindowPart::DECORATION_CORNER_ANNOTATION; + return decorations; +} + +////////////////////////////////////////////////////////////////////////// +// PRIVATE +////////////////////////////////////////////////////////////////////////// +void QmitkMultiWidgetDecorationManager::SetupLogo(const char* path) +{ + m_LogoAnnotation->SetOpacity(0.5); + mitk::Point2D offset; + offset.Fill(0.03); + m_LogoAnnotation->SetOffsetVector(offset); + m_LogoAnnotation->SetRelativeSize(0.25); + m_LogoAnnotation->SetCornerPosition(1); + vtkSmartPointer vtkLogo = GetVtkLogo(path); + + SetLogo(vtkLogo); +} + +vtkSmartPointer QmitkMultiWidgetDecorationManager::GetVtkLogo(const char* path) +{ + QImage* qimage = new QImage(path); + vtkSmartPointer qImageToVtk; + qImageToVtk = vtkSmartPointer::New(); + + qImageToVtk->SetQImage(qimage); + qImageToVtk->Update(); + vtkSmartPointer vtkLogo = qImageToVtk->GetOutput(); + return vtkLogo; +} + +void QmitkMultiWidgetDecorationManager::SetLogo(vtkSmartPointer vtkLogo) +{ + std::shared_ptr renderWindowWidget = m_CustomMultiWidget->GetLastRenderWindowWidget(); + if (nullptr != renderWindowWidget && m_LogoAnnotation.IsNotNull()) + { + mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_LogoAnnotation.GetPointer(), renderWindowWidget->GetRenderWindow()->GetRenderer()); + m_LogoAnnotation->SetLogoImage(vtkLogo); + mitk::BaseRenderer *renderer = mitk::BaseRenderer::GetInstance(renderWindowWidget->GetRenderWindow()->GetVtkRenderWindow()); + m_LogoAnnotation->Update(renderer); + renderWindowWidget->RequestUpdate(); + return; + } + + MITK_ERROR << "Logo can not be set for an unknown widget."; +} + +void QmitkMultiWidgetDecorationManager::ShowLogo(bool show) +{ + std::shared_ptr renderWindowWidget = m_CustomMultiWidget->GetLastRenderWindowWidget(); + if (nullptr != renderWindowWidget) + { + m_LogoAnnotation->SetVisibility(show); + renderWindowWidget->RequestUpdate(); + return; + } + + MITK_ERROR << "Logo can not be shown for an unknown widget."; +} + +bool QmitkMultiWidgetDecorationManager::IsLogoVisible() const +{ + return m_LogoAnnotation->IsVisible(); +} + +void QmitkMultiWidgetDecorationManager::SetColormap(QmitkMultiWidgetDecorationManager::Colormap colormap) +{ + switch (colormap) + { + case Colormap::BlackAndWhite: + { + FillAllGradientBackgroundColorsWithBlack(); + float white[3] = { 1.0f, 1.0f, 1.0f }; + SetAllDecorationColors(white); + break; + } + } +} + +void QmitkMultiWidgetDecorationManager::SetDecorationColor(const QString& widgetID, const mitk::Color& color) +{ + std::shared_ptr renderWindowWidget = m_CustomMultiWidget->GetRenderWindowWidget(widgetID); + if (nullptr != renderWindowWidget) + { + renderWindowWidget->SetDecorationColor(color); + return; + } + + MITK_ERROR << "Decoration color can not be set for an unknown widget."; +} + +void QmitkMultiWidgetDecorationManager::SetAllDecorationColors(const mitk::Color& color) +{ + QmitkCustomMultiWidget::RenderWindowWidgetMap renderWindowWidgets = m_CustomMultiWidget->GetRenderWindowWidgets(); + for (const auto& renderWindowWidget : renderWindowWidgets) + { + renderWindowWidget.second->SetDecorationColor(color); + } +} + +mitk::Color QmitkMultiWidgetDecorationManager::GetDecorationColor(const QString& widgetID) const +{ + std::shared_ptr renderWindowWidget = m_CustomMultiWidget->GetRenderWindowWidget(widgetID); + if (nullptr != renderWindowWidget) + { + return renderWindowWidget->GetDecorationColor(); + } + + MITK_ERROR << "Decoration color can not be retrieved for an unknown widget. Returning black color!"; + float black[3] = { 0.0f, 0.0f, 0.0f }; + return mitk::Color(black); +} + +void QmitkMultiWidgetDecorationManager::ShowColoredRectangle(const QString& widgetID, bool show) +{ + std::shared_ptr renderWindowWidget = m_CustomMultiWidget->GetRenderWindowWidget(widgetID); + if (nullptr != renderWindowWidget) + { + renderWindowWidget->ShowColoredRectangle(show); + return; + } + + MITK_ERROR << "Colored rectangle can not be set for an unknown widget."; +} + +void QmitkMultiWidgetDecorationManager::ShowAllColoredRectangles(bool show) +{ + QmitkCustomMultiWidget::RenderWindowWidgetMap renderWindowWidgets = m_CustomMultiWidget->GetRenderWindowWidgets(); + for (const auto& renderWindowWidget : renderWindowWidgets) + { + renderWindowWidget.second->ShowColoredRectangle(show); + } +} + +bool QmitkMultiWidgetDecorationManager::IsColoredRectangleVisible(const QString& widgetID) const +{ + std::shared_ptr renderWindowWidget = m_CustomMultiWidget->GetRenderWindowWidget(widgetID); + if (nullptr != renderWindowWidget) + { + return renderWindowWidget->IsColoredRectangleVisible(); + } + + MITK_ERROR << "Colored rectangle visibility can not be retrieved for an unknown widget. Returning 'false'."; + return false; +} + +bool QmitkMultiWidgetDecorationManager::AreAllColoredRectanglesVisible() const +{ + QmitkCustomMultiWidget::RenderWindowWidgetMap renderWindowWidgets = m_CustomMultiWidget->GetRenderWindowWidgets(); + bool allTrue = true; + for (const auto& renderWindowWidget : renderWindowWidgets) + { + allTrue = allTrue && renderWindowWidget.second->IsColoredRectangleVisible(); + } + + return allTrue; +} + +void QmitkMultiWidgetDecorationManager::SetGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower, const QString& widgetID) +{ + std::shared_ptr renderWindowWidget = m_CustomMultiWidget->GetRenderWindowWidget(widgetID); + if (nullptr != renderWindowWidget) + { + renderWindowWidget->SetGradientBackgroundColors(upper, lower); + return; + } + + MITK_ERROR << "Background color gradient can not be set for an unknown widget."; +} + +void QmitkMultiWidgetDecorationManager::SetAllGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower) +{ + QmitkCustomMultiWidget::RenderWindowWidgetMap renderWindowWidgets = m_CustomMultiWidget->GetRenderWindowWidgets(); + for (const auto& renderWindowWidget : renderWindowWidgets) + { + renderWindowWidget.second->SetGradientBackgroundColors(upper, lower); + } +} + +void QmitkMultiWidgetDecorationManager::FillAllGradientBackgroundColorsWithBlack() +{ + float black[3] = { 0.0f, 0.0f, 0.0f }; + SetAllGradientBackgroundColors(black, black); +} + +void QmitkMultiWidgetDecorationManager::ShowGradientBackground(const QString& widgetID, bool show) +{ + std::shared_ptr renderWindowWidget = m_CustomMultiWidget->GetRenderWindowWidget(widgetID); + if (nullptr != renderWindowWidget) + { + renderWindowWidget->ShowGradientBackground(show); + return; + } + + MITK_ERROR << "Background color gradient can not be shown for an unknown widget."; +} + +void QmitkMultiWidgetDecorationManager::ShowAllGradientBackgrounds(bool show) +{ + QmitkCustomMultiWidget::RenderWindowWidgetMap renderWindowWidgets = m_CustomMultiWidget->GetRenderWindowWidgets(); + for (const auto& renderWindowWidget : renderWindowWidgets) + { + renderWindowWidget.second->ShowGradientBackground(show); + } +} + +std::pair QmitkMultiWidgetDecorationManager::GetGradientBackgroundColors(const QString& widgetID) const +{ + std::shared_ptr renderWindowWidget = m_CustomMultiWidget->GetRenderWindowWidget(widgetID); + if (nullptr != renderWindowWidget) + { + return renderWindowWidget->GetGradientBackgroundColors(); + } + + MITK_ERROR << "Background color gradient can not be retrieved for an unknown widget. Returning black color pair."; + float black[3] = { 0.0f, 0.0f, 0.0f }; + return std::make_pair(mitk::Color(black), mitk::Color(black)); +} + +bool QmitkMultiWidgetDecorationManager::IsGradientBackgroundOn(const QString& widgetID) const +{ + std::shared_ptr renderWindowWidget = m_CustomMultiWidget->GetRenderWindowWidget(widgetID); + if (nullptr != renderWindowWidget) + { + return renderWindowWidget->IsGradientBackgroundOn(); + } + + MITK_ERROR << "Background color gradient flag can not be retrieved for an unknown widget. Returning 'false'."; + return false; +} + +bool QmitkMultiWidgetDecorationManager::AreAllGradientBackgroundsOn() const +{ + QmitkCustomMultiWidget::RenderWindowWidgetMap renderWindowWidgets = m_CustomMultiWidget->GetRenderWindowWidgets(); + bool allTrue = true; + for (const auto& renderWindowWidget : renderWindowWidgets) + { + allTrue = allTrue && renderWindowWidget.second->IsGradientBackgroundOn(); + } + + return allTrue; +} + +void QmitkMultiWidgetDecorationManager::SetCornerAnnotationText(const QString& widgetID, const std::string& cornerAnnotation) +{ + std::shared_ptr renderWindowWidget = m_CustomMultiWidget->GetRenderWindowWidget(widgetID); + if (nullptr != renderWindowWidget) + { + renderWindowWidget->SetCornerAnnotationText(cornerAnnotation); + return; + } + + MITK_ERROR << "Corner annotation text can not be retrieved for an unknown widget."; +} + +std::string QmitkMultiWidgetDecorationManager::GetCornerAnnotationText(const QString& widgetID) const +{ + std::shared_ptr renderWindowWidget = m_CustomMultiWidget->GetRenderWindowWidget(widgetID); + if (nullptr != renderWindowWidget) + { + return renderWindowWidget->GetCornerAnnotationText(); + } + + MITK_ERROR << "Corner annotation text can not be retrieved for an unknown widget."; + return ""; +} + +void QmitkMultiWidgetDecorationManager::ShowCornerAnnotation(const QString& widgetID, bool show) +{ + std::shared_ptr renderWindowWidget = m_CustomMultiWidget->GetRenderWindowWidget(widgetID); + if (nullptr != renderWindowWidget) + { + renderWindowWidget->ShowCornerAnnotation(show); + return; + } + + MITK_ERROR << "Corner annotation can not be set for an unknown widget."; +} + +void QmitkMultiWidgetDecorationManager::ShowAllCornerAnnotations(bool show) +{ + QmitkCustomMultiWidget::RenderWindowWidgetMap renderWindowWidgets = m_CustomMultiWidget->GetRenderWindowWidgets(); + for (const auto& renderWindowWidget : renderWindowWidgets) + { + renderWindowWidget.second->ShowCornerAnnotation(show); + } +} + +bool QmitkMultiWidgetDecorationManager::IsCornerAnnotationVisible(const QString& widgetID) const +{ + std::shared_ptr renderWindowWidget = m_CustomMultiWidget->GetRenderWindowWidget(widgetID); + if (nullptr != renderWindowWidget) + { + return renderWindowWidget->IsCornerAnnotationVisible(); + } + + MITK_ERROR << "Corner annotation visibility can not be retrieved for an unknown widget. Returning 'false'."; + return false; +} + +bool QmitkMultiWidgetDecorationManager::AreAllCornerAnnotationsVisible() const +{ + QmitkCustomMultiWidget::RenderWindowWidgetMap renderWindowWidgets = m_CustomMultiWidget->GetRenderWindowWidgets(); + bool allTrue = true; + for (const auto& renderWindowWidget : renderWindowWidgets) + { + allTrue = allTrue && renderWindowWidget.second->IsCornerAnnotationVisible(); + } + + return allTrue; +} diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/QmitkMultiWidgetDecorationManager.h b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/QmitkMultiWidgetDecorationManager.h new file mode 100644 index 0000000000..6b0637d29a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/QmitkMultiWidgetDecorationManager.h @@ -0,0 +1,144 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QMITKMULTIWIDGETDECORATIONMANAGER_H +#define QMITKMULTIWIDGETDECORATIONMANAGER_H + +// custom multi widget editor +#include + +// mitk core +#include + +// mitk annotation +#include + +// mitk qtwidgets +#include + +// berry +#include + +// vtk +#include +#include + +// qt +#include +#include + +/** +* @brief +* +* +*/ +class CUSTOMMULTIWIDGETEDITOR_EXPORT QmitkMultiWidgetDecorationManager +{ + +public: + + QmitkMultiWidgetDecorationManager(QmitkCustomMultiWidget* customMultiWidget); + + enum class Colormap + { + BlackAndWhite = 0 // black background, white decoration + }; + + void DecorationPreferencesChanged(const berry::IBerryPreferences* preferences); + + /** + * @brief Show or hide decorations like like colored borders or background, logos, menu widgets, logos and + * text annotations. + * + * \@par Show the decorations specified in decorations if true. Hide them, if not. + * \@par A list of decoration names. If empty, all supported decorations are affected. + */ + void ShowDecorations(bool show, const QStringList& decorations); + /** + * @brief Return if a specific decoration is visible. + * + * \return True, if the specified decoration is shown, false if not. + */ + bool IsDecorationVisible(const QString &decoration) const; + QStringList GetDecorations() const; + +private: + + void SetupLogo(const char* path); + vtkSmartPointer GetVtkLogo(const char* path); + void SetLogo(vtkSmartPointer vtkLogo); + void ShowLogo(bool show); + bool IsLogoVisible() const; + + void SetColormap(Colormap colormap); + + void SetDecorationColor(const QString& widgetID, const mitk::Color& color); + void SetAllDecorationColors(const mitk::Color& color); + mitk::Color GetDecorationColor(const QString& widgetID) const; + + void ShowColoredRectangle(const QString& widgetID, bool show); + void ShowAllColoredRectangles(bool show); + bool IsColoredRectangleVisible(const QString& widgetID) const; + bool AreAllColoredRectanglesVisible() const; + + /** + * @brief Set a background color gradient for a specific render window. + * + * If two different input colors are used, a gradient background is generated. + * + * @param upper The color of the gradient background. + * @param lower The color of the gradient background. + * @param widgetID The widget identifier. + */ + void SetGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower, const QString& widgetID); + /** + * @brief Set a background color gradient for all available render windows. + * + * If two different input colors are used, a gradient background is generated. + * + * @param upper The color of the gradient background. + * @param lower The color of the gradient background. + */ + void SetAllGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower); + void FillAllGradientBackgroundColorsWithBlack(); + void ShowGradientBackground(const QString& widgetID, bool show); + void ShowAllGradientBackgrounds(bool show); + /** + * @rief Return a render window (widget) specific background color gradient + * + * @param widgetID The widget identifier. + * + * @return A color gradient as a pair of colors. + * First entry: upper color value + * Second entry: lower color value + */ + std::pair GetGradientBackgroundColors(const QString& widgetID) const; + bool IsGradientBackgroundOn(const QString& widgetID) const; + bool AreAllGradientBackgroundsOn() const; + + void SetCornerAnnotationText(const QString& widgetID, const std::string& cornerAnnotation); + std::string GetCornerAnnotationText(const QString& widgetID) const; + void ShowCornerAnnotation(const QString& widgetID, bool show); + void ShowAllCornerAnnotations(bool show); + bool IsCornerAnnotationVisible(const QString& widgetID) const; + bool AreAllCornerAnnotationsVisible() const; + + QmitkCustomMultiWidget* m_CustomMultiWidget; + mitk::LogoAnnotation::Pointer m_LogoAnnotation; + +}; + +#endif // QMITKMULTIWIDGETDECORATIONMANAGER_H diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/QmitkCustomMultiWidgetEditorPreferencePage.cpp b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/QmitkCustomMultiWidgetEditorPreferencePage.cpp new file mode 100644 index 0000000000..de3c064e12 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/QmitkCustomMultiWidgetEditorPreferencePage.cpp @@ -0,0 +1,128 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkCustomMultiWidgetEditorPreferencePage.h" +#include + +// berry framework +#include +#include + +QmitkCustomMultiWidgetEditorPreferencePage::QmitkCustomMultiWidgetEditorPreferencePage() + : m_Preferences(nullptr) +{ + // nothing here +} + +QmitkCustomMultiWidgetEditorPreferencePage::~QmitkCustomMultiWidgetEditorPreferencePage() +{ + //nothing here +} + +void QmitkCustomMultiWidgetEditorPreferencePage::Init(berry::IWorkbench::Pointer) +{ + // nothing here +} + +void QmitkCustomMultiWidgetEditorPreferencePage::CreateQtControl(QWidget* parent) +{ + m_MainControl = new QWidget(parent); + + m_Ui.setupUi(m_MainControl); + + berry::IPreferencesService* preferenceService = berry::Platform::GetPreferencesService(); + Q_ASSERT(preferenceService); + m_Preferences = preferenceService->GetSystemPreferences()->Node(QmitkCustomMultiWidgetEditor::EDITOR_ID); + + connect(m_Ui.m_RenderingModeComboBox, SIGNAL(activated(int)), SLOT(ChangeRenderingMode(int))); + connect(m_Ui.m_ColormapComboBox, SIGNAL(activated(int)), SLOT(ChangeColormap(int))); + connect(m_Ui.m_ResetButton, SIGNAL(clicked()), SLOT(ResetPreferencesAndGUI())); + + Update(); +} + +QWidget* QmitkCustomMultiWidgetEditorPreferencePage::GetQtControl() const +{ + return m_MainControl; +} + +bool QmitkCustomMultiWidgetEditorPreferencePage::PerformOk() +{ + m_Preferences->PutBool("Use constrained zooming and panning", m_Ui.m_EnableFlexibleZooming->isChecked()); + m_Preferences->PutBool("Show level/window widget", m_Ui.m_ShowLevelWindowWidget->isChecked()); + m_Preferences->PutBool("PACS like mouse interaction", m_Ui.m_PACSLikeMouseMode->isChecked()); + + m_Preferences->PutInt("Rendering Mode", m_Ui.m_RenderingModeComboBox->currentIndex()); + + m_Preferences->PutInt("Render window widget colormap", m_Ui.m_ColormapComboBox->currentIndex()); + m_Preferences->PutBool("Render window individual decorations", m_Ui.m_IndividualDecorations->isChecked()); + + m_Preferences->PutInt("crosshair gap size", m_Ui.m_CrosshairGapSize->value()); + + return true; +} + +void QmitkCustomMultiWidgetEditorPreferencePage::PerformCancel() +{ + // nothing here +} + +void QmitkCustomMultiWidgetEditorPreferencePage::Update() +{ + m_Ui.m_EnableFlexibleZooming->setChecked(m_Preferences->GetBool("Use constrained zooming and panning", true)); + m_Ui.m_ShowLevelWindowWidget->setChecked(m_Preferences->GetBool("Show level/window widget", true)); + m_Ui.m_PACSLikeMouseMode->setChecked(m_Preferences->GetBool("PACS like mouse interaction", false)); + + int renderingMode = m_Preferences->GetInt("Rendering Mode", 0); + m_Ui.m_RenderingModeComboBox->setCurrentIndex(renderingMode); + + int colormap = m_Preferences->GetInt("Render window widget colormap", 0); + m_Ui.m_ColormapComboBox->setCurrentIndex(colormap); + + m_Ui.m_IndividualDecorations->setChecked(m_Preferences->GetBool("Render window individual decorations", false)); + + m_Ui.m_CrosshairGapSize->setValue(m_Preferences->GetInt("crosshair gap size", 32)); +} + +void QmitkCustomMultiWidgetEditorPreferencePage::ResetPreferencesAndGUI() +{ + m_Preferences->Clear(); + Update(); +} + +void QmitkCustomMultiWidgetEditorPreferencePage::ChangeRenderingMode(int i) +{ + if (0 == i) + { + m_CurrentRenderingMode = "Standard"; + } + else if (1 == i) + { + m_CurrentRenderingMode = "Multisampling"; + } + else if (2 == i) + { + m_CurrentRenderingMode = "DepthPeeling"; + } +} + +void QmitkCustomMultiWidgetEditorPreferencePage::ChangeColormap(int i) +{ + if (0 == i) + { + m_CurrentColormap = "Black and white"; + } +} \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/QmitkCustomMultiWidgetEditorPreferencePage.h b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/QmitkCustomMultiWidgetEditorPreferencePage.h new file mode 100644 index 0000000000..06ed1df152 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/QmitkCustomMultiWidgetEditorPreferencePage.h @@ -0,0 +1,80 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QMITKCUSTOMMULTIWIDGETEDITORPREFERENCEPAGE_H +#define QMITKCUSTOMMULTIWIDGETEDITORPREFERENCEPAGE_H + +// custom multi widget editor +#include "ui_QmitkCustomMultiWidgetEditorPreferencePage.h" + +#include +#include +#include +#include +#include + +class QmitkCustomMultiWidgetEditorPreferencePage : public QObject, public berry::IQtPreferencePage +{ + Q_OBJECT + Q_INTERFACES(berry::IPreferencePage) + +public: + QmitkCustomMultiWidgetEditorPreferencePage(); + ~QmitkCustomMultiWidgetEditorPreferencePage(); + + void Init(berry::IWorkbench::Pointer) override; + void CreateQtControl(QWidget* parent) override; + QWidget* GetQtControl() const override; + + bool PerformOk() override; + void PerformCancel() override; + void Update() override; + +public slots: + /** + * @brief ResetColors set default colors and refresh the GUI. + */ + void ResetPreferencesAndGUI(); + + /** + * @brief ChangeRenderingMode slot to chose the rendering mode via QComboBox. + * @param i index of the box. + */ + void ChangeRenderingMode(int i); + + void ChangeColormap(int i); + +protected: + /** + * @brief m_CurrentRenderingMode String for the rendering mode. + */ + std::string m_CurrentRenderingMode; + + std::string m_CurrentColormap; + + /** + * @brief m_Preferences the berry preferences. + */ + berry::IPreferences::Pointer m_Preferences; + +private: + + Ui::QmitkCustomMultiWidgetEditorPreferencePage m_Ui; + QWidget* m_MainControl; + +}; + +#endif // QMITKCUSTOMMULTIWIDGETEDITORPREFERENCEPAGE_H diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/QmitkCustomMultiWidgetEditorPreferencePage.ui b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/QmitkCustomMultiWidgetEditorPreferencePage.ui new file mode 100644 index 0000000000..f360b20922 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/QmitkCustomMultiWidgetEditorPreferencePage.ui @@ -0,0 +1,163 @@ + + + QmitkCustomMultiWidgetEditorPreferencePage + + + + 0 + 0 + 520 + 320 + + + + External Programs + + + + + + <html><head/><body><p>If activated, zooming and panning is limited to a certain space arround each image.</p></body></html> + + + Qt::LeftToRight + + + Use constraint zooming and panning + + + true + + + + + + + Qt::LeftToRight + + + Show level/window widget + + + true + + + + + + + Qt::LeftToRight + + + Use PACS like mouse interaction (select left mouse button action) + + + + + + + Qt::Horizontal + + + + + + + Rendering Mode* + + + + + + + + Standard Rendering + + + + + Enable Multisampling (Antialiasing) + + + + + Enable Depth Peeling + + + + + + + + * Changes require restart of MITK. + Depth Peeling is only supported by Windows. + For other OS, use Standard Rendering and enable + the property 'Depth Sorting' in the property list of the surface data node. + + + + + + + Qt::Horizontal + + + + + + + Colormap + + + + + + + + Black and white + + + + + + + + + 0 + 0 + + + + Allow render window individual decorations + + + + + + + <html><head/><body><p>The gap in the middle of the crosshair in pixels.</p></body></html> + + + Crosshair gap size + + + + + + + 32 + + + + + + + Reset preferences + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/mitkPluginActivator.cpp b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/mitkPluginActivator.cpp new file mode 100644 index 0000000000..76a4cb77aa --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/mitkPluginActivator.cpp @@ -0,0 +1,45 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkPluginActivator.h" + +#include +#include "QmitkCustomMultiWidgetEditorPreferencePage.h" + +namespace mitk +{ + ctkPluginContext* CustomMultiWidgetActivator::m_Context = nullptr; + + void CustomMultiWidgetActivator::start(ctkPluginContext* context) + { + m_Context = context; + + BERRY_REGISTER_EXTENSION_CLASS(QmitkCustomMultiWidgetEditor, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkCustomMultiWidgetEditorPreferencePage, context) + } + + void CustomMultiWidgetActivator::stop(ctkPluginContext* context) + { + Q_UNUSED(context) + + m_Context = nullptr; + } + + ctkPluginContext* CustomMultiWidgetActivator::GetContext() + { + return m_Context; + } +} \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/mitkPluginActivator.h b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/mitkPluginActivator.h new file mode 100644 index 0000000000..b25c98fb6b --- /dev/null +++ b/Plugins/org.mitk.gui.qt.custommultiwidgeteditor/src/internal/mitkPluginActivator.h @@ -0,0 +1,43 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical Image Computing. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef MITKPLUGINACTIVATOR_H +#define MITKPLUGINACTIVATOR_H + +#include + +namespace mitk +{ + class CustomMultiWidgetActivator : public QObject, public ctkPluginActivator + { + Q_OBJECT + Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_custommultiwidgeteditor") + Q_INTERFACES(ctkPluginActivator) + + public: + + void start(ctkPluginContext* context) override; + void stop(ctkPluginContext* context) override; + + static ctkPluginContext* GetContext(); + + private: + + static ctkPluginContext* m_Context; + + }; +} +#endif // MITKPLUGINACTIVATOR_H diff --git a/Plugins/org.mitk.gui.qt.ext/files.cmake b/Plugins/org.mitk.gui.qt.ext/files.cmake index f911d28d06..faea93108a 100644 --- a/Plugins/org.mitk.gui.qt.ext/files.cmake +++ b/Plugins/org.mitk.gui.qt.ext/files.cmake @@ -1,59 +1,63 @@ set(SRC_CPP_FILES QmitkExtActionBarAdvisor.cpp QmitkExtWorkbenchWindowAdvisor.cpp QmitkExtFileSaveProjectAction.cpp QmitkOpenDicomEditorAction.cpp + QmitkOpenCustomMultiWidgetEditorAction.cpp + QmitkOpenStdMultiWidgetEditorAction.cpp ) set(INTERNAL_CPP_FILES QmitkAboutHandler.cpp QmitkAppInstancesPreferencePage.cpp QmitkExternalProgramsPreferencePage.cpp QmitkCommonExtPlugin.cpp QmitkInputDevicesPrefPage.cpp QmitkModuleView.cpp ) set(UI_FILES src/internal/QmitkAppInstancesPreferencePage.ui src/internal/QmitkExternalProgramsPreferencePage.ui ) set(MOC_H_FILES src/QmitkExtFileSaveProjectAction.h src/QmitkExtWorkbenchWindowAdvisor.h src/internal/QmitkAboutHandler.h src/internal/QmitkAppInstancesPreferencePage.h src/internal/QmitkExternalProgramsPreferencePage.h src/internal/QmitkCommonExtPlugin.h src/internal/QmitkExtWorkbenchWindowAdvisorHack.h src/internal/QmitkInputDevicesPrefPage.h src/internal/QmitkModuleView.h src/QmitkOpenDicomEditorAction.h + src/QmitkOpenCustomMultiWidgetEditorAction.h + src/QmitkOpenStdMultiWidgetEditorAction.h ) set(CACHED_RESOURCE_FILES # list of resource files which can be used by the plug-in # system without loading the plug-ins shared library, # for example the icon used in the menu and tabs for the # plug-in views in the workbench plugin.xml resources/ModuleView.png ) set(QRC_FILES # uncomment the following line if you want to use Qt resources resources/org_mitk_gui_qt_ext.qrc resources/org_mitk_icons.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.ext/resources/Editor.png b/Plugins/org.mitk.gui.qt.ext/resources/Editor.png new file mode 100644 index 0000000000..8043b0e626 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.ext/resources/Editor.png differ diff --git a/Plugins/org.mitk.gui.qt.ext/resources/org_mitk_gui_qt_ext.qrc b/Plugins/org.mitk.gui.qt.ext/resources/org_mitk_gui_qt_ext.qrc index 007377ac7e..ad25eea67c 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/org_mitk_gui_qt_ext.qrc +++ b/Plugins/org.mitk.gui.qt.ext/resources/org_mitk_gui_qt_ext.qrc @@ -1,9 +1,11 @@ dicom.svg image_navigator.svg + Editor.png + Slider.png index.html xnat-icon.png view-manager.svg diff --git a/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp b/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp index 0156dfc168..206a6e9874 100644 --- a/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp +++ b/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp @@ -1,1407 +1,1426 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkExtWorkbenchWindowAdvisor.h" #include "QmitkExtActionBarAdvisor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include #include #include #include #include #include #include #include #include // UGLYYY #include "internal/QmitkExtWorkbenchWindowAdvisorHack.h" #include "internal/QmitkCommonExtPlugin.h" #include "mitkUndoController.h" #include "mitkVerboseLimitedLinearUndo.h" #include #include #include #include #include #include -QmitkExtWorkbenchWindowAdvisorHack - * QmitkExtWorkbenchWindowAdvisorHack::undohack = +QmitkExtWorkbenchWindowAdvisorHack* QmitkExtWorkbenchWindowAdvisorHack::undohack = new QmitkExtWorkbenchWindowAdvisorHack(); QString QmitkExtWorkbenchWindowAdvisor::QT_SETTINGS_FILENAME = "QtSettings.ini"; static bool USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS = false; class PartListenerForTitle: public berry::IPartListener { public: - PartListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa) : - windowAdvisor(wa) + PartListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa) + : windowAdvisor(wa) { } Events::Types GetPartEventTypes() const override { return Events::ACTIVATED | Events::BROUGHT_TO_TOP | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartActivated(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref.Cast ()) { windowAdvisor->UpdateTitle(false); } } void PartBroughtToTop(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref.Cast ()) { windowAdvisor->UpdateTitle(false); } } void PartClosed(const berry::IWorkbenchPartReference::Pointer& /*ref*/) override { windowAdvisor->UpdateTitle(false); } void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override { if (!windowAdvisor->lastActiveEditor.Expired() && ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock()) { windowAdvisor->UpdateTitle(true); } } void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override { if (!windowAdvisor->lastActiveEditor.Expired() && ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock()) { windowAdvisor->UpdateTitle(false); } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; }; class PartListenerForViewNavigator: public berry::IPartListener { public: - PartListenerForViewNavigator(QAction* act) : - viewNavigatorAction(act) + PartListenerForViewNavigator(QAction* act) + : viewNavigatorAction(act) { } Events::Types GetPartEventTypes() const override { return Events::OPENED | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartOpened(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigatorview") { viewNavigatorAction->setChecked(true); } } void PartClosed(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigatorview") { viewNavigatorAction->setChecked(false); } } void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigatorview") { viewNavigatorAction->setChecked(true); } } void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigatorview") { viewNavigatorAction->setChecked(false); } } private: QAction* viewNavigatorAction; }; class PartListenerForImageNavigator: public berry::IPartListener { public: - PartListenerForImageNavigator(QAction* act) : - imageNavigatorAction(act) + PartListenerForImageNavigator(QAction* act) + : imageNavigatorAction(act) { } Events::Types GetPartEventTypes() const override { return Events::OPENED | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartOpened(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(true); } } void PartClosed(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(false); } } void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(true); } } void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(false); } } private: QAction* imageNavigatorAction; }; class PerspectiveListenerForTitle: public berry::IPerspectiveListener { public: - PerspectiveListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa) : - windowAdvisor(wa), perspectivesClosed(false) + PerspectiveListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa) + : windowAdvisor(wa) + , perspectivesClosed(false) { } Events::Types GetPerspectiveEventTypes() const override { if (USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS) { return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED; } else { return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED | Events::CLOSED | Events::OPENED; } } void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveSavedAs(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*oldPerspective*/, const berry::IPerspectiveDescriptor::Pointer& /*newPerspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveOpened(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { if (perspectivesClosed) { QListIterator i(windowAdvisor->viewActions); while (i.hasNext()) { i.next()->setEnabled(true); } //GetViewRegistry()->Find("org.mitk.views.imagenavigator"); if(windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicomeditor")) { windowAdvisor->openDicomEditorAction->setEnabled(true); } + if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget")) + { + windowAdvisor->openStdMultiWidgetEditorAction->setEnabled(true); + } + if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.custommultiwidget")) + { + windowAdvisor->openCustomMultiWidgetEditorAction->setEnabled(true); + } + windowAdvisor->fileSaveProjectAction->setEnabled(true); windowAdvisor->closeProjectAction->setEnabled(true); windowAdvisor->undoAction->setEnabled(true); windowAdvisor->redoAction->setEnabled(true); windowAdvisor->imageNavigatorAction->setEnabled(true); windowAdvisor->viewNavigatorAction->setEnabled(true); windowAdvisor->resetPerspAction->setEnabled(true); if( windowAdvisor->GetShowClosePerspectiveMenuItem() ) { windowAdvisor->closePerspAction->setEnabled(true); } } perspectivesClosed = false; } void PerspectiveClosed(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { berry::IWorkbenchWindow::Pointer wnd = windowAdvisor->GetWindowConfigurer()->GetWindow(); bool allClosed = true; if (wnd->GetActivePage()) { QList perspectives(wnd->GetActivePage()->GetOpenPerspectives()); allClosed = perspectives.empty(); } if (allClosed) { perspectivesClosed = true; QListIterator i(windowAdvisor->viewActions); while (i.hasNext()) { i.next()->setEnabled(false); } if(windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicomeditor")) { windowAdvisor->openDicomEditorAction->setEnabled(false); } + if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget")) + { + windowAdvisor->openStdMultiWidgetEditorAction->setEnabled(false); + } + if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.custommultiwidget")) + { + windowAdvisor->openCustomMultiWidgetEditorAction->setEnabled(false); + } + windowAdvisor->fileSaveProjectAction->setEnabled(false); windowAdvisor->closeProjectAction->setEnabled(false); windowAdvisor->undoAction->setEnabled(false); windowAdvisor->redoAction->setEnabled(false); windowAdvisor->imageNavigatorAction->setEnabled(false); windowAdvisor->viewNavigatorAction->setEnabled(false); windowAdvisor->resetPerspAction->setEnabled(false); if( windowAdvisor->GetShowClosePerspectiveMenuItem() ) { windowAdvisor->closePerspAction->setEnabled(false); } } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; bool perspectivesClosed; }; class PerspectiveListenerForMenu: public berry::IPerspectiveListener { public: - PerspectiveListenerForMenu(QmitkExtWorkbenchWindowAdvisor* wa) : - windowAdvisor(wa) + PerspectiveListenerForMenu(QmitkExtWorkbenchWindowAdvisor* wa) + : windowAdvisor(wa) { } Events::Types GetPerspectiveEventTypes() const override { return Events::ACTIVATED | Events::DEACTIVATED; } void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& perspective) override { QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()]; if (action) { action->setChecked(true); } } void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& perspective) override { QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()]; if (action) { action->setChecked(false); } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; }; QmitkExtWorkbenchWindowAdvisor::QmitkExtWorkbenchWindowAdvisor(berry::WorkbenchAdvisor* wbAdvisor, - berry::IWorkbenchWindowConfigurer::Pointer configurer) : -berry::WorkbenchWindowAdvisor(configurer), - lastInput(nullptr), - wbAdvisor(wbAdvisor), - showViewToolbar(true), - showPerspectiveToolbar(false), - showVersionInfo(true), - showMitkVersionInfo(true), - showViewMenuItem(true), - showNewWindowMenuItem(false), - showClosePerspectiveMenuItem(true), - viewNavigatorFound(false), - showMemoryIndicator(true), - dropTargetListener(new QmitkDefaultDropTargetListener) + berry::IWorkbenchWindowConfigurer::Pointer configurer) + : berry::WorkbenchWindowAdvisor(configurer) + , lastInput(nullptr) + , wbAdvisor(wbAdvisor) + , showViewToolbar(true) + , showPerspectiveToolbar(false) + , showVersionInfo(true) + , showMitkVersionInfo(true) + , showViewMenuItem(true) + , showNewWindowMenuItem(false) + , showClosePerspectiveMenuItem(true) + , viewNavigatorFound(false) + , showMemoryIndicator(true) + , dropTargetListener(new QmitkDefaultDropTargetListener) { productName = QCoreApplication::applicationName(); viewExcludeList.push_back("org.mitk.views.viewnavigatorview"); } QmitkExtWorkbenchWindowAdvisor::~QmitkExtWorkbenchWindowAdvisor() { } -berry::ActionBarAdvisor::Pointer QmitkExtWorkbenchWindowAdvisor::CreateActionBarAdvisor( - berry::IActionBarConfigurer::Pointer configurer) +berry::ActionBarAdvisor::Pointer QmitkExtWorkbenchWindowAdvisor::CreateActionBarAdvisor(berry::IActionBarConfigurer::Pointer configurer) { if (USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS) { - berry::ActionBarAdvisor::Pointer actionBarAdvisor( - new QmitkExtActionBarAdvisor(configurer)); + berry::ActionBarAdvisor::Pointer actionBarAdvisor(new QmitkExtActionBarAdvisor(configurer)); return actionBarAdvisor; } else { return berry::WorkbenchWindowAdvisor::CreateActionBarAdvisor(configurer); } } QWidget* QmitkExtWorkbenchWindowAdvisor::CreateEmptyWindowContents(QWidget* parent) { QWidget* parentWidget = static_cast(parent); auto label = new QLabel(parentWidget); label->setText("No perspectives are open. Open a perspective in the Window->Open Perspective menu."); label->setContentsMargins(10,10,10,10); label->setAlignment(Qt::AlignTop); label->setEnabled(false); parentWidget->layout()->addWidget(label); return label; } void QmitkExtWorkbenchWindowAdvisor::ShowClosePerspectiveMenuItem(bool show) { showClosePerspectiveMenuItem = show; } bool QmitkExtWorkbenchWindowAdvisor::GetShowClosePerspectiveMenuItem() { return showClosePerspectiveMenuItem; } void QmitkExtWorkbenchWindowAdvisor::ShowMemoryIndicator(bool show) { showMemoryIndicator = show; } bool QmitkExtWorkbenchWindowAdvisor::GetShowMemoryIndicator() { return showMemoryIndicator; } void QmitkExtWorkbenchWindowAdvisor::ShowNewWindowMenuItem(bool show) { showNewWindowMenuItem = show; } void QmitkExtWorkbenchWindowAdvisor::ShowViewToolbar(bool show) { showViewToolbar = show; } void QmitkExtWorkbenchWindowAdvisor::ShowViewMenuItem(bool show) { showViewMenuItem = show; } void QmitkExtWorkbenchWindowAdvisor::ShowPerspectiveToolbar(bool show) { showPerspectiveToolbar = show; } void QmitkExtWorkbenchWindowAdvisor::ShowVersionInfo(bool show) { showVersionInfo = show; } void QmitkExtWorkbenchWindowAdvisor::ShowMitkVersionInfo(bool show) { showMitkVersionInfo = show; } void QmitkExtWorkbenchWindowAdvisor::SetProductName(const QString& product) { productName = product; } void QmitkExtWorkbenchWindowAdvisor::SetWindowIcon(const QString& wndIcon) { windowIcon = wndIcon; } void QmitkExtWorkbenchWindowAdvisor::PostWindowCreate() { // very bad hack... - berry::IWorkbenchWindow::Pointer window = - this->GetWindowConfigurer()->GetWindow(); - QMainWindow* mainWindow = - qobject_cast (window->GetShell()->GetControl()); + berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow(); + QMainWindow* mainWindow = qobject_cast (window->GetShell()->GetControl()); if (!windowIcon.isEmpty()) { mainWindow->setWindowIcon(QIcon(windowIcon)); } mainWindow->setContextMenuPolicy(Qt::PreventContextMenu); // Load icon theme QIcon::setThemeSearchPaths(QStringList() << QStringLiteral(":/org_mitk_icons/icons/")); QIcon::setThemeName(QStringLiteral("awesome")); // ==== Application menu ============================ QMenuBar* menuBar = mainWindow->menuBar(); menuBar->setContextMenuPolicy(Qt::PreventContextMenu); #ifdef __APPLE__ menuBar->setNativeMenuBar(true); #else menuBar->setNativeMenuBar(false); #endif auto basePath = QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/"); auto fileOpenAction = new QmitkFileOpenAction(berry::QtStyleManager::ThemeIcon(basePath + "document-open.svg"), window); fileOpenAction->setShortcut(QKeySequence::Open); auto fileSaveAction = new QmitkFileSaveAction(berry::QtStyleManager::ThemeIcon(basePath + "document-save.svg"), window); fileSaveAction->setShortcut(QKeySequence::Save); fileSaveProjectAction = new QmitkExtFileSaveProjectAction(window); fileSaveProjectAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "document-save.svg")); closeProjectAction = new QmitkCloseProjectAction(window); closeProjectAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "edit-delete.svg")); auto perspGroup = new QActionGroup(menuBar); std::map VDMap; // sort elements (converting vector to map...) QList::const_iterator iter; berry::IViewRegistry* viewRegistry = berry::PlatformUI::GetWorkbench()->GetViewRegistry(); const QList viewDescriptors = viewRegistry->GetViews(); bool skip = false; for (iter = viewDescriptors.begin(); iter != viewDescriptors.end(); ++iter) { // if viewExcludeList is set, it contains the id-strings of view, which // should not appear as an menu-entry in the menu if (viewExcludeList.size() > 0) { for (int i=0; iGetId()) { skip = true; break; } } if (skip) { skip = false; continue; } } if ((*iter)->GetId() == "org.blueberry.ui.internal.introview") continue; if ((*iter)->GetId() == "org.mitk.views.imagenavigator") continue; if ((*iter)->GetId() == "org.mitk.views.viewnavigatorview") continue; - std::pair p( - (*iter)->GetLabel(), (*iter)); + std::pair p((*iter)->GetLabel(), (*iter)); VDMap.insert(p); } - std::map::const_iterator - MapIter; + std::map::const_iterator MapIter; for (MapIter = VDMap.begin(); MapIter != VDMap.end(); ++MapIter) { - berry::QtShowViewAction* viewAction = new berry::QtShowViewAction(window, - (*MapIter).second); + berry::QtShowViewAction* viewAction = new berry::QtShowViewAction(window, (*MapIter).second); viewActions.push_back(viewAction); } if (!USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS) { QMenu* fileMenu = menuBar->addMenu("&File"); fileMenu->setObjectName("FileMenu"); fileMenu->addAction(fileOpenAction); fileMenu->addAction(fileSaveAction); fileMenu->addAction(fileSaveProjectAction); fileMenu->addAction(closeProjectAction); fileMenu->addSeparator(); QAction* fileExitAction = new QmitkFileExitAction(window); fileExitAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "system-log-out.svg")); fileExitAction->setShortcut(QKeySequence::Quit); fileExitAction->setObjectName("QmitkFileExitAction"); fileMenu->addAction(fileExitAction); // another bad hack to get an edit/undo menu... QMenu* editMenu = menuBar->addMenu("&Edit"); undoAction = editMenu->addAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-undo.svg"), "&Undo", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onUndo()), QKeySequence("CTRL+Z")); undoAction->setToolTip("Undo the last action (not supported by all modules)"); redoAction = editMenu->addAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-redo.svg"), "&Redo", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onRedo()), QKeySequence("CTRL+Y")); redoAction->setToolTip("execute the last action that was undone again (not supported by all modules)"); // ==== Window Menu ========================== QMenu* windowMenu = menuBar->addMenu("Window"); if (showNewWindowMenuItem) { windowMenu->addAction("&New Window", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onNewWindow())); windowMenu->addSeparator(); } QMenu* perspMenu = windowMenu->addMenu("&Open Perspective"); QMenu* viewMenu = nullptr; if (showViewMenuItem) { viewMenu = windowMenu->addMenu("Show &View"); viewMenu->setObjectName("Show View"); } windowMenu->addSeparator(); resetPerspAction = windowMenu->addAction("&Reset Perspective", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onResetPerspective())); if(showClosePerspectiveMenuItem) closePerspAction = windowMenu->addAction("&Close Perspective", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onClosePerspective())); windowMenu->addSeparator(); windowMenu->addAction("&Preferences...", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onEditPreferences()), QKeySequence("CTRL+P")); // fill perspective menu berry::IPerspectiveRegistry* perspRegistry = window->GetWorkbench()->GetPerspectiveRegistry(); QList perspectives( perspRegistry->GetPerspectives()); skip = false; for (QList::iterator perspIt = perspectives.begin(); perspIt != perspectives.end(); ++perspIt) { // if perspectiveExcludeList is set, it contains the id-strings of perspectives, which // should not appear as an menu-entry in the perspective menu if (perspectiveExcludeList.size() > 0) { for (int i=0; iGetId()) { skip = true; break; } } if (skip) { skip = false; continue; } } - QAction* perspAction = new berry::QtOpenPerspectiveAction(window, - *perspIt, perspGroup); + QAction* perspAction = new berry::QtOpenPerspectiveAction(window, *perspIt, perspGroup); mapPerspIdToAction.insert((*perspIt)->GetId(), perspAction); } perspMenu->addActions(perspGroup->actions()); if (showViewMenuItem) { for (auto viewAction : viewActions) { viewMenu->addAction(viewAction); } } // ===== Help menu ==================================== QMenu* helpMenu = menuBar->addMenu("&Help"); helpMenu->addAction("&Welcome",this, SLOT(onIntro())); helpMenu->addAction("&Open Help Perspective", this, SLOT(onHelpOpenHelpPerspective())); helpMenu->addAction("&Context Help",this, SLOT(onHelp()), QKeySequence("F1")); helpMenu->addAction("&About",this, SLOT(onAbout())); // ===================================================== } else { undoAction = new QmitkUndoAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-undo.svg"), nullptr); undoAction->setShortcut(QKeySequence::Undo); redoAction = new QmitkRedoAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-redo.svg"), nullptr); redoAction->setShortcut(QKeySequence::Redo); } // toolbar for showing file open, undo, redo and other main actions auto mainActionsToolBar = new QToolBar; mainActionsToolBar->setObjectName("mainActionsToolBar"); mainActionsToolBar->setContextMenuPolicy(Qt::PreventContextMenu); #ifdef __APPLE__ mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextUnderIcon ); #else mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextBesideIcon ); #endif basePath = QStringLiteral(":/org.mitk.gui.qt.ext/"); imageNavigatorAction = new QAction(berry::QtStyleManager::ThemeIcon(basePath + "image_navigator.svg"), "&Image Navigator", nullptr); bool imageNavigatorViewFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.imagenavigator"); - if(this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicomeditor")) + if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicomeditor")) { openDicomEditorAction = new QmitkOpenDicomEditorAction(berry::QtStyleManager::ThemeIcon(basePath + "dicom.svg"), window); } + if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget")) + { + openStdMultiWidgetEditorAction = new QmitkOpenStdMultiWidgetEditorAction(QIcon(":/org.mitk.gui.qt.ext/Editor.png"), window); + } + if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.custommultiwidget")) + { + openCustomMultiWidgetEditorAction = new QmitkOpenCustomMultiWidgetEditorAction(QIcon(":/org.mitk.gui.qt.ext/Editor.png"), window); + } if (imageNavigatorViewFound) { QObject::connect(imageNavigatorAction, SIGNAL(triggered(bool)), QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onImageNavigator())); imageNavigatorAction->setCheckable(true); // add part listener for image navigator imageNavigatorPartListener.reset(new PartListenerForImageNavigator(imageNavigatorAction)); window->GetPartService()->AddPartListener(imageNavigatorPartListener.data()); - berry::IViewPart::Pointer imageNavigatorView = - window->GetActivePage()->FindView("org.mitk.views.imagenavigator"); + berry::IViewPart::Pointer imageNavigatorView = window->GetActivePage()->FindView("org.mitk.views.imagenavigator"); imageNavigatorAction->setChecked(false); if (imageNavigatorView) { bool isImageNavigatorVisible = window->GetActivePage()->IsPartVisible(imageNavigatorView); if (isImageNavigatorVisible) imageNavigatorAction->setChecked(true); } imageNavigatorAction->setToolTip("Toggle image navigator for navigating through image"); } viewNavigatorAction = new QAction(berry::QtStyleManager::ThemeIcon(QStringLiteral(":/org.mitk.gui.qt.ext/view-manager.svg")),"&View Navigator", nullptr); viewNavigatorFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.viewnavigatorview"); if (viewNavigatorFound) { QObject::connect(viewNavigatorAction, SIGNAL(triggered(bool)), QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onViewNavigator())); viewNavigatorAction->setCheckable(true); // add part listener for view navigator viewNavigatorPartListener.reset(new PartListenerForViewNavigator(viewNavigatorAction)); window->GetPartService()->AddPartListener(viewNavigatorPartListener.data()); - berry::IViewPart::Pointer viewnavigatorview = - window->GetActivePage()->FindView("org.mitk.views.viewnavigatorview"); + berry::IViewPart::Pointer viewnavigatorview = window->GetActivePage()->FindView("org.mitk.views.viewnavigatorview"); viewNavigatorAction->setChecked(false); if (viewnavigatorview) { bool isViewNavigatorVisible = window->GetActivePage()->IsPartVisible(viewnavigatorview); if (isViewNavigatorVisible) viewNavigatorAction->setChecked(true); } viewNavigatorAction->setToolTip("Toggle View Navigator"); } mainActionsToolBar->addAction(fileOpenAction); mainActionsToolBar->addAction(fileSaveProjectAction); mainActionsToolBar->addAction(closeProjectAction); mainActionsToolBar->addAction(undoAction); mainActionsToolBar->addAction(redoAction); if(this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicomeditor")) { mainActionsToolBar->addAction(openDicomEditorAction); } + if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget")) + { + mainActionsToolBar->addAction(openStdMultiWidgetEditorAction); + } + if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.custommultiwidget")) + { + mainActionsToolBar->addAction(openCustomMultiWidgetEditorAction); + } + if (imageNavigatorViewFound) { mainActionsToolBar->addAction(imageNavigatorAction); } if (viewNavigatorFound) { mainActionsToolBar->addAction(viewNavigatorAction); } mainWindow->addToolBar(mainActionsToolBar); // ==== Perspective Toolbar ================================== auto qPerspectiveToolbar = new QToolBar; qPerspectiveToolbar->setObjectName("perspectiveToolBar"); if (showPerspectiveToolbar) { qPerspectiveToolbar->addActions(perspGroup->actions()); mainWindow->addToolBar(qPerspectiveToolbar); } else delete qPerspectiveToolbar; if (showViewToolbar) { auto prefService = berry::WorkbenchPlugin::GetDefault()->GetPreferencesService(); berry::IPreferences::Pointer stylePrefs = prefService->GetSystemPreferences()->Node(berry::QtPreferences::QT_STYLES_NODE); bool showCategoryNames = stylePrefs->GetBool(berry::QtPreferences::QT_SHOW_TOOLBAR_CATEGORY_NAMES, true); // Order view descriptors by category QMultiMap categoryViewDescriptorMap; for (auto labelViewDescriptorPair : VDMap) { auto viewDescriptor = labelViewDescriptorPair.second; auto category = !viewDescriptor->GetCategoryPath().isEmpty() ? viewDescriptor->GetCategoryPath().back() : QString(); categoryViewDescriptorMap.insert(category, viewDescriptor); } // Create a separate toolbar for each category for (auto category : categoryViewDescriptorMap.uniqueKeys()) { auto viewDescriptorsInCurrentCategory = categoryViewDescriptorMap.values(category); if (!viewDescriptorsInCurrentCategory.isEmpty()) { auto toolbar = new QToolBar; toolbar->setObjectName(category + " View Toolbar"); mainWindow->addToolBar(toolbar); if (showCategoryNames && !category.isEmpty()) { auto categoryButton = new QToolButton; categoryButton->setToolButtonStyle(Qt::ToolButtonTextOnly); categoryButton->setText(category); categoryButton->setStyleSheet("background: transparent; margin: 0; padding: 0;"); toolbar->addWidget(categoryButton); connect(categoryButton, &QToolButton::clicked, [toolbar]() { for (QWidget* widget : toolbar->findChildren()) { if (QStringLiteral("qt_toolbar_ext_button") == widget->objectName() && widget->isVisible()) { QMouseEvent pressEvent(QEvent::MouseButtonPress, QPointF(0.0f, 0.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPointF(0.0f, 0.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QApplication::sendEvent(widget, &pressEvent); QApplication::sendEvent(widget, &releaseEvent); } } }); } for (auto viewDescriptor : viewDescriptorsInCurrentCategory) { auto viewAction = new berry::QtShowViewAction(window, viewDescriptor); toolbar->addAction(viewAction); } } } } QSettings settings(GetQSettingsFile(), QSettings::IniFormat); mainWindow->restoreState(settings.value("ToolbarPosition").toByteArray()); auto qStatusBar = new QStatusBar(); //creating a QmitkStatusBar for Output on the QStatusBar and connecting it with the MainStatusBar auto statusBar = new QmitkStatusBar(qStatusBar); //disabling the SizeGrip in the lower right corner statusBar->SetSizeGripEnabled(false); auto progBar = new QmitkProgressBar(); qStatusBar->addPermanentWidget(progBar, 0); progBar->hide(); // progBar->AddStepsToDo(2); // progBar->Progress(1); mainWindow->setStatusBar(qStatusBar); if (showMemoryIndicator) { auto memoryIndicator = new QmitkMemoryUsageIndicatorView(); qStatusBar->addPermanentWidget(memoryIndicator, 0); } } void QmitkExtWorkbenchWindowAdvisor::PreWindowOpen() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); // show the shortcut bar and progress indicator, which are hidden by // default //configurer->SetShowPerspectiveBar(true); //configurer->SetShowFastViewBars(true); //configurer->SetShowProgressIndicator(true); // // add the drag and drop support for the editor area // configurer.addEditorAreaTransfer(EditorInputTransfer.getInstance()); // configurer.addEditorAreaTransfer(ResourceTransfer.getInstance()); // configurer.addEditorAreaTransfer(FileTransfer.getInstance()); // configurer.addEditorAreaTransfer(MarkerTransfer.getInstance()); // configurer.configureEditorAreaDropListener(new EditorAreaDropAdapter( // configurer.getWindow())); this->HookTitleUpdateListeners(configurer); menuPerspectiveListener.reset(new PerspectiveListenerForMenu(this)); configurer->GetWindow()->AddPerspectiveListener(menuPerspectiveListener.data()); configurer->AddEditorAreaTransfer(QStringList("text/uri-list")); configurer->ConfigureEditorAreaDropListener(dropTargetListener.data()); } void QmitkExtWorkbenchWindowAdvisor::PostWindowOpen() { berry::WorkbenchWindowAdvisor::PostWindowOpen(); // Force Rendering Window Creation on startup. berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); ctkPluginContext* context = QmitkCommonExtPlugin::getContext(); ctkServiceReference serviceRef = context->getServiceReference(); if (serviceRef) { mitk::IDataStorageService *dsService = context->getService(serviceRef); if (dsService) { mitk::IDataStorageReference::Pointer dsRef = dsService->GetDataStorage(); mitk::DataStorageEditorInput::Pointer dsInput(new mitk::DataStorageEditorInput(dsRef)); mitk::WorkbenchUtil::OpenEditor(configurer->GetWindow()->GetActivePage(),dsInput); } } } void QmitkExtWorkbenchWindowAdvisor::onIntro() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onIntro(); } void QmitkExtWorkbenchWindowAdvisor::onHelp() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onHelp(); } void QmitkExtWorkbenchWindowAdvisor::onHelpOpenHelpPerspective() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onHelpOpenHelpPerspective(); } void QmitkExtWorkbenchWindowAdvisor::onAbout() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onAbout(); } //-------------------------------------------------------------------------------- // Ugly hack from here on. Feel free to delete when command framework // and undo buttons are done. //-------------------------------------------------------------------------------- -QmitkExtWorkbenchWindowAdvisorHack::QmitkExtWorkbenchWindowAdvisorHack() : QObject() +QmitkExtWorkbenchWindowAdvisorHack::QmitkExtWorkbenchWindowAdvisorHack() + : QObject() { } QmitkExtWorkbenchWindowAdvisorHack::~QmitkExtWorkbenchWindowAdvisorHack() { } void QmitkExtWorkbenchWindowAdvisorHack::onUndo() { mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel(); if (model) { if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast( model )) { - mitk::VerboseLimitedLinearUndo::StackDescription descriptions = - verboseundo->GetUndoDescriptions(); + mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetUndoDescriptions(); if (descriptions.size() >= 1) { MITK_INFO << "Undo " << descriptions.front().second; } } model->Undo(); } else { MITK_ERROR << "No undo model instantiated"; } } void QmitkExtWorkbenchWindowAdvisorHack::onRedo() { mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel(); if (model) { if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast( model )) { - mitk::VerboseLimitedLinearUndo::StackDescription descriptions = - verboseundo->GetRedoDescriptions(); + mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetRedoDescriptions(); if (descriptions.size() >= 1) { MITK_INFO << "Redo " << descriptions.front().second; } } model->Redo(); } else { MITK_ERROR << "No undo model instantiated"; } } // safe calls to the complete chain // berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->FindView("org.mitk.views.imagenavigator"); // to cover for all possible cases of closed pages etc. static void SafeHandleNavigatorView(QString view_query_name) { berry::IWorkbench* wbench = berry::PlatformUI::GetWorkbench(); if( wbench == nullptr ) return; berry::IWorkbenchWindow::Pointer wbench_window = wbench->GetActiveWorkbenchWindow(); if( wbench_window.IsNull() ) return; berry::IWorkbenchPage::Pointer wbench_page = wbench_window->GetActivePage(); if( wbench_page.IsNull() ) return; auto wbench_view = wbench_page->FindView( view_query_name ); if( wbench_view.IsNotNull() ) { bool isViewVisible = wbench_page->IsPartVisible( wbench_view ); if( isViewVisible ) { wbench_page->HideView( wbench_view ); return; } } wbench_page->ShowView( view_query_name ); } void QmitkExtWorkbenchWindowAdvisorHack::onImageNavigator() { // show/hide ImageNavigatorView SafeHandleNavigatorView("org.mitk.views.imagenavigator"); } void QmitkExtWorkbenchWindowAdvisorHack::onViewNavigator() { // show/hide viewnavigatorView SafeHandleNavigatorView("org.mitk.views.viewnavigatorview"); } void QmitkExtWorkbenchWindowAdvisorHack::onEditPreferences() { QmitkPreferencesDialog _PreferencesDialog(QApplication::activeWindow()); _PreferencesDialog.exec(); } void QmitkExtWorkbenchWindowAdvisorHack::onQuit() { berry::PlatformUI::GetWorkbench()->Close(); } void QmitkExtWorkbenchWindowAdvisorHack::onResetPerspective() { berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ResetPerspective(); } void QmitkExtWorkbenchWindowAdvisorHack::onClosePerspective() { - berry::IWorkbenchPage::Pointer - page = + berry::IWorkbenchPage::Pointer page = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage(); page->ClosePerspective(page->GetPerspective(), true, true); } void QmitkExtWorkbenchWindowAdvisorHack::onNewWindow() { berry::PlatformUI::GetWorkbench()->OpenWorkbenchWindow(nullptr); } void QmitkExtWorkbenchWindowAdvisorHack::onIntro() { bool hasIntro = berry::PlatformUI::GetWorkbench()->GetIntroManager()->HasIntro(); if (!hasIntro) { QRegExp reg("(.*)(\\n)*"); QRegExp reg2("(\\n)*(.*)"); QFile file(":/org.mitk.gui.qt.ext/index.html"); file.open(QIODevice::ReadOnly | QIODevice::Text); //text file only for reading QString text = QString(file.readAll()); file.close(); QString title = text; title.replace(reg, ""); title.replace(reg2, ""); std::cout << title.toStdString() << std::endl; QMessageBox::information(nullptr, title, text, "Close"); } else { berry::PlatformUI::GetWorkbench()->GetIntroManager()->ShowIntro( berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow(), false); } } void QmitkExtWorkbenchWindowAdvisorHack::onHelp() { ctkPluginContext* context = QmitkCommonExtPlugin::getContext(); if (context == nullptr) { MITK_WARN << "Plugin context not set, unable to open context help"; return; } // Check if the org.blueberry.ui.qt.help plug-in is installed and started QList > plugins = context->getPlugins(); foreach(QSharedPointer p, plugins) { if (p->getSymbolicName() == "org.blueberry.ui.qt.help") { if (p->getState() != ctkPlugin::ACTIVE) { // try to activate the plug-in explicitly try { p->start(ctkPlugin::START_TRANSIENT); } catch (const ctkPluginException& pe) { MITK_ERROR << "Activating org.blueberry.ui.qt.help failed: " << pe.what(); return; } } } } ctkServiceReference eventAdminRef = context->getServiceReference(); ctkEventAdmin* eventAdmin = nullptr; if (eventAdminRef) { eventAdmin = context->getService(eventAdminRef); } if (eventAdmin == nullptr) { MITK_WARN << "ctkEventAdmin service not found. Unable to open context help"; } else { ctkEvent ev("org/blueberry/ui/help/CONTEXTHELP_REQUESTED"); eventAdmin->postEvent(ev); } } void QmitkExtWorkbenchWindowAdvisorHack::onHelpOpenHelpPerspective() { berry::PlatformUI::GetWorkbench()->ShowPerspective("org.blueberry.perspectives.help", berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()); } void QmitkExtWorkbenchWindowAdvisorHack::onAbout() { - auto aboutDialog = new QmitkAboutDialog(QApplication::activeWindow(),nullptr); + auto aboutDialog = new QmitkAboutDialog(QApplication::activeWindow(),nullptr); aboutDialog->open(); } -void QmitkExtWorkbenchWindowAdvisor::HookTitleUpdateListeners( - berry::IWorkbenchWindowConfigurer::Pointer configurer) +void QmitkExtWorkbenchWindowAdvisor::HookTitleUpdateListeners(berry::IWorkbenchWindowConfigurer::Pointer configurer) { // hook up the listeners to update the window title titlePartListener.reset(new PartListenerForTitle(this)); titlePerspectiveListener.reset(new PerspectiveListenerForTitle(this)); editorPropertyListener.reset(new berry::PropertyChangeIntAdapter< QmitkExtWorkbenchWindowAdvisor>(this, &QmitkExtWorkbenchWindowAdvisor::PropertyChange)); // configurer.getWindow().addPageListener(new IPageListener() { // public void pageActivated(IWorkbenchPage page) { // updateTitle(false); // } // // public void pageClosed(IWorkbenchPage page) { // updateTitle(false); // } // // public void pageOpened(IWorkbenchPage page) { // // do nothing // } // }); configurer->GetWindow()->AddPerspectiveListener(titlePerspectiveListener.data()); configurer->GetWindow()->GetPartService()->AddPartListener(titlePartListener.data()); } QString QmitkExtWorkbenchWindowAdvisor::ComputeTitle() { - berry::IWorkbenchWindowConfigurer::Pointer configurer = - GetWindowConfigurer(); - berry::IWorkbenchPage::Pointer currentPage = - configurer->GetWindow()->GetActivePage(); + berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); + berry::IWorkbenchPage::Pointer currentPage = configurer->GetWindow()->GetActivePage(); berry::IEditorPart::Pointer activeEditor; if (currentPage) { activeEditor = lastActiveEditor.Lock(); } QString title; berry::IProduct::Pointer product = berry::Platform::GetProduct(); if (product.IsNotNull()) { title = product->GetName(); } if (title.isEmpty()) { // instead of the product name, we use a custom variable for now title = productName; } if(showMitkVersionInfo) { title += QString(" ") + MITK_VERSION_STRING; } if (showVersionInfo) { // add version informatioin QString versions = QString(" (ITK %1.%2.%3 VTK %4.%5.%6 Qt %7 MITK %8)") .arg(ITK_VERSION_MAJOR).arg(ITK_VERSION_MINOR).arg(ITK_VERSION_PATCH) .arg(VTK_MAJOR_VERSION).arg(VTK_MINOR_VERSION).arg(VTK_BUILD_VERSION) .arg(QT_VERSION_STR) .arg(MITK_VERSION_STRING); title += versions; } if (currentPage) { if (activeEditor) { lastEditorTitle = activeEditor->GetTitleToolTip(); if (!lastEditorTitle.isEmpty()) title = lastEditorTitle + " - " + title; } - berry::IPerspectiveDescriptor::Pointer persp = - currentPage->GetPerspective(); + berry::IPerspectiveDescriptor::Pointer persp = currentPage->GetPerspective(); QString label = ""; if (persp) { label = persp->GetLabel(); } berry::IAdaptable* input = currentPage->GetInput(); if (input && input != wbAdvisor->GetDefaultPageInput()) { label = currentPage->GetLabel(); } if (!label.isEmpty()) { title = label + " - " + title; } } title += " (Not for use in diagnosis or treatment of patients)"; return title; } void QmitkExtWorkbenchWindowAdvisor::RecomputeTitle() { - berry::IWorkbenchWindowConfigurer::Pointer configurer = - GetWindowConfigurer(); + berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); QString oldTitle = configurer->GetTitle(); QString newTitle = ComputeTitle(); if (newTitle != oldTitle) { configurer->SetTitle(newTitle); } } void QmitkExtWorkbenchWindowAdvisor::UpdateTitle(bool editorHidden) { - berry::IWorkbenchWindowConfigurer::Pointer configurer = - GetWindowConfigurer(); + berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); berry::IWorkbenchWindow::Pointer window = configurer->GetWindow(); berry::IEditorPart::Pointer activeEditor; berry::IWorkbenchPage::Pointer currentPage = window->GetActivePage(); berry::IPerspectiveDescriptor::Pointer persp; berry::IAdaptable* input = nullptr; if (currentPage) { activeEditor = currentPage->GetActiveEditor(); persp = currentPage->GetPerspective(); input = currentPage->GetInput(); } if (editorHidden) { activeEditor = nullptr; } // Nothing to do if the editor hasn't changed if (activeEditor == lastActiveEditor.Lock() && currentPage == lastActivePage.Lock() && persp == lastPerspective.Lock() && input == lastInput) { return; } if (!lastActiveEditor.Expired()) { lastActiveEditor.Lock()->RemovePropertyListener(editorPropertyListener.data()); } lastActiveEditor = activeEditor; lastActivePage = currentPage; lastPerspective = persp; lastInput = input; if (activeEditor) { activeEditor->AddPropertyListener(editorPropertyListener.data()); } RecomputeTitle(); } void QmitkExtWorkbenchWindowAdvisor::PropertyChange(const berry::Object::Pointer& /*source*/, int propId) { if (propId == berry::IWorkbenchPartConstants::PROP_TITLE) { if (!lastActiveEditor.Expired()) { QString newTitle = lastActiveEditor.Lock()->GetPartName(); if (lastEditorTitle != newTitle) { RecomputeTitle(); } } } } void QmitkExtWorkbenchWindowAdvisor::SetPerspectiveExcludeList(const QList& v) { this->perspectiveExcludeList = v; } QList QmitkExtWorkbenchWindowAdvisor::GetPerspectiveExcludeList() { return this->perspectiveExcludeList; } void QmitkExtWorkbenchWindowAdvisor::SetViewExcludeList(const QList& v) { this->viewExcludeList = v; } QList QmitkExtWorkbenchWindowAdvisor::GetViewExcludeList() { return this->viewExcludeList; } void QmitkExtWorkbenchWindowAdvisor::PostWindowClose() { berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow(); QMainWindow* mainWindow = static_cast (window->GetShell()->GetControl()); QSettings settings(GetQSettingsFile(), QSettings::IniFormat); settings.setValue("ToolbarPosition", mainWindow->saveState()); } QString QmitkExtWorkbenchWindowAdvisor::GetQSettingsFile() const { QFileInfo settingsInfo = QmitkCommonExtPlugin::getContext()->getDataFile(QT_SETTINGS_FILENAME); return settingsInfo.canonicalFilePath(); } diff --git a/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.h b/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.h index f2414bdbd1..2004508251 100644 --- a/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.h +++ b/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.h @@ -1,179 +1,181 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKEXTWORKBENCHWINDOWADVISOR_H_ #define QMITKEXTWORKBENCHWINDOWADVISOR_H_ #include #include #include #include #include #include #include #include class QAction; class QMenu; class MITK_QT_COMMON_EXT_EXPORT QmitkExtWorkbenchWindowAdvisor : public QObject, public berry::WorkbenchWindowAdvisor { Q_OBJECT public: QmitkExtWorkbenchWindowAdvisor(berry::WorkbenchAdvisor* wbAdvisor, berry::IWorkbenchWindowConfigurer::Pointer configurer); ~QmitkExtWorkbenchWindowAdvisor() override; berry::SmartPointer CreateActionBarAdvisor( berry::SmartPointer configurer) override; QWidget* CreateEmptyWindowContents(QWidget* parent) override; void PostWindowCreate() override; void PreWindowOpen() override; void PostWindowOpen() override; void PostWindowClose() override; void ShowViewToolbar(bool show); void ShowPerspectiveToolbar(bool show); void ShowVersionInfo(bool show); void ShowMitkVersionInfo(bool show); void ShowViewMenuItem(bool show); void ShowNewWindowMenuItem(bool show); void ShowClosePerspectiveMenuItem(bool show); bool GetShowClosePerspectiveMenuItem(); void ShowMemoryIndicator(bool show); bool GetShowMemoryIndicator(); //TODO should be removed when product support is here void SetProductName(const QString& product); void SetWindowIcon(const QString& wndIcon); void SetPerspectiveExcludeList(const QList &v); QList GetPerspectiveExcludeList(); void SetViewExcludeList(const QList &v); QList GetViewExcludeList(); protected slots: virtual void onIntro(); virtual void onHelp(); virtual void onHelpOpenHelpPerspective(); virtual void onAbout(); private: /** * Hooks the listeners needed on the window * * @param configurer */ void HookTitleUpdateListeners(berry::IWorkbenchWindowConfigurer::Pointer configurer); QString ComputeTitle(); void RecomputeTitle(); QString GetQSettingsFile() const; /** * Updates the window title. Format will be: [pageInput -] * [currentPerspective -] [editorInput -] [workspaceLocation -] productName * @param editorHidden TODO */ void UpdateTitle(bool editorHidden); void PropertyChange(const berry::Object::Pointer& /*source*/, int propId); static QString QT_SETTINGS_FILENAME; QScopedPointer titlePartListener; QScopedPointer titlePerspectiveListener; QScopedPointer menuPerspectiveListener; QScopedPointer imageNavigatorPartListener; QScopedPointer viewNavigatorPartListener; QScopedPointer editorPropertyListener; friend struct berry::PropertyChangeIntAdapter; friend class PartListenerForTitle; friend class PerspectiveListenerForTitle; friend class PerspectiveListenerForMenu; friend class PartListenerForImageNavigator; friend class PartListenerForViewNavigator; berry::IEditorPart::WeakPtr lastActiveEditor; berry::IPerspectiveDescriptor::WeakPtr lastPerspective; berry::IWorkbenchPage::WeakPtr lastActivePage; QString lastEditorTitle; berry::IAdaptable* lastInput; berry::WorkbenchAdvisor* wbAdvisor; bool showViewToolbar; bool showPerspectiveToolbar; bool showVersionInfo; bool showMitkVersionInfo; bool showViewMenuItem; bool showNewWindowMenuItem; bool showClosePerspectiveMenuItem; bool viewNavigatorFound; bool showMemoryIndicator; QString productName; QString windowIcon; // enables DnD on the editor area QScopedPointer dropTargetListener; // stringlist for excluding perspectives from the perspective menu entry (e.g. Welcome Perspective) QList perspectiveExcludeList; // stringlist for excluding views from the menu entry QList viewExcludeList; // maps perspective ids to QAction objects QHash mapPerspIdToAction; // actions which will be enabled/disabled depending on the application state QList viewActions; QAction* fileSaveProjectAction; QAction* closeProjectAction; QAction* undoAction; QAction* redoAction; QAction* imageNavigatorAction; QAction* viewNavigatorAction; QAction* resetPerspAction; QAction* closePerspAction; QAction* openDicomEditorAction; + QAction* openStdMultiWidgetEditorAction; + QAction* openCustomMultiWidgetEditorAction; }; #endif /*QMITKEXTWORKBENCHWINDOWADVISOR_H_*/ diff --git a/Plugins/org.mitk.gui.qt.ext/src/QmitkOpenCustomMultiWidgetEditorAction.cpp b/Plugins/org.mitk.gui.qt.ext/src/QmitkOpenCustomMultiWidgetEditorAction.cpp new file mode 100644 index 0000000000..eb1dc022d5 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ext/src/QmitkOpenCustomMultiWidgetEditorAction.cpp @@ -0,0 +1,82 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkOpenCustomMultiWidgetEditorAction.h" + +#include "mitkCoreObjectFactory.h" + +#include +#include +#include +#include +#include +#include + +#include "internal/QmitkCommonExtPlugin.h" +#include + +class ctkPluginContext; + +QmitkOpenCustomMultiWidgetEditorAction::QmitkOpenCustomMultiWidgetEditorAction(berry::IWorkbenchWindow::Pointer window) + : QAction(nullptr) +{ + this->init(window); +} + +QmitkOpenCustomMultiWidgetEditorAction::QmitkOpenCustomMultiWidgetEditorAction(const QIcon& icon, berry::IWorkbenchWindow::Pointer window) + : QAction(nullptr) +{ + this->setIcon(icon); + + this->init(window); +} + +void QmitkOpenCustomMultiWidgetEditorAction::init(berry::IWorkbenchWindow::Pointer window) +{ + m_Window = window; + this->setParent(static_cast(m_Window->GetShell()->GetControl())); + this->setText("Custom Display"); + this->setToolTip("Open the custom multi widget editor"); + + berry::IPreferencesService* prefService = berry::Platform::GetPreferencesService(); + + m_GeneralPreferencesNode = prefService->GetSystemPreferences()->Node("/General"); + + this->connect(this, SIGNAL(triggered(bool)), this, SLOT(Run())); +} + +void QmitkOpenCustomMultiWidgetEditorAction::Run() +{ + // check if there is an open perspective, if not open the default perspective + if (m_Window->GetActivePage().IsNull()) + { + QString defaultPerspId = m_Window->GetWorkbench()->GetPerspectiveRegistry()->GetDefaultPerspective(); + m_Window->GetWorkbench()->ShowPerspective(defaultPerspId, m_Window); + } + + ctkPluginContext* context = QmitkCommonExtPlugin::getContext(); + ctkServiceReference serviceRef = context->getServiceReference(); + if (serviceRef) + { + mitk::IDataStorageService* dsService = context->getService(serviceRef); + if (dsService) + { + mitk::IDataStorageReference::Pointer dsRef = dsService->GetDataStorage(); + berry::IEditorInput::Pointer editorInput(new mitk::DataStorageEditorInput(dsRef)); + m_Window->GetActivePage()->OpenEditor(editorInput, "org.mitk.editors.custommultiwidget", true, berry::IWorkbenchPage::MATCH_ID); + } + } +} diff --git a/Plugins/org.mitk.gui.qt.ext/src/QmitkOpenCustomMultiWidgetEditorAction.h b/Plugins/org.mitk.gui.qt.ext/src/QmitkOpenCustomMultiWidgetEditorAction.h new file mode 100644 index 0000000000..8da39ccda9 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ext/src/QmitkOpenCustomMultiWidgetEditorAction.h @@ -0,0 +1,56 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QMITKOPENCUSTOMMULTIWIDGETEDITORACTION_H +#define QMITKOPENCUSTOMMULTIWIDGETEDITORACTION_H + +#ifdef __MINGW32__ +// We need to inlclude winbase.h here in order to declare +// atomic intrinsics like InterlockedIncrement correctly. +// Otherwhise, they would be declared wrong within qatomic_windows.h . +#include +#endif + +#include +#include + +#include + +#include +#include + +class MITK_QT_COMMON_EXT_EXPORT QmitkOpenCustomMultiWidgetEditorAction : public QAction +{ + Q_OBJECT + +public: + + QmitkOpenCustomMultiWidgetEditorAction(berry::IWorkbenchWindow::Pointer window); + QmitkOpenCustomMultiWidgetEditorAction(const QIcon& icon, berry::IWorkbenchWindow::Pointer window); + +protected slots: + + void Run(); + +private: + + void init(berry::IWorkbenchWindow::Pointer window); + berry::IWorkbenchWindow::Pointer m_Window; + berry::IPreferences::WeakPtr m_GeneralPreferencesNode; + +}; + +#endif // QMITKOPENCUSTOMMULTIWIDGETEDITORACTION_H diff --git a/Plugins/org.mitk.gui.qt.ext/src/QmitkOpenStdMultiWidgetEditorAction.cpp b/Plugins/org.mitk.gui.qt.ext/src/QmitkOpenStdMultiWidgetEditorAction.cpp new file mode 100644 index 0000000000..459b8003d1 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ext/src/QmitkOpenStdMultiWidgetEditorAction.cpp @@ -0,0 +1,82 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkOpenStdMultiWidgetEditorAction.h" + +#include "mitkCoreObjectFactory.h" + +#include +#include +#include +#include +#include +#include + +#include "internal/QmitkCommonExtPlugin.h" +#include + +class ctkPluginContext; + +QmitkOpenStdMultiWidgetEditorAction::QmitkOpenStdMultiWidgetEditorAction(berry::IWorkbenchWindow::Pointer window) + : QAction(nullptr) +{ + this->init(window); +} + +QmitkOpenStdMultiWidgetEditorAction::QmitkOpenStdMultiWidgetEditorAction(const QIcon& icon, berry::IWorkbenchWindow::Pointer window) + : QAction(nullptr) +{ + this->setIcon(icon); + + this->init(window); +} + +void QmitkOpenStdMultiWidgetEditorAction::init(berry::IWorkbenchWindow::Pointer window) +{ + m_Window = window; + this->setParent(static_cast(m_Window->GetShell()->GetControl())); + this->setText("Standard Display"); + this->setToolTip("Open the standard multi widget editor"); + + berry::IPreferencesService* prefService = berry::Platform::GetPreferencesService(); + + m_GeneralPreferencesNode = prefService->GetSystemPreferences()->Node("/General"); + + this->connect(this, SIGNAL(triggered(bool)), this, SLOT(Run())); +} + +void QmitkOpenStdMultiWidgetEditorAction::Run() +{ + // check if there is an open perspective, if not open the default perspective + if (m_Window->GetActivePage().IsNull()) + { + QString defaultPerspId = m_Window->GetWorkbench()->GetPerspectiveRegistry()->GetDefaultPerspective(); + m_Window->GetWorkbench()->ShowPerspective(defaultPerspId, m_Window); + } + + ctkPluginContext* context = QmitkCommonExtPlugin::getContext(); + ctkServiceReference serviceRef = context->getServiceReference(); + if (serviceRef) + { + mitk::IDataStorageService* dsService = context->getService(serviceRef); + if (dsService) + { + mitk::IDataStorageReference::Pointer dsRef = dsService->GetDataStorage(); + berry::IEditorInput::Pointer editorInput(new mitk::DataStorageEditorInput(dsRef)); + m_Window->GetActivePage()->OpenEditor(editorInput, "org.mitk.editors.stdmultiwidget", true, berry::IWorkbenchPage::MATCH_ID); + } + } +} diff --git a/Plugins/org.mitk.gui.qt.ext/src/QmitkOpenStdMultiWidgetEditorAction.h b/Plugins/org.mitk.gui.qt.ext/src/QmitkOpenStdMultiWidgetEditorAction.h new file mode 100644 index 0000000000..ed0a4dbad8 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ext/src/QmitkOpenStdMultiWidgetEditorAction.h @@ -0,0 +1,56 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QMITKOPENSTDMULTIWIDGETEDITORACTION_H +#define QMITKOPENSTDMULTIWIDGETEDITORACTION_H + +#ifdef __MINGW32__ +// We need to inlclude winbase.h here in order to declare +// atomic intrinsics like InterlockedIncrement correctly. +// Otherwhise, they would be declared wrong within qatomic_windows.h . +#include +#endif + +#include +#include + +#include + +#include +#include + +class MITK_QT_COMMON_EXT_EXPORT QmitkOpenStdMultiWidgetEditorAction : public QAction +{ + Q_OBJECT + +public: + + QmitkOpenStdMultiWidgetEditorAction(berry::IWorkbenchWindow::Pointer window); + QmitkOpenStdMultiWidgetEditorAction(const QIcon& icon, berry::IWorkbenchWindow::Pointer window); + +protected slots: + + void Run(); + +private: + + void init(berry::IWorkbenchWindow::Pointer window); + berry::IWorkbenchWindow::Pointer m_Window; + berry::IPreferences::WeakPtr m_GeneralPreferencesNode; + +}; + +#endif // QMITKOPENSTDMULTIWIDGETEDITORACTION_H diff --git a/Plugins/org.mitk.gui.qt.renderwindowmanager/src/internal/QmitkRenderWindowManagerView.cpp b/Plugins/org.mitk.gui.qt.renderwindowmanager/src/internal/QmitkRenderWindowManagerView.cpp index b1cbad6b7d..4671215f00 100644 --- a/Plugins/org.mitk.gui.qt.renderwindowmanager/src/internal/QmitkRenderWindowManagerView.cpp +++ b/Plugins/org.mitk.gui.qt.renderwindowmanager/src/internal/QmitkRenderWindowManagerView.cpp @@ -1,88 +1,115 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // render window manager plugin #include "QmitkRenderWindowManagerView.h" // mitk core #include #include const std::string QmitkRenderWindowManagerView::VIEW_ID = "org.mitk.views.renderwindowmanager"; +void QmitkRenderWindowManagerView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) +{ + if (m_RenderWindowPart != renderWindowPart) + { + m_RenderWindowPart = renderWindowPart; + } + + SetControlledRenderer(); +} + +void QmitkRenderWindowManagerView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) +{ + if (m_RenderWindowPart == renderWindowPart) + { + m_RenderWindowPart = nullptr; + } + + SetControlledRenderer(); +} + +void QmitkRenderWindowManagerView::RenderWindowPartInputChanged(mitk::IRenderWindowPart* renderWindowPart) +{ + if (m_RenderWindowPart == renderWindowPart) + { + SetControlledRenderer(); + } +} + void QmitkRenderWindowManagerView::SetFocus() { // nothing here } void QmitkRenderWindowManagerView::CreateQtPartControl(QWidget* parent) { // create GUI widgets m_Controls.setupUi(parent); // add custom render window manager UI widget to the 'renderWindowManagerTab' m_RenderWindowInspector = new QmitkDataStorageRenderWindowInspector(parent); m_RenderWindowInspector->SetDataStorage(GetDataStorage()); m_RenderWindowInspector->setObjectName(QStringLiteral("m_RenderWindowManipulatorWidget")); m_Controls.verticalLayout->addWidget(m_RenderWindowInspector); SetControlledRenderer(); - for (const auto& renderer : m_ControlledRenderer) - { - m_Controls.comboBoxRenderWindowSelection->addItem(renderer->GetName()); - } connect(m_Controls.comboBoxRenderWindowSelection, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(OnRenderWindowSelectionChanged(const QString&))); // data node context menu and menu actions m_InspectorView = m_RenderWindowInspector->GetView(); m_DataNodeContextMenu = new QmitkDataNodeContextMenu(GetSite(), m_InspectorView); m_DataNodeContextMenu->SetDataStorage(GetDataStorage()); //m_DataNodeContextMenu->SetSurfaceDecimation(m_SurfaceDecimation); connect(m_InspectorView, SIGNAL(customContextMenuRequested(const QPoint&)), m_DataNodeContextMenu, SLOT(OnContextMenuRequested(const QPoint&))); - - OnRenderWindowSelectionChanged(m_Controls.comboBoxRenderWindowSelection->itemText(0)); } void QmitkRenderWindowManagerView::SetControlledRenderer() { + m_Controls.comboBoxRenderWindowSelection->clear(); + const mitk::RenderingManager::RenderWindowVector allRegisteredRenderWindows = mitk::RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); mitk::BaseRenderer* baseRenderer = nullptr; for (const auto &renderWindow : allRegisteredRenderWindows) { baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow); if (nullptr != baseRenderer) { m_ControlledRenderer.push_back(baseRenderer); + m_Controls.comboBoxRenderWindowSelection->addItem(baseRenderer->GetName()); } } m_RenderWindowInspector->SetControlledRenderer(m_ControlledRenderer); + + OnRenderWindowSelectionChanged(m_Controls.comboBoxRenderWindowSelection->itemText(0)); } void QmitkRenderWindowManagerView::OnRenderWindowSelectionChanged(const QString& renderWindowId) { m_RenderWindowInspector->SetActiveRenderWindow(renderWindowId); mitk::BaseRenderer* selectedRenderer = mitk::BaseRenderer::GetByName(renderWindowId.toStdString()); if (nullptr != selectedRenderer) { m_DataNodeContextMenu->SetBaseRenderer(selectedRenderer); } } QItemSelectionModel* QmitkRenderWindowManagerView::GetDataNodeSelectionModel() const { return m_InspectorView->selectionModel(); } diff --git a/Plugins/org.mitk.gui.qt.renderwindowmanager/src/internal/QmitkRenderWindowManagerView.h b/Plugins/org.mitk.gui.qt.renderwindowmanager/src/internal/QmitkRenderWindowManagerView.h index 8eaec8973a..281b677441 100644 --- a/Plugins/org.mitk.gui.qt.renderwindowmanager/src/internal/QmitkRenderWindowManagerView.h +++ b/Plugins/org.mitk.gui.qt.renderwindowmanager/src/internal/QmitkRenderWindowManagerView.h @@ -1,75 +1,83 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKRENDERWINDOWMANAGERVIEW_H #define QMITKRENDERWINDOWMANAGERVIEW_H // render window manager plugin #include "ui_QmitkRenderWindowManagerControls.h" // render window manager UI module #include // mitk gui qt application #include +// mitk gui common plugin +#include + // mitk gui qt common plugin #include /** * @brief RenderWindowManager */ -class QmitkRenderWindowManagerView : public QmitkAbstractView +class QmitkRenderWindowManagerView : public QmitkAbstractView, public mitk::IRenderWindowPartListener { Q_OBJECT public: static const std::string VIEW_ID; + virtual void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override; + virtual void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) override; + virtual void RenderWindowPartInputChanged(mitk::IRenderWindowPart* renderWindowPart) override; + protected: virtual void SetFocus() override; virtual void CreateQtPartControl(QWidget* parent) override; private Q_SLOTS: /** * @brief Called when the user changes the render window selection in the combo box. * * @param renderWindowId The text inside the combo box. */ void OnRenderWindowSelectionChanged(const QString& renderWindowId); private: void SetControlledRenderer(); QWidget* m_Parent; Ui::QmitkRenderWindowManagerControls m_Controls; + mitk::IRenderWindowPart* m_RenderWindowPart; + QmitkDataStorageRenderWindowInspector* m_RenderWindowInspector; QmitkDataNodeContextMenu* m_DataNodeContextMenu; QAbstractItemView* m_InspectorView; mitk::RenderWindowLayerUtilities::RendererVector m_ControlledRenderer; virtual QItemSelectionModel* GetDataNodeSelectionModel() const override; - }; #endif // QMITKRENDERWINDOWMANAGERVIEW_H diff --git a/Plugins/org.mitk.gui.qt.stdmultiwidgeteditor/plugin.xml b/Plugins/org.mitk.gui.qt.stdmultiwidgeteditor/plugin.xml index 580dba9b9d..76790950d7 100644 --- a/Plugins/org.mitk.gui.qt.stdmultiwidgeteditor/plugin.xml +++ b/Plugins/org.mitk.gui.qt.stdmultiwidgeteditor/plugin.xml @@ -1,28 +1,29 @@