diff --git a/Core/Code/DataManagement/mitkDataNode.cpp b/Core/Code/DataManagement/mitkDataNode.cpp index 7c61d404b1..57e790dfe6 100644 --- a/Core/Code/DataManagement/mitkDataNode.cpp +++ b/Core/Code/DataManagement/mitkDataNode.cpp @@ -1,564 +1,568 @@ /*=================================================================== 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 "mitkDataNode.h" #include "mitkCoreObjectFactory.h" #include #include "mitkProperties.h" #include "mitkStringProperty.h" #include "mitkGroupTagProperty.h" #include "mitkSmartPointerProperty.h" //#include "mitkMaterialProperty.h" #include "mitkColorProperty.h" #include "mitkLevelWindowProperty.h" #include "mitkGeometry3D.h" #include "mitkRenderingManager.h" #include "mitkGlobalInteraction.h" #include "mitkEventMapper.h" #include "mitkGenericProperty.h" #include "mitkCoreObjectFactory.h" mitk::Mapper* mitk::DataNode::GetMapper(MapperSlotId id) const { if( (id >= m_Mappers.size()) || (m_Mappers[id].IsNull()) ) { if(id >= m_Mappers.capacity()) { // int i, size=id-m_Mappers.capacity()+10; m_Mappers.resize(id+10); } m_Mappers[id] = CoreObjectFactory::GetInstance()->CreateMapper(const_cast(this),id); } return m_Mappers[id]; } mitk::BaseData* mitk::DataNode::GetData() const { return m_Data; } mitk::Interactor* mitk::DataNode::GetInteractor() const { return m_Interactor; } void mitk::DataNode::SetData(mitk::BaseData* baseData) { if(m_Data!=baseData) { m_Data=baseData; m_Mappers.clear(); m_Mappers.resize(10); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(this); m_DataReferenceChangedTime.Modified(); Modified(); //inform the interactor about the change if (m_Interactor.IsNotNull()) m_Interactor->DataChanged(); } } void mitk::DataNode::SetInteractor(mitk::Interactor* interactor) { m_Interactor = interactor; if(m_Interactor.IsNotNull()) m_Interactor->SetDataNode(this); } mitk::DataNode::DataNode() : m_Data(NULL), m_PropertyListModifiedObserverTag(0) { m_Mappers.resize(10); m_PropertyList = PropertyList::New(); // subscribe for modified event itk::MemberCommand::Pointer _PropertyListModifiedCommand = itk::MemberCommand::New(); _PropertyListModifiedCommand->SetCallbackFunction(this, &mitk::DataNode::PropertyListModified); m_PropertyListModifiedObserverTag = m_PropertyList->AddObserver(itk::ModifiedEvent(), _PropertyListModifiedCommand); } mitk::DataNode::~DataNode() { if(m_PropertyList.IsNotNull()) // remove modified event listener m_PropertyList->RemoveObserver(m_PropertyListModifiedObserverTag); Interactor* interactor = this->GetInteractor(); if ( interactor ) { mitk::GlobalInteraction::GetInstance()->RemoveInteractor( interactor ); } m_Mappers.clear(); m_Data = NULL; } mitk::DataNode& mitk::DataNode::operator=(const DataNode& right) { mitk::DataNode* node=mitk::DataNode::New(); node->SetData(right.GetData()); return *node; } mitk::DataNode& mitk::DataNode::operator=(mitk::BaseData* right) { mitk::DataNode* node=mitk::DataNode::New(); node->SetData(right); return *node; } #if (_MSC_VER > 1200) || !defined(_MSC_VER) MBI_STD::istream& mitk::operator>>( MBI_STD::istream& i, mitk::DataNode::Pointer& dtn ) #endif #if ((defined(_MSC_VER)) && (_MSC_VER <= 1200)) MBI_STD::istream& operator>>( MBI_STD::istream& i, mitk::DataNode::Pointer& dtn ) #endif { dtn = mitk::DataNode::New(); //i >> av.get(); return i; } #if (_MSC_VER > 1200) || !defined(_MSC_VER) MBI_STD::ostream& mitk::operator<<( MBI_STD::ostream& o, mitk::DataNode::Pointer& dtn) #endif #if ((defined(_MSC_VER)) && (_MSC_VER <= 1200)) MBI_STD::ostream& operator<<( MBI_STD::ostream& o, mitk::DataNode::Pointer& dtn) #endif { if(dtn->GetData()!=NULL) o<GetData()->GetNameOfClass(); else o<<"empty data"; return o; } void mitk::DataNode::SetMapper(MapperSlotId id, mitk::Mapper* mapper) { m_Mappers[id] = mapper; if (mapper!=NULL) mapper->SetDataNode(this); } void mitk::DataNode::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } } void mitk::DataNode::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::DataNode::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::DataNode::VerifyRequestedRegion() { return true; } void mitk::DataNode::SetRequestedRegion( const itk::DataObject * /*data*/) { } void mitk::DataNode::CopyInformation(const itk::DataObject * /*data*/) { } mitk::PropertyList* mitk::DataNode::GetPropertyList(const mitk::BaseRenderer* renderer) const { if(renderer==NULL) return m_PropertyList; mitk::PropertyList::Pointer & propertyList = m_MapOfPropertyLists[renderer]; if(propertyList.IsNull()) propertyList = mitk::PropertyList::New(); assert(m_MapOfPropertyLists[renderer].IsNotNull()); return propertyList; } void mitk::DataNode::ConcatenatePropertyList(PropertyList *pList, bool replace) { m_PropertyList->ConcatenatePropertyList(pList, replace); } mitk::BaseProperty* mitk::DataNode::GetProperty(const char *propertyKey, const mitk::BaseRenderer* renderer) const { if(propertyKey==NULL) return NULL; //renderer specified? if (renderer) { std::map::const_iterator it; //check for the renderer specific property it=m_MapOfPropertyLists.find(renderer); if(it!=m_MapOfPropertyLists.end()) //found { mitk::BaseProperty::Pointer property; property=it->second->GetProperty(propertyKey); if(property.IsNotNull())//found an enabled property in the render specific list return property; else //found a renderer specific list, but not the desired property return m_PropertyList->GetProperty(propertyKey); //return renderer unspecific property } else //didn't find the property list of the given renderer { //return the renderer unspecific property if there is one return m_PropertyList->GetProperty(propertyKey); } } else //no specific renderer given; use the renderer independent one { mitk::BaseProperty::Pointer property; property=m_PropertyList->GetProperty(propertyKey); if(property.IsNotNull()) return property; } //only to satisfy compiler! return NULL; } mitk::DataNode::GroupTagList mitk::DataNode::GetGroupTags() const { GroupTagList groups; const PropertyList::PropertyMap* propertyMap = m_PropertyList->GetMap(); for ( PropertyList::PropertyMap::const_iterator groupIter = propertyMap->begin(); // m_PropertyList is created in the constructor, so we don't check it here groupIter != propertyMap->end(); ++groupIter ) { const BaseProperty* bp = groupIter->second; if ( dynamic_cast(bp) ) { groups.insert( groupIter->first ); } } return groups; } bool mitk::DataNode::GetBoolProperty(const char* propertyKey, bool& boolValue, mitk::BaseRenderer* renderer) const { mitk::BoolProperty::Pointer boolprop = dynamic_cast(GetProperty(propertyKey, renderer)); if(boolprop.IsNull()) return false; boolValue = boolprop->GetValue(); return true; } bool mitk::DataNode::GetIntProperty(const char* propertyKey, int &intValue, mitk::BaseRenderer* renderer) const { mitk::IntProperty::Pointer intprop = dynamic_cast(GetProperty(propertyKey, renderer)); if(intprop.IsNull()) return false; intValue = intprop->GetValue(); return true; } bool mitk::DataNode::GetFloatProperty(const char* propertyKey, float &floatValue, mitk::BaseRenderer* renderer) const { mitk::FloatProperty::Pointer floatprop = dynamic_cast(GetProperty(propertyKey, renderer)); if(floatprop.IsNull()) return false; floatValue = floatprop->GetValue(); return true; } bool mitk::DataNode::GetStringProperty(const char* propertyKey, std::string& string, mitk::BaseRenderer* renderer) const { mitk::StringProperty::Pointer stringProp = dynamic_cast(GetProperty(propertyKey, renderer)); if(stringProp.IsNull()) { return false; } else { //memcpy((void*)string, stringProp->GetValue(), strlen(stringProp->GetValue()) + 1 ); // looks dangerous string = stringProp->GetValue(); return true; } } bool mitk::DataNode::GetColor(float rgb[3], mitk::BaseRenderer* renderer, const char* propertyKey) const { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetProperty(propertyKey, renderer)); if(colorprop.IsNull()) return false; memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3*sizeof(float)); return true; } bool mitk::DataNode::GetOpacity(float &opacity, mitk::BaseRenderer* renderer, const char* propertyKey) const { mitk::FloatProperty::Pointer opacityprop = dynamic_cast(GetProperty(propertyKey, renderer)); if(opacityprop.IsNull()) return false; opacity=opacityprop->GetValue(); return true; } bool mitk::DataNode::GetLevelWindow(mitk::LevelWindow &levelWindow, mitk::BaseRenderer* renderer, const char* propertyKey) const { mitk::LevelWindowProperty::Pointer levWinProp = dynamic_cast(GetProperty(propertyKey, renderer)); if(levWinProp.IsNull()) return false; levelWindow=levWinProp->GetLevelWindow(); return true; } void mitk::DataNode::SetColor(const mitk::Color &color, mitk::BaseRenderer* renderer, const char* propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(color); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetColor(float red, float green, float blue, mitk::BaseRenderer* renderer, const char* propertyKey) { float color[3]; color[0]=red; color[1]=green; color[2]=blue; SetColor(color, renderer, propertyKey); } void mitk::DataNode::SetColor(const float rgb[3], mitk::BaseRenderer* renderer, const char* propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(rgb); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetVisibility(bool visible, mitk::BaseRenderer* renderer, const char* propertyKey) { mitk::BoolProperty::Pointer prop; prop = mitk::BoolProperty::New(visible); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetOpacity(float opacity, mitk::BaseRenderer* renderer, const char* propertyKey) { mitk::FloatProperty::Pointer prop; prop = mitk::FloatProperty::New(opacity); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetLevelWindow(mitk::LevelWindow levelWindow, mitk::BaseRenderer* renderer, const char* propertyKey) { mitk::LevelWindowProperty::Pointer prop; prop = mitk::LevelWindowProperty::New(levelWindow); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetIntProperty(const char* propertyKey, int intValue, mitk::BaseRenderer* renderer) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::IntProperty::New(intValue)); } void mitk::DataNode::SetBoolProperty( const char* propertyKey, bool boolValue, mitk::BaseRenderer* renderer/*=NULL*/ ) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); } void mitk::DataNode::SetFloatProperty( const char* propertyKey, float floatValue, mitk::BaseRenderer* renderer/*=NULL*/ ) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); } void mitk::DataNode::SetStringProperty( const char* propertyKey, const char* stringValue, mitk::BaseRenderer* renderer/*=NULL*/ ) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); } void mitk::DataNode::SetProperty(const char *propertyKey, BaseProperty* propertyValue, const mitk::BaseRenderer* renderer) { GetPropertyList(renderer)->SetProperty(propertyKey, propertyValue); } void mitk::DataNode::ReplaceProperty(const char *propertyKey, BaseProperty* propertyValue, const mitk::BaseRenderer* renderer) { GetPropertyList(renderer)->ReplaceProperty(propertyKey, propertyValue); } void mitk::DataNode::AddProperty(const char *propertyKey, BaseProperty* propertyValue, const mitk::BaseRenderer* renderer, bool overwrite) { if((overwrite) || (GetProperty(propertyKey, renderer) == NULL)) { SetProperty(propertyKey, propertyValue, renderer); } } vtkLinearTransform* mitk::DataNode::GetVtkTransform(int t) const { assert(m_Data.IsNotNull()); mitk::Geometry3D* geometry = m_Data->GetGeometry(t); if(geometry == NULL) return NULL; return geometry->GetVtkTransform(); } unsigned long mitk::DataNode::GetMTime() const { unsigned long time = Superclass::GetMTime(); if(m_Data.IsNotNull()) { if((time < m_Data->GetMTime()) || ((m_Data->GetSource().IsNotNull()) && (time < m_Data->GetSource()->GetMTime())) ) { Modified(); return Superclass::GetMTime(); } } return time; } void mitk::DataNode::SetSelected(bool selected, mitk::BaseRenderer* renderer) { mitk::BoolProperty::Pointer selectedProperty = dynamic_cast(GetProperty("selected")); if ( selectedProperty.IsNull() ) { selectedProperty = mitk::BoolProperty::New(); selectedProperty->SetValue(false); SetProperty("selected", selectedProperty, renderer); } if( selectedProperty->GetValue() != selected ) { selectedProperty->SetValue(selected); itk::ModifiedEvent event; InvokeEvent( event ); } } /* class SelectedEvent : public itk::ModifiedEvent { public: typedef SelectedEvent Self; typedef itk::ModifiedEvent Superclass; SelectedEvent(DataNode* dataNode) { m_DataNode = dataNode; }; DataNode* GetDataNode() { return m_DataNode; }; virtual const char * GetEventName() const { return "SelectedEvent"; } virtual bool CheckEvent(const ::itk::EventObject* e) const { return dynamic_cast(e); } virtual ::itk::EventObject* MakeObject() const { return new Self(m_DataNode); } private: DataNode* m_DataNode; SelectedEvent(const Self& event) { m_DataNode = event.m_DataNode; }; void operator=(const Self& event) { m_DataNode = event.m_DataNode; } }; */ bool mitk::DataNode::IsSelected(mitk::BaseRenderer* renderer) { bool selected; if ( !GetBoolProperty("selected", selected, renderer) ) return false; return selected; } void mitk::DataNode::SetInteractorEnabled( const bool& enabled ) { if ( m_Interactor.IsNull() ) { itkWarningMacro("Interactor is NULL. Couldn't enable or disable interaction."); return; } if ( enabled ) mitk::GlobalInteraction::GetInstance()->AddInteractor( m_Interactor.GetPointer() ); else mitk::GlobalInteraction::GetInstance()->RemoveInteractor( m_Interactor.GetPointer() ); } void mitk::DataNode::EnableInteractor() { SetInteractorEnabled( true ); } void mitk::DataNode::DisableInteractor() { SetInteractorEnabled( false ); } bool mitk::DataNode::IsInteractorEnabled() const { return mitk::GlobalInteraction::GetInstance()->InteractorRegistered( m_Interactor.GetPointer() ); } void mitk::DataNode::SetDataInteractor(const DataInteractor::Pointer& interactor) { m_DataInteractor = interactor; Modified(); + + // the interactor has changed, so we have ti invoke an InteractorChangedEvent + const mitk::DataNode::InteractorChangedEvent changedEvent; + InvokeEvent( changedEvent ); } mitk::DataInteractor::Pointer mitk::DataNode::GetDataInteractor() const { return m_DataInteractor; } void mitk::DataNode::PropertyListModified( const itk::Object* /*caller*/, const itk::EventObject& ) { Modified(); } diff --git a/Core/Code/DataManagement/mitkDataNode.h b/Core/Code/DataManagement/mitkDataNode.h index 6f39881565..51eb210449 100644 --- a/Core/Code/DataManagement/mitkDataNode.h +++ b/Core/Code/DataManagement/mitkDataNode.h @@ -1,533 +1,539 @@ /*=================================================================== 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 DATATREENODE_H_HEADER_INCLUDED_C1E14338 #define DATATREENODE_H_HEADER_INCLUDED_C1E14338 #include "mitkImageSource.h" #include "mitkBaseData.h" //#include "mitkMapper.h" #include "mitkInteractor.h" #include "mitkDataInteractor.h" #ifdef MBI_NO_STD_NAMESPACE #define MBI_STD #include #include #else #define MBI_STD std #include #include #endif #include "mitkStringProperty.h" #include "mitkColorProperty.h" #include "mitkPropertyList.h" //#include "mitkMapper.h" #include #include #include "mitkLevelWindow.h" class vtkLinearTransform; namespace mitk { class BaseRenderer; class Mapper; //##Documentation //## @brief Class for nodes of the DataTree //## //## Contains the data (instance of BaseData), a list of mappers, which can //## draw the data, a transform (vtkTransform) and a list of properties //## (PropertyList). //## @ingroup DataManagement //## //## @todo clean up all the GetProperty methods. There are too many different flavours... Can most probably be reduced to bool GetProperty(type&) //## //## @warning Change in semantics of SetProperty() since Aug 25th 2006. Check your usage of this method if you do //## more with properties than just call SetProperty( "key", new SomeProperty("value") ). class MITK_CORE_EXPORT DataNode : public itk::DataObject { public: typedef mitk::Geometry3D::Pointer Geometry3DPointer; typedef std::vector< itk::SmartPointer< Mapper > > MapperVector; typedef std::map MapOfPropertyLists; typedef std::set GroupTagList; + /** + * \brief Definition of an itk::Event that is invoked when + * a DataInteractor is set on this DataNode. + */ + itkEventMacro(InteractorChangedEvent, itk::AnyEvent); + mitkClassMacro(DataNode, itk::DataObject); itkNewMacro(Self); mitk::Mapper* GetMapper(MapperSlotId id) const; //##Documentation //## @brief Get the data object (instance of BaseData, e.g., an Image) //## managed by this DataNode BaseData* GetData() const; //##Documentation //## @brief Get the transformation applied prior to displaying the data as //## a vtkTransform //## \deprecated use GetData()->GetGeometry()->GetVtkTransform() instead vtkLinearTransform* GetVtkTransform(int t=0) const; //##Documentation //## @brief Get the Interactor. //## @deprecatedSince{2013_03} Use DataInteractor and GetDataInteractor instead. Interactor* GetInteractor() const; //##Documentation //## @brief Set the data object (instance of BaseData, e.g., an Image) //## managed by this DataNode //## @warning the actor-mode of the vtkInteractor does not work any more, if the transform of the //## data-tree-node is connected to the transform of the basedata via vtkTransform->SetInput. virtual void SetData(mitk::BaseData* baseData); //##Documentation //## @brief Set the Interactor. //## @deprecatedSince{2013_03} Use DataInteractor and SetDataInteractor instead. virtual void SetInteractor(Interactor* interactor); virtual void SetDataInteractor(const DataInteractor::Pointer& interactor); virtual DataInteractor::Pointer GetDataInteractor() const; mitk::DataNode& operator=(const DataNode& right); mitk::DataNode& operator=(BaseData* right); virtual void SetMapper(MapperSlotId id, mitk::Mapper* mapper); virtual void UpdateOutputInformation(); virtual void SetRequestedRegionToLargestPossibleRegion(); virtual bool RequestedRegionIsOutsideOfTheBufferedRegion(); virtual bool VerifyRequestedRegion(); virtual void SetRequestedRegion( const itk::DataObject *data); virtual void CopyInformation(const itk::DataObject *data); //##Documentation //## @brief Set the property (instance of BaseProperty) with key @a propertyKey in the PropertyList //## of the @a renderer (if NULL, use BaseRenderer-independent PropertyList). This is set-by-value. //## //## @warning Change in semantics since Aug 25th 2006. Check your usage of this method if you do //## more with properties than just call SetProperty( "key", new SomeProperty("value") ). //## //## @sa GetProperty //## @sa m_PropertyList //## @sa m_MapOfPropertyLists void SetProperty(const char *propertyKey, BaseProperty* property, const mitk::BaseRenderer* renderer = NULL); //##Documentation //## @brief Replace the property (instance of BaseProperty) with key @a propertyKey in the PropertyList //## of the @a renderer (if NULL, use BaseRenderer-independent PropertyList). This is set-by-reference. //## //## If @a renderer is @a NULL the property is set in the BaseRenderer-independent //## PropertyList of this DataNode. //## @sa GetProperty //## @sa m_PropertyList //## @sa m_MapOfPropertyLists void ReplaceProperty(const char *propertyKey, BaseProperty* property, const mitk::BaseRenderer* renderer = NULL); //##Documentation //## @brief Add the property (instance of BaseProperty) if it does //## not exist (or always if \a overwrite is \a true) //## with key @a propertyKey in the PropertyList //## of the @a renderer (if NULL, use BaseRenderer-independent //## PropertyList). This is set-by-value. //## //## For \a overwrite == \a false the property is \em not changed //## if it already exists. For \a overwrite == \a true the method //## is identical to SetProperty. //## //## @sa SetProperty //## @sa GetProperty //## @sa m_PropertyList //## @sa m_MapOfPropertyLists void AddProperty(const char *propertyKey, BaseProperty* property, const mitk::BaseRenderer* renderer = NULL, bool overwrite = false); //##Documentation //## @brief Get the PropertyList of the @a renderer. If @a renderer is @a //## NULL, the BaseRenderer-independent PropertyList of this DataNode //## is returned. //## @sa GetProperty //## @sa m_PropertyList //## @sa m_MapOfPropertyLists mitk::PropertyList* GetPropertyList(const mitk::BaseRenderer* renderer = NULL) const; //##Documentation //## @brief Add values from another PropertyList. //## //## Overwrites values in m_PropertyList only when possible (i.e. when types are compatible). //## If you want to allow for object type changes (replacing a "visible":BoolProperty with "visible":IntProperty, //## set the @param replace. //## //## @param replace true: if @param pList contains a property "visible" of type ColorProperty and our m_PropertyList also has a "visible" property of a different type (e.g. BoolProperty), change the type, i.e. replace the objects behind the pointer. //## //## @sa SetProperty //## @sa ReplaceProperty //## @sa m_PropertyList void ConcatenatePropertyList(PropertyList* pList, bool replace = false); //##Documentation //## @brief Get the property (instance of BaseProperty) with key @a propertyKey from the PropertyList //## of the @a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. //## //## If @a renderer is @a NULL or the @a propertyKey cannot be found //## in the PropertyList specific to @a renderer or is disabled there, the BaseRenderer-independent //## PropertyList of this DataNode is queried. //## @sa GetPropertyList //## @sa m_PropertyList //## @sa m_MapOfPropertyLists mitk::BaseProperty* GetProperty(const char *propertyKey, const mitk::BaseRenderer* renderer = NULL) const; //##Documentation //## @brief Get the property of type T with key @a propertyKey from the PropertyList //## of the @a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. //## //## If @a renderer is @a NULL or the @a propertyKey cannot be found //## in the PropertyList specific to @a renderer or is disabled there, the BaseRenderer-independent //## PropertyList of this DataNode is queried. //## @sa GetPropertyList //## @sa m_PropertyList //## @sa m_MapOfPropertyLists template bool GetProperty(itk::SmartPointer &property, const char *propertyKey, const mitk::BaseRenderer* renderer = NULL) const { property = dynamic_cast(GetProperty(propertyKey, renderer)); return property.IsNotNull(); } //##Documentation //## @brief Get the property of type T with key @a propertyKey from the PropertyList //## of the @a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. //## //## If @a renderer is @a NULL or the @a propertyKey cannot be found //## in the PropertyList specific to @a renderer or is disabled there, the BaseRenderer-independent //## PropertyList of this DataNode is queried. //## @sa GetPropertyList //## @sa m_PropertyList //## @sa m_MapOfPropertyLists template bool GetProperty(T* &property, const char *propertyKey, const mitk::BaseRenderer* renderer = NULL) const { property = dynamic_cast(GetProperty(propertyKey, renderer)); return property!=NULL; } //##Documentation //## @brief Convenience access method for GenericProperty properties //## (T being the type of the second parameter) //## @return @a true property was found template bool GetPropertyValue(const char* propertyKey, T & value, mitk::BaseRenderer* renderer=NULL) const { GenericProperty* gp= dynamic_cast*>(GetProperty(propertyKey, renderer)); if ( gp != NULL ) { value = gp->GetValue(); return true; } return false; } // @brief Get a set of all group tags from this node's property list GroupTagList GetGroupTags() const; //##Documentation //## @brief Convenience access method for bool properties (instances of //## BoolProperty) //## @return @a true property was found bool GetBoolProperty(const char* propertyKey, bool &boolValue, mitk::BaseRenderer* renderer = NULL) const; //##Documentation //## @brief Convenience access method for int properties (instances of //## IntProperty) //## @return @a true property was found bool GetIntProperty(const char* propertyKey, int &intValue, mitk::BaseRenderer* renderer=NULL) const; //##Documentation //## @brief Convenience access method for float properties (instances of //## FloatProperty) //## @return @a true property was found bool GetFloatProperty(const char* propertyKey, float &floatValue, mitk::BaseRenderer* renderer = NULL) const; //##Documentation //## @brief Convenience access method for string properties (instances of //## StringProperty) //## @return @a true property was found bool GetStringProperty(const char* propertyKey, std::string& string, mitk::BaseRenderer* renderer = NULL) const; //##Documentation //## @brief Convenience access method for color properties (instances of //## ColorProperty) //## @return @a true property was found bool GetColor(float rgb[3], mitk::BaseRenderer* renderer = NULL, const char* propertyKey = "color") const; //##Documentation //## @brief Convenience access method for level-window properties (instances of //## LevelWindowProperty) //## @return @a true property was found bool GetLevelWindow(mitk::LevelWindow &levelWindow, mitk::BaseRenderer* renderer = NULL, const char* propertyKey = "levelwindow") const; //## //##Documentation //## @brief set the node as selected void SetSelected(bool selected, mitk::BaseRenderer* renderer=NULL); //## //##Documentation //## @brief set the node as selected //## @return @a true node is selected bool IsSelected(mitk::BaseRenderer* renderer=NULL); //##Documentation //## @brief Convenience access method for accessing the name of an object (instance of //## StringProperty with property-key "name") //## @return @a true property was found bool GetName(std::string& nodeName, mitk::BaseRenderer* renderer = NULL, const char* propertyKey = "name") const { return GetStringProperty(propertyKey, nodeName, renderer); } //##Documentation //## @brief Extra convenience access method for accessing the name of an object (instance of //## StringProperty with property-key "name"). //## //## This method does not take the renderer specific //## propertylists into account, because the name of an object should never be renderer specific. //## @returns a std::string with the name of the object (content of "name" Property). //## If there is no "name" Property, an empty string will be returned. virtual std::string GetName() const { mitk::StringProperty* sp = dynamic_cast(this->GetProperty("name")); if (sp == NULL) return ""; return sp->GetValue(); } //##Documentation //## @brief Extra convenience access method to set the name of an object. //## //## The name will be stored in the non-renderer-specific PropertyList in a StringProperty named "name". virtual void SetName( const char* name) { if (name == NULL) return; this->SetProperty("name", StringProperty::New(name)); } //##Documentation //## @brief Extra convenience access method to set the name of an object. //## //## The name will be stored in the non-renderer-specific PropertyList in a StringProperty named "name". virtual void SetName( const std::string name) { this->SetName(name.c_str()); } //##Documentation //## @brief Convenience access method for visibility properties (instances //## of BoolProperty with property-key "visible") //## @return @a true property was found //## @sa IsVisible bool GetVisibility(bool &visible, mitk::BaseRenderer* renderer, const char* propertyKey = "visible") const { return GetBoolProperty(propertyKey, visible, renderer); } //##Documentation //## @brief Convenience access method for opacity properties (instances of //## FloatProperty) //## @return @a true property was found bool GetOpacity(float &opacity, mitk::BaseRenderer* renderer, const char* propertyKey = "opacity") const; //##Documentation //## @brief Convenience access method for boolean properties (instances //## of BoolProperty). Return value is the value of the property. If the property is //## not found, the value of @a defaultIsOn is returned. //## //## Thus, the return value has a different meaning than in the //## GetBoolProperty method! //## @sa GetBoolProperty bool IsOn(const char* propertyKey, mitk::BaseRenderer* renderer, bool defaultIsOn = true) const { if(propertyKey==NULL) return defaultIsOn; GetBoolProperty(propertyKey, defaultIsOn, renderer); return defaultIsOn; } //##Documentation //## @brief Convenience access method for visibility properties (instances //## of BoolProperty). Return value is the visibility. Default is //## visible==true, i.e., true is returned even if the property (@a //## propertyKey) is not found. //## //## Thus, the return value has a different meaning than in the //## GetVisibility method! //## @sa GetVisibility //## @sa IsOn bool IsVisible(mitk::BaseRenderer* renderer, const char* propertyKey = "visible", bool defaultIsOn = true) const { return IsOn(propertyKey, renderer, defaultIsOn); } //##Documentation //## @brief Convenience method for setting color properties (instances of //## ColorProperty) void SetColor(const mitk::Color &color, mitk::BaseRenderer* renderer = NULL, const char* propertyKey = "color"); //##Documentation //## @brief Convenience method for setting color properties (instances of //## ColorProperty) void SetColor(float red, float green, float blue, mitk::BaseRenderer* renderer = NULL, const char* propertyKey = "color"); //##Documentation //## @brief Convenience method for setting color properties (instances of //## ColorProperty) void SetColor(const float rgb[3], mitk::BaseRenderer* renderer = NULL, const char* propertyKey = "color"); //##Documentation //## @brief Convenience method for setting visibility properties (instances //## of BoolProperty) //## @param visible If set to true, the data will be rendered. If false, the render will skip this data. //## @param renderer Specify a renderer if the visibility shall be specific to a renderer //## @param propertykey Can be used to specify a user defined name of the visibility propery. void SetVisibility(bool visible, mitk::BaseRenderer* renderer = NULL, const char* propertyKey = "visible"); //##Documentation //## @brief Convenience method for setting opacity properties (instances of //## FloatProperty) void SetOpacity(float opacity, mitk::BaseRenderer* renderer = NULL, const char* propertyKey = "opacity"); //##Documentation //## @brief Convenience method for setting level-window properties //## (instances of LevelWindowProperty) void SetLevelWindow(mitk::LevelWindow levelWindow, mitk::BaseRenderer* renderer = NULL, const char* propertyKey = "levelwindow"); //##Documentation //## @brief Convenience method for setting int properties (instances of //## IntProperty) void SetIntProperty(const char* propertyKey, int intValue, mitk::BaseRenderer* renderer=NULL); //##Documentation //## @brief Convenience method for setting int properties (instances of //## IntProperty) void SetBoolProperty(const char* propertyKey, bool boolValue, mitk::BaseRenderer* renderer=NULL); //##Documentation //## @brief Convenience method for setting int properties (instances of //## IntProperty) void SetFloatProperty(const char* propertyKey, float floatValue, mitk::BaseRenderer* renderer=NULL); //##Documentation //## @brief Convenience method for setting int properties (instances of //## IntProperty) void SetStringProperty(const char* propertyKey, const char* string, mitk::BaseRenderer* renderer=NULL); //##Documentation //## @brief Get the timestamp of the last change of the contents of this node or //## the referenced BaseData. virtual unsigned long GetMTime() const; //##Documentation //## @brief Get the timestamp of the last change of the reference to the //## BaseData. unsigned long GetDataReferenceChangedTime() const { return m_DataReferenceChangedTime.GetMTime(); } //##Documentation //## @brief Adds or removes the associated interactor to mitk::GLobalInteraction. //## virtual void SetInteractorEnabled( const bool& enabled ); //##Documentation //## @brief Adds the interactor to mitk::GlobalInteraction //## virtual void EnableInteractor(); //##Documentation //## @brief Removes the Interactor from mitk::GlobalInteraction //## virtual void DisableInteractor(); //##Documentation //## @brief Tests, if the interactor is already added to mitk::GlobalInteraction //## virtual bool IsInteractorEnabled() const; protected: DataNode(); virtual ~DataNode(); //## //## Invoked when the property list was modified. Calls Modified() of the DataNode virtual void PropertyListModified(const itk::Object *caller, const itk::EventObject &event); //##Documentation //## @brief Mapper-slots mutable MapperVector m_Mappers; //##Documentation //## @brief The data object (instance of BaseData, e.g., an Image) managed //## by this DataNode BaseData::Pointer m_Data; //##Documentation //## @brief BaseRenderer-independent PropertyList //## //## Properties herein can be overwritten specifically for each BaseRenderer //## by the BaseRenderer-specific properties defined in m_MapOfPropertyLists. PropertyList::Pointer m_PropertyList; //##Documentation //## @brief Map associating each BaseRenderer with its own PropertyList mutable MapOfPropertyLists m_MapOfPropertyLists; //##Documentation //## @brief Interactor, that handles the Interaction Interactor::Pointer m_Interactor; // TODO: INTERACTION_LEGACY DataInteractor::Pointer m_DataInteractor; //##Documentation //## @brief Timestamp of the last change of m_Data itk::TimeStamp m_DataReferenceChangedTime; unsigned long m_PropertyListModifiedObserverTag; }; #if (_MSC_VER > 1200) || !defined(_MSC_VER) MITK_CORE_EXPORT MBI_STD::istream& operator>>( MBI_STD::istream& i, DataNode::Pointer& dtn ); MITK_CORE_EXPORT MBI_STD::ostream& operator<<( MBI_STD::ostream& o, DataNode::Pointer& dtn); #endif } // namespace mitk #if ((defined(_MSC_VER)) && (_MSC_VER <= 1200)) MITK_CORE_EXPORT MBI_STD::istream& operator>>( MBI_STD::istream& i, mitk::DataNode::Pointer& dtn ); MITK_CORE_EXPORT MBI_STD::ostream& operator<<( MBI_STD::ostream& o, mitk::DataNode::Pointer& dtn); #endif #endif /* DATATREENODE_H_HEADER_INCLUDED_C1E14338 */ diff --git a/Core/Code/DataManagement/mitkDataStorage.cpp b/Core/Code/DataManagement/mitkDataStorage.cpp index ab13e75480..02c9697803 100644 --- a/Core/Code/DataManagement/mitkDataStorage.cpp +++ b/Core/Code/DataManagement/mitkDataStorage.cpp @@ -1,512 +1,536 @@ /*=================================================================== 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 "mitkDataNode.h" #include "mitkProperties.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkGroupTagProperty.h" #include "itkMutexLockHolder.h" #include "itkCommand.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(mitk::DataNode* node, mitk::DataNode* parent) { mitk::DataStorage::SetOfObjects::Pointer parents = mitk::DataStorage::SetOfObjects::New(); parents->InsertElement(0, parent); this->Add(node, parents); } void mitk::DataStorage::Remove(const mitk::DataStorage::SetOfObjects* nodes) { if (nodes == NULL) return; for (mitk::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 { mitk::DataStorage::SetOfObjects::ConstPointer result = this->FilterSetOfObjects(this->GetAll(), condition); return result; } mitk::DataNode* mitk::DataStorage::GetNamedNode(const char* name) const { if (name == NULL) return NULL; mitk::StringProperty::Pointer s(mitk::StringProperty::New(name)); mitk::NodePredicateProperty::Pointer p = mitk::NodePredicateProperty::New("name", s); mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(p); if (rs->Size() >= 1) return rs->GetElement(0); else return NULL; } mitk::DataNode* mitk::DataStorage::GetNode(const NodePredicateBase* condition) const { if (condition == NULL) return NULL; mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(condition); if (rs->Size() >= 1) return rs->GetElement(0); else return NULL; } mitk::DataNode* mitk::DataStorage::GetNamedDerivedNode(const char* name, const mitk::DataNode* sourceNode, bool onlyDirectDerivations) const { if (name == NULL) return NULL; mitk::StringProperty::Pointer s(mitk::StringProperty::New(name)); mitk::NodePredicateProperty::Pointer p = mitk::NodePredicateProperty::New("name", s); mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetDerivations(sourceNode, p, onlyDirectDerivations); if (rs->Size() >= 1) return rs->GetElement(0); else return NULL; } void mitk::DataStorage::PrintSelf(std::ostream& os, itk::Indent indent) const { //Superclass::PrintSelf(os, indent); mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetAll(); os << indent << "DataStorage " << this << " is managing " << all->Size() << " objects. List of objects:" << std::endl; for (mitk::DataStorage::SetOfObjects::ConstIterator allIt = all->Begin(); allIt != all->End(); allIt++) { std::string name; allIt.Value()->GetName(name); std::string datatype; if (allIt.Value()->GetData() != NULL) datatype = allIt.Value()->GetData()->GetNameOfClass(); os << indent << " " << allIt.Value().GetPointer() << "<" << datatype << ">: " << name << std::endl; mitk::DataStorage::SetOfObjects::ConstPointer parents = this->GetSources(allIt.Value()); if (parents->Size() > 0) { os << indent << " Direct sources: "; for (mitk::DataStorage::SetOfObjects::ConstIterator parentIt = parents->Begin(); parentIt != parents->End(); parentIt++) os << parentIt.Value().GetPointer() << ", "; os << std::endl; } mitk::DataStorage::SetOfObjects::ConstPointer derivations = this->GetDerivations(allIt.Value()); if (derivations->Size() > 0) { os << indent << " Direct derivations: "; for (mitk::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 == NULL) return NULL; mitk::DataStorage::SetOfObjects::Pointer result = mitk::DataStorage::SetOfObjects::New(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = set->Begin(); it != set->End(); it++) if (condition == NULL || condition->CheckNode(it.Value()) == true) //alway copy the set, otherwise the iterator in mitk::DataStorage::Remove() will crash result->InsertElement(result->Size(), it.Value()); return mitk::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 (mitk::DataStorage::SetOfObjects::ConstIterator nodeIt = all->Begin(); nodeIt != all->End(); nodeIt++) // for each node { mitk::PropertyList* pl = nodeIt.Value()->GetPropertyList(); for (mitk::PropertyList::PropertyMap::const_iterator propIt = pl->GetMap()->begin(); propIt != pl->GetMap()->end(); propIt++) if (dynamic_cast(propIt->second.GetPointer()) != NULL) result.insert(propIt->first); } return result; } void mitk::DataStorage::EmitAddNodeEvent(const mitk::DataNode* node) { AddNodeEvent.Send(node); } void mitk::DataStorage::EmitRemoveNodeEvent(const mitk::DataNode* node) { RemoveNodeEvent.Send(node); } +void mitk::DataStorage::OnNodeInteractorChanged( itk::Object *caller, const itk::EventObject &event ) +{ + const mitk::DataNode* _Node = dynamic_cast(caller); + if(_Node) + { + InteractorChangedNodeEvent.Send( _Node ); + } +} + void mitk::DataStorage::OnNodeModifiedOrDeleted( const itk::Object *caller, const itk::EventObject &event ) { - if(m_BlockNodeModifiedEvents) + if( m_BlockNodeModifiedEvents ) return; const mitk::DataNode* _Node = dynamic_cast(caller); if(_Node) { const itk::ModifiedEvent* modEvent = dynamic_cast(&event); if(modEvent) ChangedNodeEvent.Send(_Node); else DeleteNodeEvent.Send(_Node); } } void mitk::DataStorage::AddListeners( const mitk::DataNode* _Node ) { itk::MutexLockHolder locked(m_MutexOne); // node must not be 0 and must not be yet registered mitk::DataNode* NonConstNode = const_cast(_Node); if(_Node && m_NodeModifiedObserverTags .find(NonConstNode) == m_NodeModifiedObserverTags.end()) { itk::MemberCommand::Pointer nodeModifiedCommand = itk::MemberCommand::New(); nodeModifiedCommand->SetCallbackFunction(this , &mitk::DataStorage::OnNodeModifiedOrDeleted); m_NodeModifiedObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand); + + itk::MemberCommand::Pointer interactorChangedCommand = itk::MemberCommand::New(); + interactorChangedCommand->SetCallbackFunction(this, &mitk::DataStorage::OnNodeInteractorChanged); + m_NodeInteractorChangedObserverTags[NonConstNode] = NonConstNode->AddObserver( mitk::DataNode::InteractorChangedEvent(), interactorChangedCommand); + // add itk delete listener on datastorage itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); deleteCommand->SetCallbackFunction(this, &mitk::DataStorage::OnNodeModifiedOrDeleted); // add observer m_NodeDeleteObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::DeleteEvent(), deleteCommand); } } void mitk::DataStorage::RemoveListeners( const mitk::DataNode* _Node ) { itk::MutexLockHolder locked(m_MutexOne) ; // node must not be 0 and must be registered mitk::DataNode* 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::TimeSlicedGeometry::Pointer mitk::DataStorage::ComputeBoundingGeometry3D( const SetOfObjects* input, const char* boolPropertyKey, mitk::BaseRenderer* renderer, const char* boolPropertyKey2) { if (input == NULL) 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(ScalarTypeNumericTraits::max()); ScalarType stmin, stmax; stmin= ScalarTypeNumericTraits::NonpositiveMin(); stmax= ScalarTypeNumericTraits::max(); ScalarType minimalIntervallSize = stmax; ScalarType minimalTime = stmax; ScalarType maximalTime = 0; // Needed for check of zero bounding boxes mitk::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() != NULL) && (node->GetData()->IsEmpty()==false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer) ) { const TimeSlicedGeometry* geometry = node->GetData()->GetUpdatedTimeSlicedGeometry(); if (geometry != NULL ) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = geometry->GetBoundingBox()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for(i=0; i<8; ++i) { point = geometry->GetCornerPoint(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 ); } } // spacing try { AffineTransform3D::Pointer inverseTransform = AffineTransform3D::New(); geometry->GetIndexToWorldTransform()->GetInverse(inverseTransform); vnl_vector< AffineTransform3D::MatrixType::ValueType > unitVector(3); int axis; for(axis = 0; axis < 3; ++axis) { unitVector.fill(0); unitVector[axis] = 1.0; ScalarType mmPerPixel = 1.0/(inverseTransform->GetMatrix()*unitVector).magnitude(); if(minSpacing[axis] > mmPerPixel) { minSpacing[axis] = mmPerPixel; } } // time bounds // iterate over all time steps // Attention: Objects with zero bounding box are not respected in time bound calculation TimeBounds minTB = geometry->GetTimeBounds(); for (unsigned int i=0; iGetTimeSteps(); i++) { const TimeBounds & curTimeBounds = node->GetData()->GetGeometry(i)->GetTimeBounds(); // get the minimal time of all objects in the DataStorage if ((curTimeBounds[0]stmin)) { minimalTime=curTimeBounds[0]; } // get the maximal time of all objects in the DataStorage if ((curTimeBounds[1]>maximalTime)&&(curTimeBounds[1]SetPoints(pointscontainer); result->ComputeBoundingBox(); // minimal time bounds of a single time step for all geometries TimeBounds minTimeBounds; minTimeBounds[0] = 0; minTimeBounds[1] = 1; // compute the number of time steps unsigned int numberOfTimeSteps = 1; if (maximalTime!=0) // make sure that there is at least one time sliced geometry in the data storage { minTimeBounds[0] = minimalTime; minTimeBounds[1] = minimalTime + minimalIntervallSize; numberOfTimeSteps = static_cast((maximalTime-minimalTime)/minimalIntervallSize); } TimeSlicedGeometry::Pointer timeSlicedGeometry = NULL; 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(); int i; for(i = 0; i < 6; ++i) { bounds[i] /= minSpacing[i/2]; } geometry->SetBounds(bounds); geometry->SetSpacing(minSpacing); geometry->SetTimeBounds(minTimeBounds); // Initialize the time sliced geometry timeSlicedGeometry = TimeSlicedGeometry::New(); timeSlicedGeometry->InitializeEvenlyTimed(geometry,numberOfTimeSteps); } return timeSlicedGeometry; } mitk::TimeSlicedGeometry::Pointer mitk::DataStorage::ComputeBoundingGeometry3D( const char* boolPropertyKey, mitk::BaseRenderer* renderer, const char* boolPropertyKey2) { return this->ComputeBoundingGeometry3D(this->GetAll(), boolPropertyKey, renderer, boolPropertyKey2); } mitk::TimeSlicedGeometry::Pointer mitk::DataStorage::ComputeVisibleBoundingGeometry3D( mitk::BaseRenderer* renderer, const char* boolPropertyKey ) { return ComputeBoundingGeometry3D( "visible", renderer, boolPropertyKey ); } mitk::BoundingBox::Pointer mitk::DataStorage::ComputeBoundingBox( const char* boolPropertyKey, mitk::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 mitk::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() != NULL) && (node->GetData()->IsEmpty()==false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer) ) { const Geometry3D* geometry = node->GetData()->GetUpdatedTimeSlicedGeometry(); if (geometry != NULL ) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = geometry->GetBoundingBox()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for(i=0; i<8; ++i) { point = geometry->GetCornerPoint(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, mitk::BaseRenderer* renderer, const char* boolPropertyKey2) { TimeBounds timeBounds; ScalarType stmin, stmax, cur; stmin= ScalarTypeNumericTraits::NonpositiveMin(); stmax= ScalarTypeNumericTraits::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() != NULL) && (node->GetData()->IsEmpty()==false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer) ) { const Geometry3D* geometry = node->GetData()->GetUpdatedTimeSlicedGeometry(); if (geometry != NULL ) { 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; +} diff --git a/Core/Code/DataManagement/mitkDataStorage.h b/Core/Code/DataManagement/mitkDataStorage.h index fc61b3c54e..6a6a114fa7 100644 --- a/Core/Code/DataManagement/mitkDataStorage.h +++ b/Core/Code/DataManagement/mitkDataStorage.h @@ -1,394 +1,414 @@ /*=================================================================== 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_HEADER_INCLUDED_ #define MITKDATASTORAGE_H_HEADER_INCLUDED_ #include "itkObject.h" #include #include "mitkMessage.h" #include "itkVectorContainer.h" #include "mitkDataNode.h" #include "mitkGeometry3D.h" #include "itkSimpleFastMutexLock.h" #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 MITK_CORE_EXPORT DataStorage : public itk::Object { public: mitkClassMacro(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 NULL or if the DataNode has already been added, //## an exception will be thrown. virtual void Add(mitk::DataNode* node, const mitk::DataStorage::SetOfObjects* parents = NULL) = 0; //##Documentation //## @brief Convenience method to add a node that has one parent //## void Add(mitk::DataNode* node, mitk::DataNode* parent); //##Documentation //## @brief Removes node from the DataStorage //## virtual void Remove(const mitk::DataNode* node) = 0; //##Documentation //## @brief Checks if a node exists in the DataStorage //## virtual bool Exists(const mitk::DataNode* node) const = 0; //##Documentation //## @brief Removes a set of nodes from the DataStorage //## void Remove(const mitk::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 a set of source objects for a given node that meet the given condition(s). //## virtual SetOfObjects::ConstPointer GetSources(const mitk::DataNode* node, const NodePredicateBase* condition = NULL, 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 mitk::DataNode* node, const NodePredicateBase* condition = NULL, 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 //## mitk::DataNode* GetNode(const NodePredicateBase* condition = NULL) const; //##Documentation //## @brief Convenience method to get the first node with a given name //## mitk::DataNode* GetNamedNode(const char* name) const; //##Documentation //## @brief Convenience method to get the first node with a given name //## mitk::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 //## mitk::DataNode* GetNamedDerivedNode(const char* name, const mitk::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 == NULL) return NULL; mitk::DataNode* n = this->GetNamedNode(name); if (n == NULL) return NULL; 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 mitk::DataNode* sourceNode, bool onlyDirectDerivations = true) const { if (name == NULL) return NULL; mitk::DataNode* n = this->GetNamedDerivedNode(name, sourceNode, onlyDirectDerivations); if (n == NULL) return NULL; 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 NULL //## @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 mitk::TimeSlicedGeometry::Pointer ComputeBoundingGeometry3D( const SetOfObjects* input, const char* boolPropertyKey = NULL, mitk::BaseRenderer* renderer = NULL, const char* boolPropertyKey2 = NULL); //##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 mitk::TimeSlicedGeometry::Pointer ComputeBoundingGeometry3D( const char* boolPropertyKey = NULL, mitk::BaseRenderer* renderer = NULL, const char* boolPropertyKey2 = NULL); //##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. mitk::TimeSlicedGeometry::Pointer ComputeVisibleBoundingGeometry3D( mitk::BaseRenderer* renderer = NULL, const char* boolPropertyKey = NULL); //##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 mitk::BoundingBox::Pointer ComputeBoundingBox( const char* boolPropertyKey = NULL, mitk::BaseRenderer* renderer = NULL, const char* boolPropertyKey2 = NULL); //##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. mitk::BoundingBox::Pointer ComputeVisibleBoundingBox( mitk::BaseRenderer* renderer = NULL, const char* boolPropertyKey = NULL) { 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 mitk::TimeBounds ComputeTimeBounds( const char* boolPropertyKey, mitk::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 mitk::TimeBounds ComputeTimeBounds( mitk::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 mitk::DataNode* node); //##Documentation //## @brief EmitRemoveNodeEvent emits the RemoveNodeEvent //## //## This method should be called by subclasses to emit the RemoveNodeEvent void EmitRemoveNodeEvent(const mitk::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 mitk::DataNode* _Node); //##Documentation //## @brief Removes a Modified-Listener from the given Node. void RemoveListeners(const mitk::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 virtual ~DataStorage(); //##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 virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; }; } // namespace mitk #endif /* MITKDATASTORAGE_H_HEADER_INCLUDED_ */ diff --git a/Core/Code/Interactions/mitkBindDispatcherInteractor.cpp b/Core/Code/Interactions/mitkBindDispatcherInteractor.cpp index 6a90d6ed25..eae8a2ea7d 100644 --- a/Core/Code/Interactions/mitkBindDispatcherInteractor.cpp +++ b/Core/Code/Interactions/mitkBindDispatcherInteractor.cpp @@ -1,110 +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 "mitkBindDispatcherInteractor.h" #include "mitkMessage.h" #include // us #include "mitkGetModuleContext.h" #include "mitkModule.h" #include "mitkModuleRegistry.h" mitk::BindDispatcherInteractor::BindDispatcherInteractor() : m_DataStorage(NULL) { ModuleContext* context = ModuleRegistry::GetModule(1)->GetModuleContext(); if (context == NULL) { MITK_ERROR<< "BindDispatcherInteractor() - Context could not be obtained."; return; } m_Dispatcher = Dispatcher::New(); } void mitk::BindDispatcherInteractor::SetDataStorage(mitk::DataStorage::Pointer dataStorage) { // Set/Change Datastorage. This registers BDI to listen for events of DataStorage, to be informed when // a DataNode with a Interactor is added/modified/removed. // clean up events from previous datastorage UnRegisterDataStorageEvents(); m_DataStorage = dataStorage; RegisterDataStorageEvents(); } mitk::BindDispatcherInteractor::~BindDispatcherInteractor() { if (m_DataStorage.IsNotNull()) { UnRegisterDataStorageEvents(); } } void mitk::BindDispatcherInteractor::RegisterInteractor(const mitk::DataNode* dataNode) { if (m_Dispatcher.IsNotNull()) { m_Dispatcher->AddDataInteractor(dataNode); } } void mitk::BindDispatcherInteractor::RegisterDataStorageEvents() { if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.AddListener( MessageDelegate1(this, &BindDispatcherInteractor::RegisterInteractor)); + m_DataStorage->RemoveNodeEvent.AddListener( MessageDelegate1(this, &BindDispatcherInteractor::UnRegisterInteractor)); - m_DataStorage->ChangedNodeEvent.AddListener( + + m_DataStorage->InteractorChangedNodeEvent.AddListener( MessageDelegate1(this, &BindDispatcherInteractor::RegisterInteractor)); } } void mitk::BindDispatcherInteractor::UnRegisterInteractor(const DataNode* dataNode) { if (m_Dispatcher.IsNotNull()) { m_Dispatcher->RemoveDataInteractor(dataNode); } } mitk::Dispatcher::Pointer mitk::BindDispatcherInteractor::GetDispatcher() const { return m_Dispatcher; } void mitk::BindDispatcherInteractor::SetDispatcher(Dispatcher::Pointer dispatcher) { m_Dispatcher = dispatcher; } void mitk::BindDispatcherInteractor::UnRegisterDataStorageEvents() { if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1(this, &BindDispatcherInteractor::RegisterInteractor)); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1(this, &BindDispatcherInteractor::UnRegisterInteractor)); m_DataStorage->ChangedNodeEvent.RemoveListener( MessageDelegate1(this, &BindDispatcherInteractor::RegisterInteractor)); } } diff --git a/Core/Code/Interactions/mitkDispatcher.cpp b/Core/Code/Interactions/mitkDispatcher.cpp index ba928ba780..90d899a3c9 100644 --- a/Core/Code/Interactions/mitkDispatcher.cpp +++ b/Core/Code/Interactions/mitkDispatcher.cpp @@ -1,231 +1,235 @@ /*=================================================================== 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 "mitkDispatcher.h" #include "mitkInteractionEvent.h" #include "mitkInternalEvent.h" // MicroServices #include "mitkGetModuleContext.h" #include "mitkModule.h" #include "mitkModuleRegistry.h" #include "mitkInteractionEventObserver.h" mitk::Dispatcher::Dispatcher() : m_ProcessingMode(REGULAR) { m_EventObserverTracker = new mitk::ServiceTracker(GetModuleContext()); m_EventObserverTracker->Open(); } void mitk::Dispatcher::AddDataInteractor(const DataNode* dataNode) { RemoveDataInteractor(dataNode); RemoveOrphanedInteractors(); DataInteractor::Pointer dataInteractor = dataNode->GetDataInteractor(); if (dataInteractor.IsNotNull()) { m_Interactors.push_back(dataInteractor); } } /* * Note: One DataInteractor can only have one DataNode and vice versa, * BUT the m_Interactors list may contain another DataInteractor that is still connected to this DataNode, * in this case we have to remove >1 DataInteractor. (Some special case of switching DataNodes between DataInteractors and registering a * DataNode to a DataStorage after assigning it to an DataInteractor) */ void mitk::Dispatcher::RemoveDataInteractor(const DataNode* dataNode) { for (ListInteractorType::iterator it = m_Interactors.begin(); it != m_Interactors.end();) { if ((*it)->GetDataNode() == dataNode) { it = m_Interactors.erase(it); } else { ++it; } } } size_t mitk::Dispatcher::GetNumberOfInteractors() { return m_Interactors.size(); } mitk::Dispatcher::~Dispatcher() { m_EventObserverTracker->Close(); delete m_EventObserverTracker; + + m_Interactors.clear(); } bool mitk::Dispatcher::ProcessEvent(InteractionEvent* event) { InteractionEvent::Pointer p = event; //MITK_INFO << event->GetEventClass(); bool eventIsHandled = false; /* Filter out and handle Internal Events separately */ InternalEvent* internalEvent = dynamic_cast(event); if (internalEvent != NULL) { eventIsHandled = HandleInternalEvent(internalEvent); // InternalEvents that are handled are not sent to the listeners if (eventIsHandled) { return true; } } switch (m_ProcessingMode) { case CONNECTEDMOUSEACTION: // finished connected mouse action if (std::strcmp(p->GetNameOfClass(), "MouseReleaseEvent") == 0) { m_ProcessingMode = REGULAR; eventIsHandled = m_SelectedInteractor->HandleEvent(event, m_SelectedInteractor->GetDataNode()); } // give event to selected interactor if (eventIsHandled == false) { eventIsHandled = m_SelectedInteractor->HandleEvent(event, m_SelectedInteractor->GetDataNode()); } break; case GRABINPUT: eventIsHandled = m_SelectedInteractor->HandleEvent(event, m_SelectedInteractor->GetDataNode()); SetEventProcessingMode(m_SelectedInteractor); break; case PREFERINPUT: if (m_SelectedInteractor->HandleEvent(event, m_SelectedInteractor->GetDataNode()) == true) { SetEventProcessingMode(m_SelectedInteractor); eventIsHandled = true; } break; case REGULAR: break; } // Standard behavior. Is executed in STANDARD mode and PREFERINPUT mode, if preferred interactor rejects event. if (m_ProcessingMode == REGULAR || (m_ProcessingMode == PREFERINPUT && eventIsHandled == false)) { - m_Interactors.sort(cmp()); // sorts interactors by layer (descending); - for (std::list::iterator it = m_Interactors.begin(); it != m_Interactors.end(); ++it) + + // copy the list to prevent iterator invalidation as executing actions + // in HandleEvent() can cause the m_Interactors list to be updated + std::list tmpInteractorList( m_Interactors ); + std::list::iterator it; + for ( it=tmpInteractorList.begin(); it!=tmpInteractorList.end(); it++ ) { - // explicit copy of pointer because HandleEvent function causes the m_Interactors list to be updated, - // which in turn invalidates the iterator. DataInteractor::Pointer dataInteractor = *it; - if (dataInteractor->HandleEvent(event, dataInteractor->GetDataNode())) + if ( (*it)->HandleEvent(event, dataInteractor->GetDataNode()) ) { // if an event is handled several properties are checked, in order to determine the processing mode of the dispatcher SetEventProcessingMode(dataInteractor); if (std::strcmp(p->GetNameOfClass(), "MousePressEvent") == 0 && m_ProcessingMode == REGULAR) { m_SelectedInteractor = dataInteractor; m_ProcessingMode = CONNECTEDMOUSEACTION; } eventIsHandled = true; break; } } } /* Notify InteractionEventObserver */ std::list listEventObserver; m_EventObserverTracker->GetServiceReferences(listEventObserver); for (std::list::iterator it = listEventObserver.begin(); it != listEventObserver.end(); ++it) { InteractionEventObserver* interactionEventObserver = m_EventObserverTracker->GetService(*it); if (interactionEventObserver != NULL) { if (interactionEventObserver->IsEnabled()) { interactionEventObserver->Notify(event, eventIsHandled); } } } // Process event queue if (!m_QueuedEvents.empty()) { InteractionEvent::Pointer e = m_QueuedEvents.front(); m_QueuedEvents.pop_front(); ProcessEvent(e); } return eventIsHandled; } /* * Checks if DataNodes associated with DataInteractors point back to them. * If not remove the DataInteractors. (This can happen when s.o. tries to set DataNodes to multiple DataInteractors) */ void mitk::Dispatcher::RemoveOrphanedInteractors() { for (ListInteractorType::iterator it = m_Interactors.begin(); it != m_Interactors.end();) { DataNode::Pointer dn = (*it)->GetDataNode(); if (dn.IsNull()) { it = m_Interactors.erase(it); } else { DataInteractor::Pointer interactor = dn->GetDataInteractor(); if (interactor != it->GetPointer()) { it = m_Interactors.erase(it); } else { ++it; } } } } void mitk::Dispatcher::QueueEvent(InteractionEvent* event) { m_QueuedEvents.push_back(event); } void mitk::Dispatcher::SetEventProcessingMode(DataInteractor::Pointer dataInteractor) { m_ProcessingMode = dataInteractor->GetMode(); if (dataInteractor->GetMode() != REGULAR) { m_SelectedInteractor = dataInteractor; } } bool mitk::Dispatcher::HandleInternalEvent(InternalEvent* internalEvent) { if (internalEvent->GetSignalName() == DataInteractor::IntDeactivateMe && internalEvent->GetTargetInteractor() != NULL) { internalEvent->GetTargetInteractor()->GetDataNode()->SetDataInteractor(NULL); internalEvent->GetTargetInteractor()->SetDataNode(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } return false; }