diff --git a/Core/Code/Interactions/mitkEventConfig.cpp b/Core/Code/Interactions/mitkEventConfig.cpp index e815b18519..a7d87e81ee 100755 --- a/Core/Code/Interactions/mitkEventConfig.cpp +++ b/Core/Code/Interactions/mitkEventConfig.cpp @@ -1,302 +1,358 @@ /*=================================================================== 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 "mitkEventConfig.h" #include "mitkEventFactory.h" #include "mitkInteractionEvent.h" #include "mitkInternalEvent.h" #include "mitkInteractionKeyEvent.h" #include "mitkInteractionEventConst.h" // VTK #include #include // us +#include "mitkGetModuleContext.h" #include "mitkModule.h" #include "mitkModuleResource.h" #include "mitkModuleResourceStream.h" -#include "mitkModuleRegistry.h" namespace mitk { class EventConfigXMLParser : public vtkXMLParser { public: EventConfigXMLParser(EventConfigPrivate* d); protected: /** * @brief Derived from XMLReader **/ void StartElement(const char* elementName, const char **atts); /** * @brief Derived from XMLReader **/ void EndElement(const char* elementName); std::string ReadXMLStringAttribute(const std::string& name, const char** atts); bool ReadXMLBooleanAttribute(const std::string& name, const char** atts); private: EventConfigPrivate* const d; }; struct EventConfigPrivate : public SharedData { EventConfigPrivate(); EventConfigPrivate(const EventConfigPrivate& other); struct EventMapping { std::string variantName; InteractionEvent::ConstPointer interactionEvent; }; /** * Checks if mapping with the same parameters already exists, if so, it is replaced, * else the new mapping added */ void InsertMapping(const EventMapping& mapping); /** * @brief List of all global properties of the config object. */ PropertyList::Pointer m_PropertyList; /** * @brief Temporal list of all properties of a Event. Used to parse an Input-Event and collect all parameters between the two * and tags. */ PropertyList::Pointer m_EventPropertyList; EventMapping m_CurrEventMapping; typedef std::list EventListType; /** * Stores InteractionEvents and their corresponding VariantName */ EventListType m_EventList; bool m_Errors; // use member, because of inheritance from vtkXMLParser we can't return a success value for parsing the file. EventConfigXMLParser m_XmlParser; }; } mitk::EventConfigPrivate::EventConfigPrivate() : m_PropertyList(PropertyList::New()) , m_Errors(false) , m_XmlParser(this) { + // Avoid VTK warning: Trying to delete object with non-zero reference count. + m_XmlParser.SetReferenceCount(0); } mitk::EventConfigPrivate::EventConfigPrivate(const EventConfigPrivate& other) : SharedData(other) , m_PropertyList(other.m_PropertyList->Clone()) , m_EventPropertyList(other.m_EventPropertyList->Clone()) , m_CurrEventMapping(other.m_CurrEventMapping) , m_EventList(other.m_EventList) , m_Errors(other.m_Errors) , m_XmlParser(this) { + // Avoid VTK warning: Trying to delete object with non-zero reference count. + m_XmlParser.SetReferenceCount(0); } void mitk::EventConfigPrivate::InsertMapping(const EventMapping& mapping) { for (EventListType::iterator it = m_EventList.begin(); it != m_EventList.end(); ++it) { if (*(it->interactionEvent) == *mapping.interactionEvent) { //MITK_INFO<< "Configuration overwritten:" << (*it).variantName; m_EventList.erase(it); break; } } m_EventList.push_back(mapping); } mitk::EventConfigXMLParser::EventConfigXMLParser(EventConfigPrivate *d) : d(d) { } void mitk::EventConfigXMLParser::StartElement(const char* elementName, const char **atts) { std::string name(elementName); if (name == InteractionEventConst::xmlTagConfigRoot) { // } else if (name == InteractionEventConst::xmlTagParam) { std::string name = ReadXMLStringAttribute(InteractionEventConst::xmlParameterName, atts); std::string value = ReadXMLStringAttribute(InteractionEventConst::xmlParameterValue, atts); d->m_PropertyList->SetStringProperty(name.c_str(), value.c_str()); } else if (name == InteractionEventConst::xmlTagEventVariant) { std::string eventClass = ReadXMLStringAttribute(InteractionEventConst::xmlParameterEventClass, atts); std::string eventVariant = ReadXMLStringAttribute(InteractionEventConst::xmlParameterName, atts); // New list in which all parameters are stored that are given within the tag d->m_EventPropertyList = PropertyList::New(); d->m_EventPropertyList->SetStringProperty(InteractionEventConst::xmlParameterEventClass.c_str(), eventClass.c_str()); d->m_EventPropertyList->SetStringProperty(InteractionEventConst::xmlParameterEventVariant.c_str(), eventVariant.c_str()); d->m_CurrEventMapping.variantName = eventVariant; } else if (name == InteractionEventConst::xmlTagAttribute) { // Attributes that describe an Input Event, such as which MouseButton triggered the event,or which modifier keys are pressed std::string name = ReadXMLStringAttribute(InteractionEventConst::xmlParameterName, atts); std::string value = ReadXMLStringAttribute(InteractionEventConst::xmlParameterValue, atts); d->m_EventPropertyList->SetStringProperty(name.c_str(), value.c_str()); } } void mitk::EventConfigXMLParser::EndElement(const char* elementName) { std::string name(elementName); // At end of input section, all necessary infos are collected to created an interaction event. if (name == InteractionEventConst::xmlTagEventVariant) { InteractionEvent::Pointer event = EventFactory::CreateEvent(d->m_EventPropertyList); if (event.IsNotNull()) { d->m_CurrEventMapping.interactionEvent = event; d->InsertMapping(d->m_CurrEventMapping); } else { MITK_WARN<< "EventConfig: Unknown Event-Type in config. Entry skipped: " << name; } } } std::string mitk::EventConfigXMLParser::ReadXMLStringAttribute(const std::string& name, const char** atts) { if (atts) { const char** attsIter = atts; while (*attsIter) { if (name == *attsIter) { attsIter++; return *attsIter; } attsIter += 2; } } return std::string(); } bool mitk::EventConfigXMLParser::ReadXMLBooleanAttribute(const std::string& name, const char** atts) { std::string s = ReadXMLStringAttribute(name, atts); std::transform(s.begin(), s.end(), s.begin(), ::toupper); return s == "TRUE"; } mitk::EventConfig::EventConfig() : d(new EventConfigPrivate) { } mitk::EventConfig::EventConfig(const EventConfig &other) : d(other.d) { } +mitk::EventConfig::EventConfig(const std::string& filename, const Module* module) + : d(new EventConfigPrivate) +{ + if (module == NULL) + { + module = GetModuleContext()->GetModule(); + } + mitk::ModuleResource resource = module->GetResource("Interactions/" + filename); + if (!resource.IsValid()) + { + MITK_ERROR << "Resource not valid. State machine pattern in module " << module->GetName() + << " not found: /Interactions/" << filename; + return; + } + + EventConfig newConfig; + mitk::ModuleResourceStream stream(resource); + newConfig.d->m_XmlParser.SetStream(&stream); + bool success = newConfig.d->m_XmlParser.Parse() && !newConfig.d->m_Errors; + if (success) + { + *this = newConfig; + } +} + mitk::EventConfig& mitk::EventConfig::operator =(const mitk::EventConfig& other) { d = other.d; return *this; } mitk::EventConfig::~EventConfig() { } bool mitk::EventConfig::IsValid() const { return !d->m_EventList.empty(); } - -bool mitk::EventConfig::LoadConfig(const std::string& fileName, const std::string& moduleName) +bool mitk::EventConfig::AddConfig(const std::string& fileName, const Module* module) { - mitk::Module* module = mitk::ModuleRegistry::GetModule(moduleName); + if (module == NULL) + { + module = GetModuleContext()->GetModule(); + } mitk::ModuleResource resource = module->GetResource("Interactions/" + fileName); if (!resource.IsValid()) { - mitkThrow()<< ("Resource not valid. State machine pattern not found:" + fileName); + MITK_ERROR << "Resource not valid. State machine pattern in module " << module->GetName() + << " not found: /Interactions/" << fileName; + return false; } + + EventConfig newConfig(*this); mitk::ModuleResourceStream stream(resource); - d->m_XmlParser.SetStream(&stream); - return d->m_XmlParser.Parse() && !d->m_Errors; + newConfig.d->m_XmlParser.SetStream(&stream); + bool success = newConfig.d->m_XmlParser.Parse() && !newConfig.d->m_Errors; + if (success) + { + *this = newConfig; + } + return success; +} + +bool mitk::EventConfig::AddConfig(const EventConfig& config) +{ + if (!config.IsValid()) return false; + + d->m_PropertyList->ConcatenatePropertyList(config.d->m_PropertyList->Clone(), true); + d->m_EventPropertyList = config.d->m_EventPropertyList->Clone(); + d->m_CurrEventMapping = config.d->m_CurrEventMapping; + d->InsertMapping(config.d->m_CurrEventMapping); + return true; } mitk::PropertyList::Pointer mitk::EventConfig::GetAttributes() const { return d->m_PropertyList; } std::string mitk::EventConfig::GetMappedEvent(const EventType& interactionEvent) const { // internal events are excluded from mapping if (std::strcmp(interactionEvent->GetNameOfClass(), "InternalEvent") == 0) { InternalEvent* internalEvent = dynamic_cast(interactionEvent.GetPointer()); return internalEvent->GetSignalName(); } for (EventConfigPrivate::EventListType::const_iterator it = d->m_EventList.begin(); it != d->m_EventList.end(); ++it) { if (*(it->interactionEvent) == *interactionEvent) { return (*it).variantName; } } // if this part is reached, no mapping has been found, // so here we handle key events and map a key event to the string "Std" + letter/code // so "A" will be returned as "StdA" if (std::strcmp(interactionEvent->GetNameOfClass(), "InteractionKeyEvent") == 0) { InteractionKeyEvent* keyEvent = dynamic_cast(interactionEvent.GetPointer()); return ("Std" + keyEvent->GetKey()); } return ""; } void mitk::EventConfig::ClearConfig() { + d->m_PropertyList->Clear(); + d->m_EventPropertyList->Clear(); + d->m_CurrEventMapping.variantName.clear(); + d->m_CurrEventMapping.interactionEvent = NULL; d->m_EventList.clear(); + d->m_Errors = false; } diff --git a/Core/Code/Interactions/mitkEventConfig.h b/Core/Code/Interactions/mitkEventConfig.h index b7ebdae1e1..a6d8339c79 100755 --- a/Core/Code/Interactions/mitkEventConfig.h +++ b/Core/Code/Interactions/mitkEventConfig.h @@ -1,102 +1,138 @@ /*=================================================================== 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 mitkStateMachineConfig_h #define mitkStateMachineConfig_h #include #include "mitkSharedData.h" #include "mitkPropertyList.h" #include "itkSmartPointer.h" namespace mitk { class InteractionEvent; + class Module; struct EventConfigPrivate; /** * \class EventConfig * \brief Configuration Object for Statemachines. * * Reads given config file, which translates specific user inputs (InteractionEvents) into EventVariants that can be processed * by the StateMachine. * Refer to \ref ConfigFileDescriptionSection . * * @ingroup Interaction **/ class MITK_CORE_EXPORT EventConfig { public: typedef itk::SmartPointer EventType; /** * @brief Constructs an invalid EventConfig object. * * Call LoadConfig to create a valid configuration object. */ EventConfig(); EventConfig(const EventConfig& other); + /** + * @brief Construct an EventConfig object based on a XML configuration file. + * + * Uses the specified resource file containing an XML event configuration to + * construct a EventConfig object. If the resource is invalid, the created + * EventConfig object will also be invalid. + * + * @param filename The resource name relative to the Interactions resource folder. + * @param module + */ + EventConfig(const std::string& filename, const Module* module = NULL); + EventConfig& operator=(const EventConfig& other); ~EventConfig(); /** * @brief Checks wether this EventConfig object is valid. * @return Returns \c true if a configuration was successfully loaded, \c false otherwise. */ bool IsValid() const; /** - * @brief Loads XML resource + * @brief This method \e extends this configuration. + * + * The configuration from the resource provided is loaded and only the ones conflicting are replaced by the new one. + * This way several configuration files can be combined. * - * Loads a XML resource file in the given module context. - * The files have to be placed in the Resources/Interaction folder of their respective module. - **/ - bool LoadConfig(const std::string& fileName, const std::string& moduleName = "Mitk"); + * @see AddConfig(const EventConfig&) + * @see InteractionEventHandler::AddEventConfig(const std::string&, const Module*) + * + * @param filename The resource name relative to the Interactions resource folder. + * @param module The module containing the resource. Defaults to the Mitk module. + * @return \c true if the configuration was successfully added, \c false otherwise. + */ + bool AddConfig(const std::string& filename, const Module* module = NULL); + /** + * @brief This method \e extends this configuration. + * The configuration from the EventConfig object is loaded and only the ones conflicting are replaced by the new one. + * This way several configurations can be combined. + * + * @see AddConfig(const std::string&, const Module*) + * @see InteractionEventHandler::AddEventConfig(const EventConfig&) + * + * @param config The EventConfig object whose configuration should be added. + * @return \c true if the configuration was successfully added, \c false otherwise. + */ + bool AddConfig(const EventConfig& config); + + /** + * @brief Reset this EventConfig object, rendering it invalid. + */ void ClearConfig(); /** * Returns a PropertyList that contains the properties set in the configuration file. * All properties are stored as strings. */ PropertyList::Pointer GetAttributes() const; /** * Checks if the config object has a definition for the given event. If it has, the corresponding variant name is returned, else * an empty string is returned. - * \note mitk::InternalEvents are handled differently. Their signal name is returned as event variant. So there is no need + * \note mitk::InternalEvent is handled differently. Their signal name is returned as event variant. So there is no need * to configure them in a config file. - * \note mitk::InteractionKeys may have a defined event variant, if this is the case, this function returns it. If no + * \note mitk::InteractionKeyEvent may have a defined event variant, if this is the case, this function returns it. If no * such definition is found key events are mapped to Std + Key , so an 'A' will be return as 'StdA' . */ std::string GetMappedEvent(const EventType& interactionEvent) const; private: SharedDataPointer d; }; } // namespace mitk #endif /* mitkStateMachineConfig_h */ diff --git a/Core/Code/Interactions/mitkEventStateMachine.cpp b/Core/Code/Interactions/mitkEventStateMachine.cpp index d08b54c4ee..530433bb8b 100644 --- a/Core/Code/Interactions/mitkEventStateMachine.cpp +++ b/Core/Code/Interactions/mitkEventStateMachine.cpp @@ -1,189 +1,189 @@ /*=================================================================== 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 "mitkEventStateMachine.h" #include "mitkStateMachineContainer.h" #include "mitkInteractionEvent.h" #include "mitkStateMachineAction.h" #include "mitkStateMachineTransition.h" #include "mitkStateMachineState.h" // us #include "mitkModule.h" #include "mitkModuleResource.h" #include "mitkModuleResourceStream.h" #include "mitkModuleRegistry.h" mitk::EventStateMachine::EventStateMachine() : m_StateMachineContainer(NULL), m_CurrentState(NULL) { } -bool mitk::EventStateMachine::LoadStateMachine(const std::string& filename, const std::string& moduleName) +bool mitk::EventStateMachine::LoadStateMachine(const std::string& filename, const Module* module) { if (m_StateMachineContainer != NULL) { m_StateMachineContainer->Delete(); } m_StateMachineContainer = StateMachineContainer::New(); - if (m_StateMachineContainer->LoadBehavior(filename, moduleName)) + if (m_StateMachineContainer->LoadBehavior(filename, module)) { m_CurrentState = m_StateMachineContainer->GetStartState(); // clear actions map ,and connect all actions as declared in sub-class for(std::map::iterator i = m_ActionFunctionsMap.begin(); i != m_ActionFunctionsMap.end(); ++i) { delete i->second; } m_ActionFunctionsMap.clear(); for(ActionDelegatesMapType::iterator i = m_ActionDelegatesMap.begin(); i != m_ActionDelegatesMap.end(); ++i) { delete i->second; } m_ActionDelegatesMap.clear(); ConnectActionsAndFunctions(); return true; } else { MITK_WARN<< "Unable to load StateMachine from file: " << filename; return false; } } mitk::EventStateMachine::~EventStateMachine() { if (m_StateMachineContainer != NULL) { m_StateMachineContainer->Delete(); } } void mitk::EventStateMachine::AddActionFunction(const std::string& action, mitk::TActionFunctor* functor) { if (!functor) return; // make sure double calls for same action won't cause memory leaks delete m_ActionFunctionsMap[action]; ActionDelegatesMapType::iterator i = m_ActionDelegatesMap.find(action); if (i != m_ActionDelegatesMap.end()) { delete i->second; m_ActionDelegatesMap.erase(i); } m_ActionFunctionsMap[action] = functor; } void mitk::EventStateMachine::AddActionFunction(const std::string& action, const ActionFunctionDelegate& delegate) { std::map::iterator i = m_ActionFunctionsMap.find(action); if (i != m_ActionFunctionsMap.end()) { delete i->second; m_ActionFunctionsMap.erase(i); } delete m_ActionDelegatesMap[action]; m_ActionDelegatesMap[action] = delegate.Clone(); } bool mitk::EventStateMachine::HandleEvent(InteractionEvent* event, DataNode* dataNode) { if (!FilterEvents(event, dataNode)) { return false; } // check if the current state holds a transition that works with the given event. StateMachineTransition::Pointer transition = m_CurrentState->GetTransition(event->GetNameOfClass(), MapToEventVariant(event)); if (transition.IsNotNull()) { // iterate over all actions in this transition and execute them ActionVectorType actions = transition->GetActions(); bool success = false; for (ActionVectorType::iterator it = actions.begin(); it != actions.end(); ++it) { success |= ExecuteAction(*it, event); // treat an event as handled if at least one of the actions is executed successfully } if (success || actions.empty()) // an empty action list is always successful { // perform state change m_CurrentState = transition->GetNextState(); //MITK_INFO<< "StateChange: " << m_CurrentState->GetName(); } return success; } else { return false; // no transition found that matches event } } void mitk::EventStateMachine::ConnectActionsAndFunctions() { MITK_WARN<< "ConnectActionsAndFunctions in DataInteractor not implemented.\n DataInteractor will not be able to process any events."; } bool mitk::EventStateMachine::ExecuteAction(StateMachineAction* action, InteractionEvent* event) { if (action == NULL) { return false; } bool retVal = false; // Maps Action-Name to Functor and executes the Functor. ActionDelegatesMapType::iterator delegateIter = m_ActionDelegatesMap.find(action->GetActionName()); if (delegateIter != m_ActionDelegatesMap.end()) { retVal = delegateIter->second->Execute(action, event); } else { // try the legacy system std::map::iterator functionIter = m_ActionFunctionsMap.find(action->GetActionName()); if (functionIter != m_ActionFunctionsMap.end()) { retVal = functionIter->second->DoAction(action, event); } } return retVal; } mitk::StateMachineState* mitk::EventStateMachine::GetCurrentState() const { return m_CurrentState.GetPointer(); } bool mitk::EventStateMachine::FilterEvents(InteractionEvent* interactionEvent, DataNode* dataNode) { if (dataNode == NULL) { MITK_WARN<< "EventStateMachine: Empty DataNode received along with this Event " << interactionEvent; return false; } bool visible = false; if (dataNode->GetPropertyList()->GetBoolProperty("visible", visible) == false) { //property doesn't exist return false; } return visible; } diff --git a/Core/Code/Interactions/mitkEventStateMachine.h b/Core/Code/Interactions/mitkEventStateMachine.h index a198ad53db..480a414e61 100644 --- a/Core/Code/Interactions/mitkEventStateMachine.h +++ b/Core/Code/Interactions/mitkEventStateMachine.h @@ -1,189 +1,190 @@ /*=================================================================== 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 MITKEVENTSTATEMACHINE_H_ #define MITKEVENTSTATEMACHINE_H_ #include "itkObject.h" #include "itkObjectFactory.h" #include "mitkCommon.h" #include "mitkMessage.h" #include "mitkInteractionEventHandler.h" #include #include namespace mitk { class StateMachineContainer; class StateMachineAction; class InteractionEvent; class StateMachineState; class DataNode; + class Module; /** * \class TActionFunctor * \brief Base class of ActionFunctors, to provide an easy to connect actions with functions. * * \deprecatedSince{2013_03} Use mitk::Message classes instead. */ class TActionFunctor { public: virtual bool DoAction(StateMachineAction*, InteractionEvent*)=0; virtual ~TActionFunctor() { } }; /** * \class TSpecificActionFunctor * Specific implementation of ActionFunctor class, implements a reference to the function which is to be executed. It takes two arguments: * StateMachineAction - the action by which the function call is invoked, InteractionEvent - the event that caused the transition. */ template class DEPRECATED() TSpecificActionFunctor : public TActionFunctor { public: TSpecificActionFunctor(T* object, bool (T::*memberFunctionPointer)(StateMachineAction*, InteractionEvent*)) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } virtual ~TSpecificActionFunctor() { } virtual bool DoAction(StateMachineAction* action, InteractionEvent* event) { return (*m_Object.*m_MemberFunctionPointer)(action, event);// executes member function } private: T* m_Object; bool (T::*m_MemberFunctionPointer)(StateMachineAction*, InteractionEvent*); }; /** Macro that can be used to connect a StateMachineAction with a function. * It assumes that there is a typedef Classname Self in classes that use this macro, as is provided by e.g. mitkClassMacro */ #define CONNECT_FUNCTION(a, f) \ EventStateMachine::AddActionFunction(a, MessageDelegate2(this, &Self::f)); /** * \class EventStateMachine * * \brief Super-class that provides the functionality of a StateMachine to DataInteractors. * * A state machine is created by loading a state machine pattern. It consists of states, transitions and action. * The state represent the current status of the interaction, transitions are means to switch between states. Each transition * is triggered by an event and it is associated with actions that are to be executed when the state change is performed. * */ class MITK_CORE_EXPORT EventStateMachine : public mitk::InteractionEventHandler { public: mitkClassMacro(EventStateMachine, InteractionEventHandler) itkNewMacro(Self) typedef std::map DEPRECATED(ActionFunctionsMapType); typedef itk::SmartPointer StateMachineStateType; /** * @brief Loads XML resource * - * Loads a XML resource file in the given module context. + * Loads a XML resource file from the given module. * Default is the Mitk module (core). * The files have to be placed in the Resources/Interaction folder of their respective module. **/ - bool LoadStateMachine(const std::string& filename, const std::string& moduleName="Mitk"); + bool LoadStateMachine(const std::string& filename, const Module* module = NULL); /** * Receives Event from Dispatcher. * Event is mapped using the EventConfig Object to a variant, then it is checked if the StateMachine is listening for * such an Event. If this is the case, the transition to the next state it performed and all actions associated with the transition executed, * and true is returned to the caller. * If the StateMachine can't handle this event false is returned. * Attention: * If a transition is associated with multiple actions - "true" is returned if one action returns true, * and the event is treated as HANDLED even though some actions might not have been executed! So be sure that all actions that occur within * one transitions have the same conditions. */ bool HandleEvent(InteractionEvent* event, DataNode* dataNode); protected: EventStateMachine(); virtual ~EventStateMachine(); typedef MessageAbstractDelegate2 ActionFunctionDelegate; /** * Connects action from StateMachine (String in XML file) with a function that is called when this action is to be executed. */ DEPRECATED(void AddActionFunction(const std::string& action, TActionFunctor* functor)); void AddActionFunction(const std::string& action, const ActionFunctionDelegate& delegate); StateMachineState* GetCurrentState() const; /** * Is called after loading a statemachine. * Overwrite this function in specific interactor implementations. * Connect actions and functions using the CONNECT_FUNCTION macro within this function. */ virtual void ConnectActionsAndFunctions(); /** * Looks up function that is associated with action and executes it. * To implement your own execution scheme overwrite this in your DataInteractor. */ virtual bool ExecuteAction(StateMachineAction* action, InteractionEvent* interactionEvent); /** * Implements filter scheme for events. * Standard implementation accepts events from 2d and 3d windows, * and rejects events if DataNode is not visible. * \return true if event is accepted, else false * * Overwrite this function to adapt for your own needs, for example to filter out events from * 3d windows like this: \code bool mitk::EventStateMachine::FilterEvents(InteractionEvent* interactionEvent, DataNode*dataNode) { return interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard2D; // only 2D mappers } \endcode * or to enforce that the interactor only reacts when the corresponding DataNode is selected in the DataManager view.. */ virtual bool FilterEvents(InteractionEvent* interactionEvent, DataNode* dataNode); private: typedef std::map ActionDelegatesMapType; StateMachineContainer* m_StateMachineContainer; // storage of all states, action, transitions on which the statemachine operates. std::map m_ActionFunctionsMap; // stores association between action string ActionDelegatesMapType m_ActionDelegatesMap; StateMachineStateType m_CurrentState; }; } /* namespace mitk */ #endif /* MITKEVENTSTATEMACHINE_H_ */ diff --git a/Core/Code/Interactions/mitkInteractionEventHandler.cpp b/Core/Code/Interactions/mitkInteractionEventHandler.cpp index 2aa5d57bf5..6d28d8da60 100644 --- a/Core/Code/Interactions/mitkInteractionEventHandler.cpp +++ b/Core/Code/Interactions/mitkInteractionEventHandler.cpp @@ -1,77 +1,118 @@ /*=================================================================== 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 "mitkInteractionEventHandler.h" #include "mitkInteractionEvent.h" mitk::InteractionEventHandler::InteractionEventHandler() : m_EventConfig() { } mitk::InteractionEventHandler::~InteractionEventHandler() { } -bool mitk::InteractionEventHandler::LoadEventConfig(const std::string& filename, const std::string& moduleName) +bool mitk::InteractionEventHandler::SetEventConfig(const std::string& filename, const Module* module) { + EventConfig newConfig(filename, module); + if (newConfig.IsValid()) + { + m_EventConfig = newConfig; + // notify sub-classes that new config is set + ConfigurationChanged(); + return true; + } + return false; +} + +bool mitk::InteractionEventHandler::SetEventConfig(const EventConfig& config) +{ + if (config.IsValid()) + { + m_EventConfig = config; + // notify sub-classes that new config is set + ConfigurationChanged(); + return true; + } + return false; +} + +mitk::EventConfig mitk::InteractionEventHandler::GetEventConfig() const +{ + return m_EventConfig; +} + +bool mitk::InteractionEventHandler::AddEventConfig(const std::string& filename, const Module* module) +{ + if (!m_EventConfig.IsValid()) + { + MITK_ERROR<< "SetEventConfig has to be called before AddEventConfig can be used."; + return false; + } // notify sub-classes that new config is set - bool success = m_EventConfig.LoadConfig(filename, moduleName); - ConfigurationChanged(); + bool success = m_EventConfig.AddConfig(filename, module); + if (success) + { + ConfigurationChanged(); + } return success; } -bool mitk::InteractionEventHandler::AddEventConfig(const std::string& filename, const std::string& moduleName) +bool mitk::InteractionEventHandler::AddEventConfig(const EventConfig& config) { if (!m_EventConfig.IsValid()) { - MITK_ERROR<< "LoadEventConfig has to be called before AddEventConfig can be used."; + MITK_ERROR<< "SetEventConfig has to be called before AddEventConfig can be used."; return false; } // notify sub-classes that new config is set - bool success = m_EventConfig.LoadConfig(filename, moduleName); - ConfigurationChanged(); + bool success = m_EventConfig.AddConfig(config); + if (success) + { + ConfigurationChanged(); + } return success; } mitk::PropertyList::Pointer mitk::InteractionEventHandler::GetAttributes() const { if (m_EventConfig.IsValid()) { return m_EventConfig.GetAttributes(); } else { MITK_ERROR << "InteractionEventHandler::GetAttributes() requested, but not configuration loaded."; return NULL; } } std::string mitk::InteractionEventHandler::MapToEventVariant(InteractionEvent* interactionEvent) { if (m_EventConfig.IsValid()) { return m_EventConfig.GetMappedEvent(interactionEvent); } else { return ""; } } void mitk::InteractionEventHandler::ConfigurationChanged() { } diff --git a/Core/Code/Interactions/mitkInteractionEventHandler.h b/Core/Code/Interactions/mitkInteractionEventHandler.h index 5a34256136..e2cc0e7273 100644 --- a/Core/Code/Interactions/mitkInteractionEventHandler.h +++ b/Core/Code/Interactions/mitkInteractionEventHandler.h @@ -1,84 +1,133 @@ /*=================================================================== 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 MITKEVENTHANDLER_H_ #define MITKEVENTHANDLER_H_ #include "itkLightObject.h" #include "itkObjectFactory.h" #include "mitkCommon.h" #include #include "mitkEventConfig.h" #include "mitkPropertyList.h" #include namespace mitk { + class Module; + /** * \class EventHandler * Serves as a base class for all objects and classes that handle mitk::InteractionEvents. * * It provides an interface to load configuration objects map of events to variant names. */ class InteractionEvent; class MITK_CORE_EXPORT InteractionEventHandler : public itk::LightObject { public: mitkClassMacro(InteractionEventHandler, itk::LightObject) itkNewMacro(Self) + + /** + * @brief Loads a configuration from an XML resource. + * + * Loads an event configuration from an XML resource file contained in the given module. + * Default is the Mitk module (core). + * The files have to be placed in the Resources/Interactions folder of their respective module. + * This method will remove all existing configuration and replaces it with the new one. + * + * @see SetEventConfig(const EventConfig&) + * + * @param filename The resource name relative to the Interactions resource folder. + * @param module The module containing the resource. Defaults to the Mitk module. + * @return \c true if the resource was successfully loaded, \c false otherwise. + */ + bool SetEventConfig(const std::string& filename, const Module* module = NULL); + + /** + * @brief Loads a configuration from an EventConfig object. + * + * Loads an event configuration from the given EventConfig object. This method will remove + * all existing configuration and replaces it with the new one. + * + * @see SetEventConfig(const std::string&, const Module*) + * + * @param config The EventConfig object containing the new configuration. + * @return \c true if the configuration was successfully loaded, \c false otherwise. + */ + bool SetEventConfig(const EventConfig& config); + /** - * @brief Loads XML resource - * - * Loads a XML resource file in the given module context. - * Default is the Mitk module (core). - * The files have to be placed in the Resources/Interaction folder of their respective module. - * This method will remove all existing configuration and replaces it with the new one. - */ - virtual bool LoadEventConfig(const std::string& filename, const std::string& moduleName = "Mitk"); + * @brief Returns the current configuration. + * @return A EventConfig object representing the current event configuration. + */ + EventConfig GetEventConfig() const; + /** - * This method EXTENDs the configuration. + * @brief This method \e extends the configuration. + * * The configuration from the resource provided is loaded and only the ones conflicting are replaced by the new one. - * This way several configuration files can be combined + * This way several configuration files can be combined. + * + * @see AddEventConfig(const EventConfig&) + * + * @param filename The resource name relative to the Interactions resource folder. + * @param module The module containing the resource. Defaults to the Mitk module. + * @return \c true if the configuration was successfully added, \c false otherwise. */ - virtual bool AddEventConfig(const std::string& filename, const std::string& moduleName = "Mitk"); + bool AddEventConfig(const std::string& filename, const Module* module = NULL); + + /** + * @brief This method \e extends the configuration. + * The configuration from the EventConfig object is loaded and only the ones conflicting are replaced by the new one. + * This way several configurations can be combined. + * + * @see AddEventConfig(const std::string&, const Module*) + * + * @param config The EventConfig object whose configuration should be added. + * @return \c true if the configuration was successfully added, \c false otherwise. + */ + bool AddEventConfig(const EventConfig& config); protected: InteractionEventHandler(); virtual ~InteractionEventHandler(); + /** * Returns a PropertyList in which the parameters defined in the config file are listed. */ PropertyList::Pointer GetAttributes() const; std::string MapToEventVariant(InteractionEvent* interactionEvent); /** * Is called whenever a new config object ist set. * Overwrite this method e.g. to initialize EventHandler with parameters in configuration file. */ virtual void ConfigurationChanged(); private: EventConfig m_EventConfig; }; } /* namespace mitk */ #endif /* MITKEVENTHANDLER_H_ */ diff --git a/Core/Code/Interactions/mitkMouseModeSwitcher.cpp b/Core/Code/Interactions/mitkMouseModeSwitcher.cpp index f7a5f24a4b..5abf241d42 100644 --- a/Core/Code/Interactions/mitkMouseModeSwitcher.cpp +++ b/Core/Code/Interactions/mitkMouseModeSwitcher.cpp @@ -1,113 +1,113 @@ /*=================================================================== 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 "mitkMouseModeSwitcher.h" // us #include "mitkGetModuleContext.h" #include "mitkModule.h" #include "mitkModuleRegistry.h" #include "mitkInteractionEventObserver.h" mitk::MouseModeSwitcher::MouseModeSwitcher() : m_ActiveInteractionScheme(MITK), m_ActiveMouseMode(MousePointer), m_CurrentObserver(NULL) { this->InitializeListeners(); this->SetInteractionScheme(m_ActiveInteractionScheme); } mitk::MouseModeSwitcher::~MouseModeSwitcher() { m_ServiceRegistration.Unregister(); } void mitk::MouseModeSwitcher::InitializeListeners() { if (m_CurrentObserver.IsNull()) { m_CurrentObserver = mitk::DisplayInteractor::New(); m_CurrentObserver->LoadStateMachine("DisplayInteraction.xml"); - m_CurrentObserver->LoadEventConfig("DisplayConfigMITK.xml"); + m_CurrentObserver->SetEventConfig("DisplayConfigMITK.xml"); // Register as listener via micro services ServiceProperties props; props["name"] = std::string("DisplayInteractor"); m_ServiceRegistration = GetModuleContext()->RegisterService( m_CurrentObserver.GetPointer(),props); } } void mitk::MouseModeSwitcher::SetInteractionScheme(InteractionScheme scheme) { switch (scheme) { case MITK: { - m_CurrentObserver->LoadEventConfig("DisplayConfigMITK.xml"); + m_CurrentObserver->SetEventConfig("DisplayConfigMITK.xml"); } break; case PACS: { - m_CurrentObserver->LoadEventConfig("DisplayConfigPACS.xml"); + m_CurrentObserver->SetEventConfig("DisplayConfigPACS.xml"); } break; } m_ActiveInteractionScheme = scheme; this->InvokeEvent(MouseModeChangedEvent()); } void mitk::MouseModeSwitcher::SelectMouseMode(MouseMode mode) { if (m_ActiveInteractionScheme != PACS) return; switch (mode) { case MousePointer: { - m_CurrentObserver->LoadEventConfig("DisplayConfigPACS.xml"); + m_CurrentObserver->SetEventConfig("DisplayConfigPACS.xml"); break; } // case 0 case Scroll: { m_CurrentObserver->AddEventConfig("DisplayConfigPACSScroll.xml"); break; } case LevelWindow: { m_CurrentObserver->AddEventConfig("DisplayConfigPACSLevelWindow.xml"); break; } case Zoom: { m_CurrentObserver->AddEventConfig("DisplayConfigPACSZoom.xml"); break; } case Pan: { m_CurrentObserver->AddEventConfig("DisplayConfigPACSPan.xml"); break; } } // end switch (mode) m_ActiveMouseMode = mode; this->InvokeEvent(MouseModeChangedEvent()); } mitk::MouseModeSwitcher::MouseMode mitk::MouseModeSwitcher::GetCurrentMouseMode() const { return m_ActiveMouseMode; } diff --git a/Core/Code/Interactions/mitkStateMachineContainer.cpp b/Core/Code/Interactions/mitkStateMachineContainer.cpp index 17d18a4971..3b224851d8 100755 --- a/Core/Code/Interactions/mitkStateMachineContainer.cpp +++ b/Core/Code/Interactions/mitkStateMachineContainer.cpp @@ -1,218 +1,221 @@ /*=================================================================== 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 "mitkStateMachineContainer.h" #include #include #include #include // us +#include "mitkGetModuleContext.h" #include "mitkModule.h" #include "mitkModuleResource.h" #include "mitkModuleResourceStream.h" -#include "mitkModuleRegistry.h" /** * @brief This class builds up all the necessary structures for a statemachine. * and stores one start-state for all built statemachines. **/ //XML StateMachine Tags const std::string NAME = "name"; const std::string CONFIG = "statemachine"; const std::string STATE = "state"; const std::string STATEMODE = "state_mode"; const std::string TRANSITION = "transition"; const std::string EVENTCLASS = "event_class"; const std::string EVENTVARIANT = "event_variant"; const std::string STARTSTATE = "startstate"; const std::string TARGET = "target"; const std::string ACTION = "action"; namespace mitk { vtkStandardNewMacro(StateMachineContainer); } mitk::StateMachineContainer::StateMachineContainer() : m_StartStateFound(false), m_errors(false) { } mitk::StateMachineContainer::~StateMachineContainer() { } /** * @brief Loads the xml file filename and generates the necessary instances. **/ -bool mitk::StateMachineContainer::LoadBehavior(std::string fileName, std::string moduleName) +bool mitk::StateMachineContainer::LoadBehavior(const std::string& fileName, const Module* module) { - mitk::Module* module = mitk::ModuleRegistry::GetModule(moduleName); + if (module == NULL) + { + module = GetModuleContext()->GetModule(); + } mitk::ModuleResource resource = module->GetResource("Interactions/" + fileName); if (!resource.IsValid() ) { mitkThrow() << ("Resource not valid. State machine pattern not found:" + fileName); } mitk::ModuleResourceStream stream(resource); this->SetStream(&stream); m_Filename = fileName; return this->Parse() && !m_errors; } mitk::StateMachineState::Pointer mitk::StateMachineContainer::GetStartState() const { return m_StartState; } /** * @brief sets the pointers in Transition (setNextState(..)) according to the extracted xml-file content **/ void mitk::StateMachineContainer::ConnectStates() { for (StateMachineCollectionType::iterator it = m_States.begin(); it != m_States.end(); ++it) { if ((*it)->ConnectTransitions(&m_States) == false) m_errors = true; } } void mitk::StateMachineContainer::StartElement(const char* elementName, const char **atts) { std::string name(elementName); if (name == CONFIG) { // } else if (name == STATE) { std::string stateName = ReadXMLStringAttribut(NAME, atts); std::transform(stateName.begin(), stateName.end(), stateName.begin(), ::toupper); std::string stateMode = ReadXMLStringAttribut(STATEMODE, atts); std::transform(stateMode.begin(), stateMode.end(), stateMode.begin(), ::toupper); bool isStartState = ReadXMLBooleanAttribut(STARTSTATE, atts); if (isStartState) { m_StartStateFound = true; } // sanitize state modes if (stateMode == "" || stateMode == "REGULAR") { stateMode = "REGULAR"; } else if (stateMode != "GRAB_INPUT" && stateMode != "PREFER_INPUT") { MITK_WARN<< "Invalid State Modus " << stateMode << ". Mode assumed to be REGULAR"; stateMode = "REGULAR"; } m_CurrState = mitk::StateMachineState::New(stateName, stateMode); if (isStartState) m_StartState = m_CurrState; } else if (name == TRANSITION) { std::string eventClass = ReadXMLStringAttribut(EVENTCLASS, atts); std::string eventVariant = ReadXMLStringAttribut(EVENTVARIANT, atts); std::string target = ReadXMLStringAttribut(TARGET, atts); std::transform(target.begin(), target.end(), target.begin(), ::toupper); mitk::StateMachineTransition::Pointer transition = mitk::StateMachineTransition::New(target, eventClass, eventVariant); if (m_CurrState) { m_CurrState->AddTransition(transition); } else { MITK_WARN<< "Malformed Statemachine Pattern. Transition has no origin. \n Will be ignored."; MITK_WARN<< "Malformed Transition details: target="<< target << ", event class:" << eventClass << ", event variant:"<< eventVariant ; } m_CurrTransition = transition; } else if (name == ACTION) { std::string actionName = ReadXMLStringAttribut(NAME, atts); mitk::StateMachineAction::Pointer action = mitk::StateMachineAction::New(actionName); if (m_CurrTransition) m_CurrTransition->AddAction(action); else MITK_WARN<< "Malformed state machine Pattern. Action without transition. \n Will be ignored."; } } void mitk::StateMachineContainer::EndElement(const char* elementName) { std::string name(elementName); if (name == CONFIG) { if (m_StartState.IsNull()) { MITK_ERROR << "State machine pattern has no start state and cannot be used: " << m_Filename; } ConnectStates(); } else if (name == TRANSITION) { m_CurrTransition = NULL; } else if (name == ACTION) { // } else if (name == STATE) { m_States.push_back(m_CurrState); m_CurrState = NULL; } } std::string mitk::StateMachineContainer::ReadXMLStringAttribut(std::string name, const char** atts) { if (atts) { const char** attsIter = atts; while (*attsIter) { if (name == *attsIter) { attsIter++; return *attsIter; } attsIter++; attsIter++; } } return std::string(); } bool mitk::StateMachineContainer::ReadXMLBooleanAttribut(std::string name, const char** atts) { std::string s = ReadXMLStringAttribut(name, atts); std::transform(s.begin(), s.end(), s.begin(), ::toupper); if (s == "TRUE") return true; else return false; } diff --git a/Core/Code/Interactions/mitkStateMachineContainer.h b/Core/Code/Interactions/mitkStateMachineContainer.h index f77e0089ed..5c3c9fc28a 100755 --- a/Core/Code/Interactions/mitkStateMachineContainer.h +++ b/Core/Code/Interactions/mitkStateMachineContainer.h @@ -1,119 +1,121 @@ /*=================================================================== 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 STATEMACHINECONTAINER_H_HEADER_INCLUDED_C19AEDDD #define STATEMACHINECONTAINER_H_HEADER_INCLUDED_C19AEDDD #include #include #include "itkObject.h" #include "itkObjectFactory.h" #include "mitkCommon.h" #include #include "mitkStateMachineState.h" #include "mitkStateMachineTransition.h" #include "mitkStateMachineAction.h" namespace mitk { + class Module; + /** *@brief * * @ingroup Interaction **/ class StateMachineContainer : public vtkXMLParser { public: static StateMachineContainer *New(); vtkTypeMacro(StateMachineContainer,vtkXMLParser); /** * @brief This type holds all states of one statemachine. **/ typedef std::vector StateMachineCollectionType; /** * @brief Returns the StartState of the StateMachine. **/ StateMachineState::Pointer GetStartState() const; /** * @brief Loads XML resource * * Loads a XML resource file in the given module context. * The files have to be placed in the Resources/Interaction folder of their respective module. **/ - bool LoadBehavior(std::string fileName , std::string moduleName = "Mitk"); + bool LoadBehavior(const std::string& fileName , const Module* module); /** * brief To enable StateMachine to access states **/ friend class InteractionStateMachine; protected: StateMachineContainer(); virtual ~StateMachineContainer(); /** * @brief Derived from XMLReader **/ void StartElement (const char* elementName, const char **atts); /** * @brief Derived from XMLReader **/ void EndElement (const char* elementName); private: /** * @brief Derived from XMLReader **/ std::string ReadXMLStringAttribut( std::string name, const char** atts); /** * @brief Derived from XMLReader **/ bool ReadXMLBooleanAttribut( std::string name, const char** atts ); /** * @brief Sets the pointers in Transition (setNextState(..)) according to the extracted xml-file content **/ void ConnectStates(); StateMachineState::Pointer m_StartState; StateMachineState::Pointer m_CurrState; StateMachineTransition::Pointer m_CurrTransition; StateMachineCollectionType m_States; bool m_StartStateFound; bool m_errors; // use member, because of inheritance from vtkXMLParser we can't return a success value for parsing the file. std::string m_Filename; // store file name for debug purposes. }; } // namespace mitk #endif /* STATEMACHINECONTAINER_H_HEADER_INCLUDED_C19AEDDD */ diff --git a/Modules/MitkExt/Controllers/mitkToolManager.cpp b/Modules/MitkExt/Controllers/mitkToolManager.cpp index 02ba2ad59b..351c284264 100644 --- a/Modules/MitkExt/Controllers/mitkToolManager.cpp +++ b/Modules/MitkExt/Controllers/mitkToolManager.cpp @@ -1,576 +1,573 @@ /*=================================================================== 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 "mitkToolManager.h" #include "mitkGlobalInteraction.h" #include "mitkCoreObjectFactory.h" #include #include #include #include "mitkInteractionEventObserver.h" #include "mitkDisplayInteractor.h" // MicroServices #include "mitkGetModuleContext.h" #include "mitkModule.h" #include "mitkModuleRegistry.h" mitk::ToolManager::ToolManager(DataStorage* storage) :m_ActiveTool(NULL), m_ActiveToolID(-1), m_RegisteredClients(0), m_DataStorage(storage) { CoreObjectFactory::GetInstance(); // to make sure a CoreObjectFactory was instantiated (and in turn, possible tools are registered) - bug 1029 // get a list of all known mitk::Tools std::list thingsThatClaimToBeATool = itk::ObjectFactoryBase::CreateAllInstance("mitkTool"); // remember these tools for ( std::list::iterator iter = thingsThatClaimToBeATool.begin(); iter != thingsThatClaimToBeATool.end(); ++iter ) { if ( Tool* tool = dynamic_cast( iter->GetPointer() ) ) { tool->SetToolManager(this); // important to call right after instantiation tool->ErrorMessage += MessageDelegate1( this, &ToolManager::OnToolErrorMessage ); tool->GeneralMessage += MessageDelegate1( this, &ToolManager::OnGeneralToolMessage ); m_Tools.push_back( tool ); } } //ActivateTool(0); // first one is default } mitk::ToolManager::~ToolManager() { for (DataVectorType::iterator dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter) (*dataIter)->RemoveObserver(m_WorkingDataObserverTags[(*dataIter)]); if(this->GetDataStorage() != NULL) this->GetDataStorage()->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1 ( this, &ToolManager::OnNodeRemoved )); if (m_ActiveTool) { m_ActiveTool->Deactivated(); GlobalInteraction::GetInstance()->RemoveListener( m_ActiveTool ); m_ActiveTool = NULL; m_ActiveToolID = -1; // no tool active ActiveToolChanged.Send(); } for ( NodeTagMapType::iterator observerTagMapIter = m_ReferenceDataObserverTags.begin(); observerTagMapIter != m_ReferenceDataObserverTags.end(); ++observerTagMapIter ) { observerTagMapIter->first->RemoveObserver( observerTagMapIter->second ); } } void mitk::ToolManager::OnToolErrorMessage(std::string s) { this->ToolErrorMessage(s); } void mitk::ToolManager::OnGeneralToolMessage(std::string s) { this->GeneralToolMessage(s); } const mitk::ToolManager::ToolVectorTypeConst mitk::ToolManager::GetTools() { ToolVectorTypeConst resultList; for ( ToolVectorType::iterator iter = m_Tools.begin(); iter != m_Tools.end(); ++iter ) { resultList.push_back( iter->GetPointer() ); } return resultList; } mitk::Tool* mitk::ToolManager::GetToolById(int id) { try { return m_Tools.at(id); } catch(std::exception&) { return NULL; } } bool mitk::ToolManager::ActivateTool(int id) { if(this->GetDataStorage()) { this->GetDataStorage()->RemoveNodeEvent.AddListener( mitk::MessageDelegate1 ( this, &ToolManager::OnNodeRemoved ) ); } //MITK_INFO << "ToolManager::ActivateTool("<SetEventNotificationPolicy(GlobalInteraction::INFORM_MULTIPLE); + // Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools // in new interaction framework - std::list listEventObserver; - ServiceTracker* eventObserverTracker= new ServiceTracker(GetModuleContext()); - eventObserverTracker->Open(); - eventObserverTracker->GetServiceReferences(listEventObserver); // query all registered interaction event observer - for (std::list::iterator it = listEventObserver.begin(); it != listEventObserver.end(); ++it) + for (std::map::iterator it = m_DisplayInteractorConfigs.begin(); + it != m_DisplayInteractorConfigs.end(); ++it) { - InteractionEventObserver* interactionEventObserver = eventObserverTracker->GetService(*it); - if (interactionEventObserver != NULL) { - DisplayInteractor* displayInteractor = dynamic_cast(interactionEventObserver); - if (displayInteractor != NULL) { + if (it->first) + { + DisplayInteractor* displayInteractor = static_cast( + GetModuleContext()->GetService(it->first)); + if (displayInteractor != NULL) + { // here the regular configuration is loaded again - displayInteractor->LoadEventConfig("DisplayConfigMITK.xml","Mitk"); + displayInteractor->SetEventConfig(it->second); } } } - eventObserverTracker->Close(); - delete eventObserverTracker; + m_DisplayInteractorConfigs.clear(); } if ( GetToolById( id ) == m_ActiveTool ) return true; // no change needed static int nextTool = -1; nextTool = id; //MITK_INFO << "ToolManager::ActivateTool("<Deactivated(); GlobalInteraction::GetInstance()->RemoveListener( m_ActiveTool ); } m_ActiveTool = GetToolById( nextTool ); m_ActiveToolID = m_ActiveTool ? nextTool : -1; // current ID if tool is valid, otherwise -1 ActiveToolChanged.Send(); if (m_ActiveTool) { if (m_RegisteredClients > 0) { m_ActiveTool->Activated(); GlobalInteraction::GetInstance()->AddListener( m_ActiveTool ); //If a tool is activated set event notification policy to one GlobalInteraction::GetInstance()->SetEventNotificationPolicy(GlobalInteraction::INFORM_ONE); + // As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts with tools // Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction will still be enabled - std::list listEventObserver; - ServiceTracker* eventObserverTracker= new ServiceTracker(GetModuleContext()); - eventObserverTracker->Open(); - eventObserverTracker->GetServiceReferences(listEventObserver); + m_DisplayInteractorConfigs.clear(); + std::list listEventObserver = GetModuleContext()->GetServiceReferences(); for (std::list::iterator it = listEventObserver.begin(); it != listEventObserver.end(); ++it) { - InteractionEventObserver* interactionEventObserver = eventObserverTracker->GetService(*it); - if (interactionEventObserver != NULL) { - DisplayInteractor* displayInteractor = dynamic_cast(interactionEventObserver); - if (displayInteractor != NULL) { - // here the regular configuration is loaded again - displayInteractor->LoadEventConfig("Legacy/DisplayConfigMITKTools.xml","Mitk"); - } + DisplayInteractor* displayInteractor = dynamic_cast( + GetModuleContext()->GetService(*it)); + if (displayInteractor != NULL) + { + // remember the original configuration + m_DisplayInteractorConfigs.insert(std::make_pair(*it, displayInteractor->GetEventConfig())); + // here the alternative configuration is loaded + displayInteractor->SetEventConfig("Legacy/DisplayConfigMITKTools.xml"); } } - eventObserverTracker->Close(); - delete eventObserverTracker; } } } inActivateTool = false; return (m_ActiveTool != NULL); } void mitk::ToolManager::SetReferenceData(DataVectorType data) { if (data != m_ReferenceData) { // remove observers from old nodes for ( DataVectorType::iterator dataIter = m_ReferenceData.begin(); dataIter != m_ReferenceData.end(); ++dataIter ) { NodeTagMapType::iterator searchIter = m_ReferenceDataObserverTags.find( *dataIter ); if ( searchIter != m_ReferenceDataObserverTags.end() ) { //MITK_INFO << "Stopping observation of " << (void*)(*dataIter) << std::endl; (*dataIter)->RemoveObserver( searchIter->second ); } } m_ReferenceData = data; // TODO tell active tool? // attach new observers m_ReferenceDataObserverTags.clear(); for ( DataVectorType::iterator dataIter = m_ReferenceData.begin(); dataIter != m_ReferenceData.end(); ++dataIter ) { //MITK_INFO << "Observing " << (void*)(*dataIter) << std::endl; itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &ToolManager::OnOneOfTheReferenceDataDeleted ); command->SetCallbackFunction( this, &ToolManager::OnOneOfTheReferenceDataDeletedConst ); m_ReferenceDataObserverTags.insert( std::pair( (*dataIter), (*dataIter)->AddObserver( itk::DeleteEvent(), command ) ) ); } ReferenceDataChanged.Send(); } } void mitk::ToolManager::OnOneOfTheReferenceDataDeletedConst(const itk::Object* caller, const itk::EventObject& e) { OnOneOfTheReferenceDataDeleted( const_cast(caller), e ); } void mitk::ToolManager::OnOneOfTheReferenceDataDeleted(itk::Object* caller, const itk::EventObject& itkNotUsed(e)) { //MITK_INFO << "Deleted: " << (void*)caller << " Removing from reference data list." << std::endl; DataVectorType v; for (DataVectorType::iterator dataIter = m_ReferenceData.begin(); dataIter != m_ReferenceData.end(); ++dataIter ) { //MITK_INFO << " In list: " << (void*)(*dataIter); if ( (void*)(*dataIter) != (void*)caller ) { v.push_back( *dataIter ); //MITK_INFO << " kept" << std::endl; } else { //MITK_INFO << " removed" << std::endl; m_ReferenceDataObserverTags.erase( *dataIter ); // no tag to remove anymore } } this->SetReferenceData( v ); } void mitk::ToolManager::SetReferenceData(DataNode* data) { //MITK_INFO << "ToolManager::SetReferenceData(" << (void*)data << ")" << std::endl; DataVectorType v; if (data) { v.push_back(data); } SetReferenceData(v); } void mitk::ToolManager::SetWorkingData(DataVectorType data) { if ( data != m_WorkingData ) { // remove observers from old nodes for ( DataVectorType::iterator dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter ) { NodeTagMapType::iterator searchIter = m_WorkingDataObserverTags.find( *dataIter ); if ( searchIter != m_WorkingDataObserverTags.end() ) { //MITK_INFO << "Stopping observation of " << (void*)(*dataIter) << std::endl; (*dataIter)->RemoveObserver( searchIter->second ); } } m_WorkingData = data; // TODO tell active tool? // attach new observers m_WorkingDataObserverTags.clear(); for ( DataVectorType::iterator dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter ) { //MITK_INFO << "Observing " << (void*)(*dataIter) << std::endl; itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &ToolManager::OnOneOfTheWorkingDataDeleted ); command->SetCallbackFunction( this, &ToolManager::OnOneOfTheWorkingDataDeletedConst ); m_WorkingDataObserverTags.insert( std::pair( (*dataIter), (*dataIter)->AddObserver( itk::DeleteEvent(), command ) ) ); } WorkingDataChanged.Send(); } } void mitk::ToolManager::OnOneOfTheWorkingDataDeletedConst(const itk::Object* caller, const itk::EventObject& e) { OnOneOfTheWorkingDataDeleted( const_cast(caller), e ); } void mitk::ToolManager::OnOneOfTheWorkingDataDeleted(itk::Object* caller, const itk::EventObject& itkNotUsed(e)) { //MITK_INFO << "Deleted: " << (void*)caller << " Removing from reference data list." << std::endl; DataVectorType v; for (DataVectorType::iterator dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter ) { //MITK_INFO << " In list: " << (void*)(*dataIter); if ( (void*)(*dataIter) != (void*)caller ) { v.push_back( *dataIter ); //MITK_INFO << " kept" << std::endl; } else { //MITK_INFO << " removed" << std::endl; m_WorkingDataObserverTags.erase( *dataIter ); // no tag to remove anymore } } this->SetWorkingData( v ); } void mitk::ToolManager::SetWorkingData(DataNode* data) { DataVectorType v; if (data) // don't allow for NULL nodes { v.push_back(data); } SetWorkingData(v); } void mitk::ToolManager::SetRoiData(DataVectorType data) { if (data != m_RoiData) { // remove observers from old nodes for ( DataVectorType::iterator dataIter = m_RoiData.begin(); dataIter != m_RoiData.end(); ++dataIter ) { NodeTagMapType::iterator searchIter = m_RoiDataObserverTags.find( *dataIter ); if ( searchIter != m_RoiDataObserverTags.end() ) { //MITK_INFO << "Stopping observation of " << (void*)(*dataIter) << std::endl; (*dataIter)->RemoveObserver( searchIter->second ); } } m_RoiData = data; // TODO tell active tool? // attach new observers m_RoiDataObserverTags.clear(); for ( DataVectorType::iterator dataIter = m_RoiData.begin(); dataIter != m_RoiData.end(); ++dataIter ) { //MITK_INFO << "Observing " << (void*)(*dataIter) << std::endl; itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &ToolManager::OnOneOfTheRoiDataDeleted ); command->SetCallbackFunction( this, &ToolManager::OnOneOfTheRoiDataDeletedConst ); m_RoiDataObserverTags.insert( std::pair( (*dataIter), (*dataIter)->AddObserver( itk::DeleteEvent(), command ) ) ); } RoiDataChanged.Send(); } } void mitk::ToolManager::SetRoiData(DataNode* data) { DataVectorType v; if(data) { v.push_back(data); } this->SetRoiData(v); } void mitk::ToolManager::OnOneOfTheRoiDataDeletedConst(const itk::Object* caller, const itk::EventObject& e) { OnOneOfTheRoiDataDeleted( const_cast(caller), e ); } void mitk::ToolManager::OnOneOfTheRoiDataDeleted(itk::Object* caller, const itk::EventObject& itkNotUsed(e)) { //MITK_INFO << "Deleted: " << (void*)caller << " Removing from roi data list." << std::endl; DataVectorType v; for (DataVectorType::iterator dataIter = m_RoiData.begin(); dataIter != m_RoiData.end(); ++dataIter ) { //MITK_INFO << " In list: " << (void*)(*dataIter); if ( (void*)(*dataIter) != (void*)caller ) { v.push_back( *dataIter ); //MITK_INFO << " kept" << std::endl; } else { //MITK_INFO << " removed" << std::endl; m_RoiDataObserverTags.erase( *dataIter ); // no tag to remove anymore } } this->SetRoiData( v ); } mitk::ToolManager::DataVectorType mitk::ToolManager::GetReferenceData() { return m_ReferenceData; } mitk::DataNode* mitk::ToolManager::GetReferenceData(int idx) { try { return m_ReferenceData.at(idx); } catch(std::exception&) { return NULL; } } mitk::ToolManager::DataVectorType mitk::ToolManager::GetWorkingData() { return m_WorkingData; } mitk::ToolManager::DataVectorType mitk::ToolManager::GetRoiData() { return m_RoiData; } mitk::DataNode* mitk::ToolManager::GetRoiData(int idx) { try { return m_RoiData.at(idx); } catch(std::exception&) { return NULL; } } mitk::DataStorage* mitk::ToolManager::GetDataStorage() { if ( m_DataStorage.IsNotNull() ) { return m_DataStorage; } else { return NULL; } } void mitk::ToolManager::SetDataStorage(DataStorage& storage) { m_DataStorage = &storage; } mitk::DataNode* mitk::ToolManager::GetWorkingData(int idx) { try { return m_WorkingData.at(idx); } catch(std::exception&) { return NULL; } } int mitk::ToolManager::GetActiveToolID() { return m_ActiveToolID; } mitk::Tool* mitk::ToolManager::GetActiveTool() { return m_ActiveTool; } void mitk::ToolManager::RegisterClient() { if ( m_RegisteredClients < 1 ) { if ( m_ActiveTool ) { m_ActiveTool->Activated(); GlobalInteraction::GetInstance()->AddListener( m_ActiveTool ); } } ++m_RegisteredClients; } void mitk::ToolManager::UnregisterClient() { if ( m_RegisteredClients < 1) return; --m_RegisteredClients; if ( m_RegisteredClients < 1 ) { if ( m_ActiveTool ) { m_ActiveTool->Deactivated(); GlobalInteraction::GetInstance()->RemoveListener( m_ActiveTool ); } } } int mitk::ToolManager::GetToolID( const Tool* tool ) { int id(0); for ( ToolVectorType::iterator iter = m_Tools.begin(); iter != m_Tools.end(); ++iter, ++id ) { if ( tool == iter->GetPointer() ) { return id; } } return -1; } void mitk::ToolManager::OnNodeRemoved(const mitk::DataNode* node) { //check if the data of the node is typeof Image /*if(dynamic_cast(node->GetData())) {*/ //check all storage vectors OnOneOfTheReferenceDataDeleted(const_cast(node), itk::DeleteEvent()); OnOneOfTheRoiDataDeleted(const_cast(node),itk::DeleteEvent()); OnOneOfTheWorkingDataDeleted(const_cast(node),itk::DeleteEvent()); //} } diff --git a/Modules/MitkExt/Controllers/mitkToolManager.h b/Modules/MitkExt/Controllers/mitkToolManager.h index c6786ff719..a8ee554b74 100644 --- a/Modules/MitkExt/Controllers/mitkToolManager.h +++ b/Modules/MitkExt/Controllers/mitkToolManager.h @@ -1,290 +1,295 @@ /*=================================================================== 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 mitkToolManager_h_Included #define mitkToolManager_h_Included #include "mitkTool.h" #include "MitkExtExports.h" #include "mitkDataNode.h" #include "mitkDataStorage.h" #include "mitkWeakPointer.h" +#include "mitkServiceReference.h" #pragma GCC visibility push(default) #include #pragma GCC visibility pop #include #include namespace mitk { class Image; class PlaneGeometry; /** \brief Manages and coordinates instances of mitk::Tool. \sa QmitkToolSelectionBox \sa QmitkToolReferenceDataSelectionBox \sa QmitkToolWorkingDataSelectionBox \sa Tool \sa QmitkSegmentationView \ingroup Interaction \ingroup ToolManagerEtAl There is a separate page describing the general design of QmitkSegmentationView: \ref QmitkSegmentationTechnicalPage This class creates and manages several instances of mitk::Tool. \li ToolManager creates instances of mitk::Tool by asking the itk::ObjectFactory to list all known implementations of mitk::Tool. As a result, one has to implement both a subclass of mitk::Tool and a matching subclass of itk::ObjectFactoryBase that is registered to the top-level itk::ObjectFactory. For an example, see mitkContourToolFactory.h. (this limitiation of one-class-one-factory is due to the implementation of itk::ObjectFactory). In MITK, the right place to register the factories to itk::ObjectFactory is the mitk::QMCoreObjectFactory or mitk::SBCoreObjectFactory. \li One (and only one - or none at all) of the registered tools can be activated using ActivateTool. This tool is registered to mitk::GlobalInteraction as a listener and will receive all mouse clicks and keyboard strokes that get into the MITK event mechanism. Tools are automatically unregistered from GlobalInteraction when no clients are registered to ToolManager (see RegisterClient()). \li ToolManager knows a set of "reference" DataNodes and a set of "working" DataNodes. The first application are segmentation tools, where the reference is the original image and the working data the (kind of) binary segmentation. However, ToolManager is implemented more generally, so that there could be other tools that work, e.g., with surfaces. \li Any "user/client" of ToolManager, i.e. every functionality that wants to use a tool, should call RegisterClient when the tools should be active. ToolManager keeps track of how many clients want it to be used, and when this count reaches zero, it unregistes the active Tool from GlobalInteraction. In "normal" settings, the functionality does not need to care about that if it uses a QmitkToolSelectionBox, which does exactly that when it is enabled/disabled. \li There is a set of events that are sent by ToolManager. At the moment these are TODO update documentation: - mitk::ToolReferenceDataChangedEvent whenever somebody calls SetReferenceData. Most of the time this actually means that the data has changed, but there might be cases where the same data is passed to SetReferenceData a second time, so don't rely on the assumption that something actually changed. - mitk::ToolSelectedEvent is sent when a (truly) different tool was activated. In reaction to this event you can ask for the active Tool using GetActiveTool or GetActiveToolID (where NULL or -1 indicate that NO tool is active at the moment). Design descisions: \li Not a singleton, because there could be two functionalities using tools, each one with different reference/working data. $Author$ */ class MitkExt_EXPORT ToolManager : public itk::Object { public: typedef std::vector ToolVectorType; typedef std::vector ToolVectorTypeConst; typedef std::vector DataVectorType; // has to be observed for delete events! typedef std::map NodeTagMapType; Message<> NodePropertiesChanged; Message<> NewNodesGenerated; Message1 NewNodeObjectsGenerated; Message<> ActiveToolChanged; Message<> ReferenceDataChanged; Message<> WorkingDataChanged; Message<> RoiDataChanged; Message1 ToolErrorMessage; Message1 GeneralToolMessage; mitkClassMacro(ToolManager, itk::Object); mitkNewMacro1Param(ToolManager, DataStorage*); /** \brief Gives you a list of all tools. This is const on purpose. */ const ToolVectorTypeConst GetTools(); int GetToolID( const Tool* tool ); /* \param id The tool of interest. Counting starts with 0. */ Tool* GetToolById(int id); /** \param id The tool to activate. Provide -1 for disabling any tools. Counting starts with 0. Registeres a listner for NodeRemoved event at DataStorage (see mitk::ToolManager::OnNodeRemoved). */ bool ActivateTool(int id); template int GetToolIdByToolType() { int id = 0; for ( ToolVectorType::iterator iter = m_Tools.begin(); iter != m_Tools.end(); ++iter, ++id ) { if ( dynamic_cast(iter->GetPointer()) ) { return id; } } return -1; } /** \return -1 for "No tool is active" */ int GetActiveToolID(); /** \return NULL for "No tool is active" */ Tool* GetActiveTool(); /* \brief Set a list of data/images as reference objects. */ void SetReferenceData(DataVectorType); /* \brief Set single data item/image as reference object. */ void SetReferenceData(DataNode*); /* \brief Set a list of data/images as working objects. */ void SetWorkingData(DataVectorType); /* \brief Set single data item/image as working object. */ void SetWorkingData(DataNode*); /* \brief Set a list of data/images as roi objects. */ void SetRoiData(DataVectorType); /* \brief Set a single data item/image as roi object. */ void SetRoiData(DataNode*); /* \brief Get the list of reference data. */ DataVectorType GetReferenceData(); /* \brief Get the current reference data. \warning If there is a list of items, this method will only return the first list item. */ DataNode* GetReferenceData(int); /* \brief Get the list of working data. */ DataVectorType GetWorkingData(); /* \brief Get the current working data. \warning If there is a list of items, this method will only return the first list item. */ DataNode* GetWorkingData(int); /* \brief Get the current roi data */ DataVectorType GetRoiData(); /* \brief Get the roi data at position idx */ DataNode* GetRoiData(int idx); DataStorage* GetDataStorage(); void SetDataStorage(DataStorage& storage); /* \brief Tell that someone is using tools. GUI elements should call this when they become active. This method increases an internal "client count". Tools are only registered to GlobalInteraction when this count is greater than 0. This is useful to automatically deactivate tools when you hide their GUI elements. */ void RegisterClient(); /* \brief Tell that someone is NOT using tools. GUI elements should call this when they become active. This method increases an internal "client count". Tools are only registered to GlobalInteraction when this count is greater than 0. This is useful to automatically deactivate tools when you hide their GUI elements. */ void UnregisterClient(); void OnOneOfTheReferenceDataDeletedConst(const itk::Object* caller, const itk::EventObject& e); void OnOneOfTheReferenceDataDeleted (itk::Object* caller, const itk::EventObject& e); void OnOneOfTheWorkingDataDeletedConst(const itk::Object* caller, const itk::EventObject& e); void OnOneOfTheWorkingDataDeleted (itk::Object* caller, const itk::EventObject& e); void OnOneOfTheRoiDataDeletedConst(const itk::Object* caller, const itk::EventObject& e); void OnOneOfTheRoiDataDeleted (itk::Object* caller, const itk::EventObject& e); /* \brief Connected to tool's messages This method just resends error messages coming from any of the tools. This way clients (GUIs) only have to observe one message. */ void OnToolErrorMessage(std::string s); void OnGeneralToolMessage(std::string s); protected: /** You may specify a list of tool "groups" that should be available for this ToolManager. Every Tool can report its group as a string. This constructor will try to find the tool's group inside the supplied string. If there is a match, the tool is accepted. Effectively, you can provide a human readable list like "default, lymphnodevolumetry, oldERISstuff". */ ToolManager(DataStorage* storage); // purposely hidden virtual ~ToolManager(); ToolVectorType m_Tools; Tool* m_ActiveTool; int m_ActiveToolID; DataVectorType m_ReferenceData; NodeTagMapType m_ReferenceDataObserverTags; DataVectorType m_WorkingData; NodeTagMapType m_WorkingDataObserverTags; DataVectorType m_RoiData; NodeTagMapType m_RoiDataObserverTags; int m_RegisteredClients; WeakPointer m_DataStorage; /// \brief Callback for NodeRemove events void OnNodeRemoved(const mitk::DataNode* node); + + private: + + std::map m_DisplayInteractorConfigs; }; } // namespace #endif