diff --git a/Core/Code/Interactions/mitkDispatcher.cpp b/Core/Code/Interactions/mitkDispatcher.cpp index 6abc280f28..ba928ba780 100644 --- a/Core/Code/Interactions/mitkDispatcher.cpp +++ b/Core/Code/Interactions/mitkDispatcher.cpp @@ -1,231 +1,231 @@ /*=================================================================== 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; } 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 (p->GetEventClass() == "MouseReleaseEvent") + 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) { // 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 an event is handled several properties are checked, in order to determine the processing mode of the dispatcher SetEventProcessingMode(dataInteractor); - if (p->GetEventClass() == "MousePressEvent" && m_ProcessingMode == REGULAR) + 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; } diff --git a/Core/Code/Interactions/mitkDisplayInteractor.cpp b/Core/Code/Interactions/mitkDisplayInteractor.cpp index dc0a4fb335..540731cd53 100644 --- a/Core/Code/Interactions/mitkDisplayInteractor.cpp +++ b/Core/Code/Interactions/mitkDisplayInteractor.cpp @@ -1,346 +1,346 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDisplayInteractor.h" #include "mitkBaseRenderer.h" #include "mitkInteractionPositionEvent.h" #include "mitkPropertyList.h" #include // level window #include "mitkStandaloneDataStorage.h" #include "mitkNodePredicateDataType.h" #include "mitkLevelWindowProperty.h" #include "mitkLevelWindow.h" void mitk::DisplayInteractor::Notify(InteractionEvent* interactionEvent, bool isHandled) { // to use the state machine pattern, // the event is passed to the state machine interface to be handled if (!isHandled || m_AlwaysReact) { this->HandleEvent(interactionEvent, NULL); } } void mitk::DisplayInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("init", Init); CONNECT_FUNCTION("move", Move); CONNECT_FUNCTION("zoom", Zoom); CONNECT_FUNCTION("scroll", Scroll); CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown); CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp); CONNECT_FUNCTION("levelWindow", AdjustLevelWindow); } mitk::DisplayInteractor::DisplayInteractor() : m_IndexToSliceModifier(4),m_AutoRepeat(false), m_AlwaysReact(false), m_ZoomFactor(2) { m_StartDisplayCoordinate.Fill(0); m_LastDisplayCoordinate.Fill(0); m_CurrentDisplayCoordinate.Fill(0); } mitk::DisplayInteractor::~DisplayInteractor() { } bool mitk::DisplayInteractor::Init(StateMachineAction*, InteractionEvent* interactionEvent) { BaseRenderer* sender = interactionEvent->GetSender(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent == NULL) { - MITK_WARN<< "DisplayVectorInteractor cannot process the event: " << interactionEvent->GetEventClass(); + MITK_WARN<< "DisplayVectorInteractor cannot process the event: " << interactionEvent->GetNameOfClass(); return false; } Vector2D origin = sender->GetDisplayGeometry()->GetOriginInMM(); double scaleFactorMMPerDisplayUnit = sender->GetDisplayGeometry()->GetScaleFactorMMPerDisplayUnit(); m_StartDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); m_StartCoordinateInMM = mitk::Point2D( (origin + m_StartDisplayCoordinate.GetVectorFromOrigin() * scaleFactorMMPerDisplayUnit).GetDataPointer()); return true; } bool mitk::DisplayInteractor::Move(StateMachineAction*, InteractionEvent* interactionEvent) { BaseRenderer* sender = interactionEvent->GetSender(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent == NULL) { - MITK_WARN<< "DisplayVectorInteractor: cannot process the event in Move action: " << interactionEvent->GetEventClass(); + MITK_WARN<< "DisplayVectorInteractor: cannot process the event in Move action: " << interactionEvent->GetNameOfClass(); return false; } // perform translation sender->GetDisplayGeometry()->MoveBy((positionEvent->GetPointerPositionOnScreen() - m_LastDisplayCoordinate) * (-1.0)); sender->GetRenderingManager()->RequestUpdate(sender->GetRenderWindow()); m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); return true; } bool mitk::DisplayInteractor::Zoom(StateMachineAction*, InteractionEvent* interactionEvent) { const BaseRenderer::Pointer sender = interactionEvent->GetSender(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent == NULL) { - MITK_WARN<< "DisplayVectorInteractor cannot process the event: " << interactionEvent->GetEventClass(); + MITK_WARN<< "DisplayVectorInteractor cannot process the event: " << interactionEvent->GetNameOfClass(); return false; } float factor = 1.0; float distance = 0; if (m_ZoomDirection == "leftright") { distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } // set zooming speed if (distance < 0.0) { factor = 1.0 / m_ZoomFactor; } else if (distance > 0.0) { factor = 1.0 * m_ZoomFactor; } sender->GetDisplayGeometry()->ZoomWithFixedWorldCoordinates(factor, m_StartDisplayCoordinate, m_StartCoordinateInMM); sender->GetRenderingManager()->RequestUpdate(sender->GetRenderWindow()); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); return true; } bool mitk::DisplayInteractor::Scroll(StateMachineAction*, InteractionEvent* interactionEvent) { InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent == NULL) { - MITK_WARN<< "DisplayVectorInteractor::Scroll cannot process the event: " << interactionEvent->GetEventClass(); + MITK_WARN<< "DisplayVectorInteractor::Scroll cannot process the event: " << interactionEvent->GetNameOfClass(); return false; } mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (sliceNaviController) { int delta = 0; // Scrolling direction if (m_ScrollDirection == "leftright") { delta = static_cast(m_LastDisplayCoordinate[1] - positionEvent->GetPointerPositionOnScreen()[1]); } else { delta = static_cast(m_LastDisplayCoordinate[0] - positionEvent->GetPointerPositionOnScreen()[0]); } // Set how many pixels the mouse has to be moved to scroll one slice // if we moved less than 'm_IndexToSliceModifier' pixels slice ONE slice only if (delta > 0 && delta < m_IndexToSliceModifier) { delta = m_IndexToSliceModifier; } else if (delta < 0 && delta > -m_IndexToSliceModifier) { delta = -m_IndexToSliceModifier; } delta /= m_IndexToSliceModifier; int newPos = sliceNaviController->GetSlice()->GetPos() + delta; // if auto repeat is on, start at first slice if you reach the last slice and vice versa int maxSlices = sliceNaviController->GetSlice()->GetSteps(); if (m_AutoRepeat) { while (newPos < 0) { newPos += maxSlices; } while (newPos >= maxSlices) { newPos -= maxSlices; } } else { // if the new slice is below 0 we still show slice 0 // due to the stepper using unsigned int we have to do this ourselves if (newPos < 1) { newPos = 0; } } // set the new position sliceNaviController->GetSlice()->SetPos(newPos); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); } return true; } bool mitk::DisplayInteractor::ScrollOneDown(StateMachineAction*, InteractionEvent* interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper* stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Next(); } return true; } bool mitk::DisplayInteractor::ScrollOneUp(StateMachineAction*, InteractionEvent* interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper* stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Previous(); return true; } return false; } bool mitk::DisplayInteractor::AdjustLevelWindow(StateMachineAction*, InteractionEvent* interactionEvent) { BaseRenderer::Pointer sender = interactionEvent->GetSender(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent == NULL) { - MITK_WARN<< "DisplayVectorInteractor::Scroll cannot process the event: " << interactionEvent->GetEventClass(); + MITK_WARN<< "DisplayVectorInteractor::Scroll cannot process the event: " << interactionEvent->GetNameOfClass(); return false; } m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // search for active image mitk::DataStorage::Pointer storage = sender->GetDataStorage(); mitk::DataNode::Pointer node = NULL; mitk::DataStorage::SetOfObjects::ConstPointer allImageNodes = storage->GetSubset(mitk::NodePredicateDataType::New("Image")); for (unsigned int i = 0; i < allImageNodes->size(); i++) { bool isActiveImage = false; bool propFound = allImageNodes->at(i)->GetBoolProperty("imageForLevelWindow", isActiveImage); if (propFound && isActiveImage) { node = allImageNodes->at(i); continue; } } if (node.IsNull()) { node = storage->GetNode(mitk::NodePredicateDataType::New("Image")); } if (node.IsNull()) { return false; } mitk::LevelWindow lv = mitk::LevelWindow(); node->GetLevelWindow(lv); ScalarType level = lv.GetLevel(); ScalarType window = lv.GetWindow(); // calculate adjustments from mouse movements level += (m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]) * static_cast(2); window += (m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]) * static_cast(2); lv.SetLevelWindow(level, window); dynamic_cast(node->GetProperty("levelwindow"))->SetLevelWindow(lv); sender->GetRenderingManager()->RequestUpdateAll(); return true; } void mitk::DisplayInteractor::ConfigurationChanged() { mitk::PropertyList::Pointer properties = GetAttributes(); // auto repeat std::string strAutoRepeat = ""; if (properties->GetStringProperty("autoRepeat", strAutoRepeat)) { if (strAutoRepeat == "true") { m_AutoRepeat = true; } else { m_AutoRepeat = false; } } // pixel movement for scrolling one slice std::string strPixelPerSlice = ""; if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice)) { m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str()); } else { m_IndexToSliceModifier = 4; } // scroll direction if (!properties->GetStringProperty("zoomDirection", m_ScrollDirection)) { m_ScrollDirection = "updown"; } // zoom direction if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection)) { m_ZoomDirection = "updown"; } // zoom factor std::string strZoomFactor = ""; properties->GetStringProperty("zoomFactor", strZoomFactor); m_ZoomFactor = .05; if (atoi(strZoomFactor.c_str()) > 0) { m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0); } // allwaysReact std::string strAlwaysReact = ""; if (properties->GetStringProperty("alwaysReact", strAlwaysReact)) { if (strAlwaysReact == "true") { m_AlwaysReact = true; } else { m_AlwaysReact = false; } } else { m_AlwaysReact = false; } } bool mitk::DisplayInteractor::FilterEvents(InteractionEvent* /*interactionEvent*/, DataNode* /*dataNode*/) { return true; } diff --git a/Core/Code/Interactions/mitkEventConfig.cpp b/Core/Code/Interactions/mitkEventConfig.cpp index e828c13e0a..e815b18519 100755 --- a/Core/Code/Interactions/mitkEventConfig.cpp +++ b/Core/Code/Interactions/mitkEventConfig.cpp @@ -1,302 +1,302 @@ /*=================================================================== 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 "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) { } 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) { } 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& 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) { mitk::Module* module = mitk::ModuleRegistry::GetModule(moduleName); mitk::ModuleResource resource = module->GetResource("Interactions/" + fileName); if (!resource.IsValid()) { mitkThrow()<< ("Resource not valid. State machine pattern not found:" + fileName); } mitk::ModuleResourceStream stream(resource); d->m_XmlParser.SetStream(&stream); return d->m_XmlParser.Parse() && !d->m_Errors; } 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 (interactionEvent->GetNameOfClass() == "InternalEvent") + 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 (interactionEvent->GetNameOfClass() == "InteractionKeyEvent") + if (std::strcmp(interactionEvent->GetNameOfClass(), "InteractionKeyEvent") == 0) { InteractionKeyEvent* keyEvent = dynamic_cast(interactionEvent.GetPointer()); return ("Std" + keyEvent->GetKey()); } return ""; } void mitk::EventConfig::ClearConfig() { d->m_EventList.clear(); } diff --git a/Core/Code/Interactions/mitkEventFactory.cpp b/Core/Code/Interactions/mitkEventFactory.cpp index fcb08c9c80..ad26f7e4dc 100755 --- a/Core/Code/Interactions/mitkEventFactory.cpp +++ b/Core/Code/Interactions/mitkEventFactory.cpp @@ -1,224 +1,224 @@ /*=================================================================== 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 "mitkEventFactory.h" #include #include #include #include #include #include #include #include #include #include #include namespace { std::vector &split(const std::string &s, char delim, std::vector &elems) { std::stringstream ss(s); std::string item; while (std::getline(ss, item, delim)) { elems.push_back(item); } return elems; } std::vector split(const std::string &s, char delim) { std::vector < std::string > elems; return split(s, delim, elems); } } mitk::InteractionEvent::Pointer mitk::EventFactory::CreateEvent(PropertyList::Pointer list) { // std::string eventClass, eventVariant; list->GetStringProperty(InteractionEventConst::xmlParameterEventClass.c_str(), eventClass); list->GetStringProperty(InteractionEventConst::xmlParameterEventVariant.c_str(), eventVariant); // Query all possible attributes, if they are not present, set their default values. // Position Events & Key Events std::string strModifiers; InteractionEvent::ModifierKeys modifiers = InteractionEvent::NoKey; std::string strEventButton; InteractionEvent::MouseButtons eventButton = InteractionEvent::NoButton; std::string strButtonState; InteractionEvent::MouseButtons buttonState = InteractionEvent::NoButton; std::string strKey; std::string key; std::string strWheelDelta; int wheelDelta; std::string strSignalName = ""; Point2D pos; pos.Fill(0); // Parse modifier information if (list->GetStringProperty(InteractionEventConst::xmlEventPropertyModifier.c_str(), strModifiers)) { std::vector mods = split(strModifiers, ','); for (std::vector::iterator it = mods.begin(); it != mods.end(); ++it) { std::transform((*it).begin(), (*it).end(), (*it).begin(), ::toupper); if (*it == "CTRL") { modifiers = modifiers | InteractionEvent::ControlKey; } else if (*it == "ALT") { modifiers = modifiers | InteractionEvent::AltKey; } else if (*it == "SHIFT") { modifiers = modifiers | InteractionEvent::ShiftKey; } else { MITK_WARN<< "mitkEventFactory: Invalid event modifier in config file :" << (*it); } } } // Set EventButton if (list->GetStringProperty(InteractionEventConst::xmlEventPropertyEventButton.c_str(), strEventButton)) { std::transform(strEventButton.begin(), strEventButton.end(), strEventButton.begin(), ::toupper); if (strEventButton == "MIDDLEMOUSEBUTTON") { eventButton = InteractionEvent::MiddleMouseButton; } else if (strEventButton == "LEFTMOUSEBUTTON") { eventButton = InteractionEvent::LeftMouseButton; } else if (strEventButton == "RIGHTMOUSEBUTTON") { eventButton = InteractionEvent::RightMouseButton; } else { MITK_WARN<< "mitkEventFactory: Invalid event button in config file: " << strEventButton; } } // Parse ButtonStates if (list->GetStringProperty(InteractionEventConst::xmlEventPropertyButtonState.c_str(), strButtonState)) { std::vector mods = split(strButtonState, ','); for (std::vector::iterator it = mods.begin(); it != mods.end(); ++it) { std::transform((*it).begin(), (*it).end(), (*it).begin(), ::toupper); if (*it == "MIDDLEMOUSEBUTTON") { buttonState = buttonState | InteractionEvent::MiddleMouseButton; } else if (*it == "LEFTMOUSEBUTTON") { buttonState = buttonState | InteractionEvent::LeftMouseButton; } else if (*it == "RIGHTMOUSEBUTTON") { buttonState = buttonState | InteractionEvent::RightMouseButton; } else { MITK_WARN<< "mitkEventFactory: Invalid event buttonstate in config file:" << (*it); } } } // Key if (!list->GetStringProperty(InteractionEventConst::xmlEventPropertyKey.c_str(), strKey)) { key = ""; } else { key = strKey; } // WheelDelta if (!list->GetStringProperty(InteractionEventConst::xmlEventPropertyScrollDirection.c_str(), strWheelDelta)) { wheelDelta = 0; } else { std::transform(strWheelDelta.begin(), strWheelDelta.end(), strWheelDelta.begin(), ::toupper); if (strWheelDelta == "DOWN") { wheelDelta = -1; } else { wheelDelta = 1; } } // Internal Signals Name list->GetStringProperty(InteractionEventConst::xmlEventPropertySignalName.c_str(), strSignalName); /* * Here the objects are created */ mitk::InteractionEvent::Pointer event; std::transform(eventClass.begin(), eventClass.end(), eventClass.begin(), ::toupper); if (eventClass == "MOUSEPRESSEVENT") { // buttonstates incorporate the event button (as in Qt) buttonState = buttonState | eventButton; event = MousePressEvent::New(NULL, pos, buttonState, modifiers, eventButton); } else if (eventClass == "MOUSEMOVEEVENT") { event = MouseMoveEvent::New(NULL, pos, buttonState, modifiers); } else if (eventClass == "MOUSERELEASEEVENT") { event = MouseReleaseEvent::New(NULL, pos, buttonState, modifiers, eventButton); } - else if (eventClass == "KEYEVENT") + else if (eventClass == "INTERACTIONKEYEVENT") { event = InteractionKeyEvent::New(NULL, key, modifiers); } else if (eventClass == "MOUSEWHEELEVENT") { event = MouseWheelEvent::New(NULL, pos, buttonState, modifiers, wheelDelta); } - else if (eventClass == "POSITIONEVENT") + else if (eventClass == "INTERACTIONPOSITIONEVENT") { - event = InteractionPositionEvent::New(NULL, pos, "PositionEvent"); + event = InteractionPositionEvent::New(NULL, pos); } else if (eventClass == "INTERNALEVENT") { event = InternalEvent::New(NULL, NULL, strSignalName); } else if (eventClass == "INTERACTIONEVENT") { - event = InteractionEvent::New(NULL, strSignalName); + event = InteractionEvent::New(NULL); } if (event.IsNull()) { MITK_WARN<< "Event couldn't be constructed. Please check your StateMachine patterns and config files\n for the following event class, which is not valid: " << eventClass; return NULL; } return event; } diff --git a/Core/Code/Interactions/mitkEventStateMachine.cpp b/Core/Code/Interactions/mitkEventStateMachine.cpp index 7c211ec145..d08b54c4ee 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) { if (m_StateMachineContainer != NULL) { m_StateMachineContainer->Delete(); } m_StateMachineContainer = StateMachineContainer::New(); if (m_StateMachineContainer->LoadBehavior(filename, moduleName)) { 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->GetEventClass(), MapToEventVariant(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/mitkInteractionEvent.cpp b/Core/Code/Interactions/mitkInteractionEvent.cpp index 0a207c8280..4ccd4c0210 100644 --- a/Core/Code/Interactions/mitkInteractionEvent.cpp +++ b/Core/Code/Interactions/mitkInteractionEvent.cpp @@ -1,92 +1,86 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkException.h" #include "mitkInteractionEvent.h" const std::string mitk::InteractionEvent::KeyEsc = "Escape"; const std::string mitk::InteractionEvent::KeyEnter = "Enter"; const std::string mitk::InteractionEvent::KeyReturn = "Return"; const std::string mitk::InteractionEvent::KeyDelete = "Delete"; const std::string mitk::InteractionEvent::KeyArrowUp = "ArrowUp"; const std::string mitk::InteractionEvent::KeyArrowDown = "ArrowDown"; const std::string mitk::InteractionEvent::KeyArrowLeft = "ArrowLeft"; const std::string mitk::InteractionEvent::KeyArrowRight = "ArrowRight"; const std::string mitk::InteractionEvent::KeyF1 = "F1"; const std::string mitk::InteractionEvent::KeyF2 = "F2"; const std::string mitk::InteractionEvent::KeyF3 = "F3"; const std::string mitk::InteractionEvent::KeyF4 = "F4"; const std::string mitk::InteractionEvent::KeyF5 = "F5"; const std::string mitk::InteractionEvent::KeyF6 = "F6"; const std::string mitk::InteractionEvent::KeyF7 = "F7"; const std::string mitk::InteractionEvent::KeyF8 = "F8"; const std::string mitk::InteractionEvent::KeyF9 = "F9"; const std::string mitk::InteractionEvent::KeyF10 = "F10"; const std::string mitk::InteractionEvent::KeyF11 = "F11"; const std::string mitk::InteractionEvent::KeyF12 = "F12"; const std::string mitk::InteractionEvent::KeyPos1 = "Pos1"; const std::string mitk::InteractionEvent::KeyEnd = "End"; const std::string mitk::InteractionEvent::KeyInsert = "Insert"; const std::string mitk::InteractionEvent::KeyPageUp = "PageUp"; const std::string mitk::InteractionEvent::KeyPageDown = "PageDown"; const std::string mitk::InteractionEvent::KeySpace = "Space"; -mitk::InteractionEvent::InteractionEvent(BaseRenderer* baseRenderer, const std::string& eventClass) +mitk::InteractionEvent::InteractionEvent(BaseRenderer* baseRenderer) : m_Sender(baseRenderer) -, m_EventClass(eventClass) { } void mitk::InteractionEvent::SetSender(mitk::BaseRenderer* sender) { m_Sender = sender; } mitk::BaseRenderer* mitk::InteractionEvent::GetSender() const { return m_Sender; } bool mitk::InteractionEvent::IsEqual(const InteractionEvent&) const { return true; } mitk::InteractionEvent::~InteractionEvent() { } bool mitk::InteractionEvent::IsSuperClassOf(const InteractionEvent::Pointer& baseClass) const { return (dynamic_cast(baseClass.GetPointer()) != NULL) ; } -std::string mitk::InteractionEvent::GetEventClass() const -{ - return m_EventClass; -} - bool mitk::operator==(const InteractionEvent& a, const InteractionEvent& b) { return (typeid(a) == typeid(b) && a.IsEqual(b)); } bool mitk::operator!=(const InteractionEvent& a, const InteractionEvent& b) { return !(a == b); } diff --git a/Core/Code/Interactions/mitkInteractionEvent.h b/Core/Code/Interactions/mitkInteractionEvent.h index c61d5301f3..5e35f1e54e 100644 --- a/Core/Code/Interactions/mitkInteractionEvent.h +++ b/Core/Code/Interactions/mitkInteractionEvent.h @@ -1,157 +1,150 @@ /*=================================================================== 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 MITKINTERACTIONEVENT_H_ #define MITKINTERACTIONEVENT_H_ #include "itkLightObject.h" #include "itkObjectFactory.h" #include "mitkCommon.h" #include "mitkBaseRenderer.h" #include #include namespace mitk { class MITK_CORE_EXPORT InteractionEvent: public itk::LightObject { public: mitkClassMacro(InteractionEvent,itk::LightObject) - mitkNewMacro2Param(Self,BaseRenderer*, const std::string&) + mitkNewMacro1Param(Self,BaseRenderer*) void SetSender(BaseRenderer* sender); BaseRenderer* GetSender() const; - /** - * Return unique string identifier that gives the event class of this object, as it can be used in a state machine pattern. - * --- itk - */ - std::string GetEventClass() const; - /** * This class implements an up cast to check if the provided baseClass object is derived from this class. * This function is used to support polymorphism on state machine pattern (XML) level. */ virtual bool IsSuperClassOf(const InteractionEvent::Pointer& baseClass) const; /** * Mouse/keyboard state values */ enum MouseButtons { NoButton = 0x0000, LeftMouseButton = 0x0001, RightMouseButton = 0x0002, MiddleMouseButton = 0x0004 }; enum ModifierKeys { NoKey = 0x0000, ShiftKey = 0x0100, ControlKey = 0x0200, AltKey = 0x0400 }; /** * KeyConstants Constants for special keys */ // Special Keys static const std::string KeyEsc; // = "Escape"; static const std::string KeyEnter; // = "Enter"; static const std::string KeyReturn; // = "Return"; static const std::string KeyDelete; // = "Delete"; static const std::string KeyArrowUp; // = "ArrowUp"; static const std::string KeyArrowDown; // = "ArrowDown"; static const std::string KeyArrowLeft; // = "ArrowLeft"; static const std::string KeyArrowRight; // = "ArrowRight"; static const std::string KeyF1; // = "F1"; static const std::string KeyF2; // = "F2"; static const std::string KeyF3; // = "F3"; static const std::string KeyF4; // = "F4"; static const std::string KeyF5; // = "F5"; static const std::string KeyF6; // = "F6"; static const std::string KeyF7; // = "F7"; static const std::string KeyF8; // = "F8"; static const std::string KeyF9; // = "F9"; static const std::string KeyF10; // = "F10"; static const std::string KeyF11; // = "F11"; static const std::string KeyF12; // = "F12"; static const std::string KeyPos1; // = "Pos1"; static const std::string KeyEnd; // = "End"; static const std::string KeyInsert; // = "Insert"; static const std::string KeyPageUp; // = "PageUp"; static const std::string KeyPageDown; // = "PageDown"; static const std::string KeySpace; // = "Space"; // End special keys protected: - InteractionEvent(BaseRenderer*, const std::string&); + InteractionEvent(BaseRenderer*); virtual ~InteractionEvent(); friend MITK_CORE_EXPORT bool operator==(const InteractionEvent&, const InteractionEvent&); virtual bool IsEqual(const InteractionEvent& other) const; private: BaseRenderer* m_Sender; - std::string m_EventClass; }; /** * Implementation of equality for event classes. * Equality does \b not mean an exact copy or pointer equality. * * A match is determined by agreement in all attributes that are necessary to describe * the event for a state machine transition. * E.g. for a mouse event press event, it is important which modifiers are used, * which mouse button was used to triggered the event, but the mouse position is irrelevant. */ MITK_CORE_EXPORT bool operator==(const InteractionEvent& a, const InteractionEvent& b); MITK_CORE_EXPORT bool operator!=(const InteractionEvent& a, const InteractionEvent& b); /* * Allow bitwise OR operation on enums. */ inline InteractionEvent::MouseButtons operator|(InteractionEvent::MouseButtons a, InteractionEvent::MouseButtons b) { return static_cast(static_cast(a) | static_cast(b)); } inline InteractionEvent::MouseButtons& operator|=(InteractionEvent::MouseButtons& a, InteractionEvent::MouseButtons& b) { a = static_cast(static_cast(a) | static_cast(b)); return a; } inline InteractionEvent::ModifierKeys operator|(InteractionEvent::ModifierKeys a, InteractionEvent::ModifierKeys b) { return static_cast(static_cast(a) | static_cast(b)); } inline InteractionEvent::ModifierKeys& operator|=(InteractionEvent::ModifierKeys& a, InteractionEvent::ModifierKeys& b) { a = static_cast(static_cast(a) | static_cast(b)); return a; } } /* namespace mitk */ #endif /* MITKINTERACTIONEVENT_H_ */ diff --git a/Core/Code/Interactions/mitkInteractionPositionEvent.cpp b/Core/Code/Interactions/mitkInteractionPositionEvent.cpp index 1c0c606a97..74f963600e 100644 --- a/Core/Code/Interactions/mitkInteractionPositionEvent.cpp +++ b/Core/Code/Interactions/mitkInteractionPositionEvent.cpp @@ -1,58 +1,57 @@ /*=================================================================== 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 "mitkInteractionPositionEvent.h" #include mitk::InteractionPositionEvent::InteractionPositionEvent(mitk::BaseRenderer* baseRenderer, - const mitk::Point2D& mousePosition, - const std::string& eventClass) -: InteractionEvent(baseRenderer, eventClass) + const mitk::Point2D& mousePosition) +: InteractionEvent(baseRenderer) , m_PointerPosition(mousePosition) { if (GetSender() != NULL) { m_WorldPosition = GetSender()->Map2DRendererPositionTo3DWorldPosition(&m_PointerPosition); } else { m_WorldPosition.Fill(0); } } const mitk::Point2D mitk::InteractionPositionEvent::GetPointerPositionOnScreen() const { return m_PointerPosition; } const mitk::Point3D mitk::InteractionPositionEvent::GetPositionInWorld() const { return m_WorldPosition; } bool mitk::InteractionPositionEvent::IsEqual(const InteractionEvent& other) const { return Superclass::IsEqual(other); } mitk::InteractionPositionEvent::~InteractionPositionEvent() { } bool mitk::InteractionPositionEvent::IsSuperClassOf(const InteractionEvent::Pointer& baseClass) const { return (dynamic_cast(baseClass.GetPointer()) != NULL) ; } diff --git a/Core/Code/Interactions/mitkInteractionPositionEvent.h b/Core/Code/Interactions/mitkInteractionPositionEvent.h index fdcf383dbb..74ad60a5f1 100644 --- a/Core/Code/Interactions/mitkInteractionPositionEvent.h +++ b/Core/Code/Interactions/mitkInteractionPositionEvent.h @@ -1,65 +1,65 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKINTERACTIONPOSITIONEVENT_H_ #define MITKINTERACTIONPOSITIONEVENT_H_ #include "itkObject.h" #include "itkObjectFactory.h" #include "mitkCommon.h" #include "mitkInteractionEvent.h" #include "mitkInteractionEventConst.h" #include #include namespace mitk { /** * \class InteractionPositionEvent * * \brief Super class for all position events. * * This class is instantiated with a BaseRenderer and the 2D pointer position relative to the renderer, * the object then queries the Renderer for 3D world coordinates and supplies them to deriving classes. * */ class MITK_CORE_EXPORT InteractionPositionEvent : public InteractionEvent { public: mitkClassMacro(InteractionPositionEvent,InteractionEvent); - mitkNewMacro3Param(Self, BaseRenderer*, const Point2D& , const std::string&); + mitkNewMacro2Param(Self, BaseRenderer*, const Point2D&); const Point2D GetPointerPositionOnScreen() const; const Point3D GetPositionInWorld() const; virtual bool IsSuperClassOf(const InteractionEvent::Pointer& baseClass) const; protected: - InteractionPositionEvent(BaseRenderer* baseRenderer, const Point2D& mousePosition, const std::string& eventClass); + InteractionPositionEvent(BaseRenderer* baseRenderer, const Point2D& mousePosition); virtual ~InteractionPositionEvent(); virtual bool IsEqual(const InteractionEvent&) const; private: Point2D m_PointerPosition; Point3D m_WorldPosition; }; } /* namespace mitk */ #endif /* MITKINTERACTIONPOSITIONEVENT_H_ */ diff --git a/Core/Code/Interactions/mitkInternalEvent.cpp b/Core/Code/Interactions/mitkInternalEvent.cpp index 97438110e1..171cd49f3a 100644 --- a/Core/Code/Interactions/mitkInternalEvent.cpp +++ b/Core/Code/Interactions/mitkInternalEvent.cpp @@ -1,51 +1,51 @@ /*=================================================================== 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 "mitkInternalEvent.h" #include "mitkDataInteractor.h" mitk::InternalEvent::InternalEvent(mitk::BaseRenderer* baseRenderer, DataInteractor* sourceInteractor, const std::string& signalName) -: InteractionEvent(baseRenderer, "InternalEvent") +: InteractionEvent(baseRenderer) , m_DataInteractor(sourceInteractor) , m_SignalName(signalName) { } bool mitk::InternalEvent::IsEqual(const mitk::InteractionEvent& interactionEvent) const { const mitk::InternalEvent& internalEvent = static_cast(interactionEvent); return (m_SignalName == internalEvent.GetSignalName() && Superclass::IsEqual(interactionEvent)); } mitk::InternalEvent::~InternalEvent() { } std::string mitk::InternalEvent::GetSignalName() const { return m_SignalName; } mitk::DataInteractor* mitk::InternalEvent::GetTargetInteractor() const { return m_DataInteractor.GetPointer(); } bool mitk::InternalEvent::IsSuperClassOf(const InteractionEvent::Pointer& baseClass) const { return (NULL != dynamic_cast(baseClass.GetPointer()) ); } diff --git a/Core/Code/Interactions/mitkMouseMoveEvent.cpp b/Core/Code/Interactions/mitkMouseMoveEvent.cpp index 203e8efc72..d58ce5c563 100644 --- a/Core/Code/Interactions/mitkMouseMoveEvent.cpp +++ b/Core/Code/Interactions/mitkMouseMoveEvent.cpp @@ -1,61 +1,61 @@ /*=================================================================== 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 "mitkException.h" #include "mitkMouseMoveEvent.h" mitk::MouseMoveEvent::MouseMoveEvent(mitk::BaseRenderer* baseRenderer, const mitk::Point2D& mousePosition , MouseButtons buttonStates, ModifierKeys modifiers) -: InteractionPositionEvent(baseRenderer, mousePosition, "MouseMoveEvent") +: InteractionPositionEvent(baseRenderer, mousePosition) , m_ButtonStates(buttonStates) , m_Modifiers(modifiers) { } mitk::InteractionEvent::ModifierKeys mitk::MouseMoveEvent::GetModifiers() const { return m_Modifiers; } mitk::InteractionEvent::MouseButtons mitk::MouseMoveEvent::GetButtonStates() const { return m_ButtonStates; } void mitk::MouseMoveEvent::SetModifiers(ModifierKeys modifiers) { m_Modifiers = modifiers; } void mitk::MouseMoveEvent::SetButtonStates(MouseButtons buttons) { m_ButtonStates = buttons; } mitk::MouseMoveEvent::~MouseMoveEvent() { } bool mitk::MouseMoveEvent::IsEqual(const mitk::InteractionEvent& interactionEvent) const { const mitk::MouseMoveEvent& mpe = static_cast(interactionEvent); return (this->GetModifiers() == mpe.GetModifiers() && this->GetButtonStates() == mpe.GetButtonStates() && Superclass::IsEqual(interactionEvent)); } bool mitk::MouseMoveEvent::IsSuperClassOf(const InteractionEvent::Pointer& baseClass) const { return (dynamic_cast(baseClass.GetPointer()) != NULL) ; } diff --git a/Core/Code/Interactions/mitkMousePressEvent.cpp b/Core/Code/Interactions/mitkMousePressEvent.cpp index 09518f0936..12af1bfe82 100644 --- a/Core/Code/Interactions/mitkMousePressEvent.cpp +++ b/Core/Code/Interactions/mitkMousePressEvent.cpp @@ -1,77 +1,77 @@ /*=================================================================== 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 "mitkException.h" #include "mitkMousePressEvent.h" mitk::MousePressEvent::MousePressEvent(mitk::BaseRenderer* baseRenderer, const mitk::Point2D& mousePosition, MouseButtons buttonStates, ModifierKeys modifiers, MouseButtons eventButton) -: InteractionPositionEvent(baseRenderer, mousePosition, "MousePressEvent") +: InteractionPositionEvent(baseRenderer, mousePosition) , m_EventButton(eventButton) , m_ButtonStates(buttonStates) , m_Modifiers( modifiers) { } mitk::InteractionEvent::MouseButtons mitk::MousePressEvent::GetEventButton() const { return m_EventButton; } void mitk::MousePressEvent::SetEventButton(MouseButtons buttons) { m_EventButton = buttons; } mitk::InteractionEvent::ModifierKeys mitk::MousePressEvent::GetModifiers() const { return m_Modifiers; } mitk::InteractionEvent::MouseButtons mitk::MousePressEvent::GetButtonStates() const { return m_ButtonStates; } void mitk::MousePressEvent::SetModifiers(ModifierKeys modifiers) { m_Modifiers = modifiers; } void mitk::MousePressEvent::SetButtonStates(MouseButtons buttons) { m_ButtonStates = buttons; } mitk::MousePressEvent::~MousePressEvent() { } bool mitk::MousePressEvent::IsEqual(const mitk::InteractionEvent& interactionEvent) const { const mitk::MousePressEvent& mpe = static_cast(interactionEvent); return (this->GetEventButton() == mpe.GetEventButton() && this->GetModifiers() == mpe.GetModifiers() && this->GetButtonStates() == mpe.GetButtonStates() && Superclass::IsEqual(interactionEvent)); } bool mitk::MousePressEvent::IsSuperClassOf(const InteractionEvent::Pointer& baseClass) const { return (dynamic_cast(baseClass.GetPointer()) != NULL) ; } diff --git a/Core/Code/Interactions/mitkMouseReleaseEvent.cpp b/Core/Code/Interactions/mitkMouseReleaseEvent.cpp index c2e06bfa58..00162b9f49 100644 --- a/Core/Code/Interactions/mitkMouseReleaseEvent.cpp +++ b/Core/Code/Interactions/mitkMouseReleaseEvent.cpp @@ -1,78 +1,78 @@ /*=================================================================== 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 "mitkException.h" #include "mitkMouseReleaseEvent.h" mitk::MouseReleaseEvent::MouseReleaseEvent(mitk::BaseRenderer* baseRenderer, const mitk::Point2D& mousePosition, MouseButtons buttonStates, ModifierKeys modifiers, MouseButtons eventButton) -: InteractionPositionEvent(baseRenderer, mousePosition, "MouseReleaseEvent") +: InteractionPositionEvent(baseRenderer, mousePosition) , m_EventButton(eventButton) , m_ButtonStates(buttonStates) , m_Modifiers(modifiers) { } mitk::InteractionEvent::MouseButtons mitk::MouseReleaseEvent::GetEventButton() const { return m_EventButton; } void mitk::MouseReleaseEvent::SetEventButton(MouseButtons buttons) { m_EventButton = buttons; } mitk::InteractionEvent::ModifierKeys mitk::MouseReleaseEvent::GetModifiers() const { return m_Modifiers; } mitk::InteractionEvent::MouseButtons mitk::MouseReleaseEvent::GetButtonStates() const { return m_ButtonStates; } void mitk::MouseReleaseEvent::SetModifiers(ModifierKeys modifiers) { m_Modifiers = modifiers; } void mitk::MouseReleaseEvent::SetButtonStates(MouseButtons buttons) { m_ButtonStates = buttons; } mitk::MouseReleaseEvent::~MouseReleaseEvent() { } bool mitk::MouseReleaseEvent::IsEqual(const mitk::InteractionEvent& interactionEvent) const { const mitk::MouseReleaseEvent& mre = static_cast(interactionEvent); return (this->GetEventButton() == mre.GetEventButton() && this->GetModifiers() == mre.GetModifiers() && this->GetButtonStates() == mre.GetButtonStates() && Superclass::IsEqual(interactionEvent)); } bool mitk::MouseReleaseEvent::IsSuperClassOf(const InteractionEvent::Pointer& baseClass) const { return (dynamic_cast(baseClass.GetPointer()) != NULL) ; } diff --git a/Core/Code/Interactions/mitkMouseWheelEvent.cpp b/Core/Code/Interactions/mitkMouseWheelEvent.cpp index cbadf9c0a5..b6c05a4355 100644 --- a/Core/Code/Interactions/mitkMouseWheelEvent.cpp +++ b/Core/Code/Interactions/mitkMouseWheelEvent.cpp @@ -1,76 +1,76 @@ /*=================================================================== 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 "mitkMouseWheelEvent.h" mitk::MouseWheelEvent::MouseWheelEvent(BaseRenderer* baseRenderer, const Point2D& mousePosition, MouseButtons buttonStates, ModifierKeys modifiers, int wheelDelta) -: InteractionPositionEvent(baseRenderer, mousePosition, "MouseWheelEvent") +: InteractionPositionEvent(baseRenderer, mousePosition) , m_WheelDelta(wheelDelta) , m_ButtonStates(buttonStates) , m_Modifiers(modifiers) { } int mitk::MouseWheelEvent::GetWheelDelta() const { return m_WheelDelta; } void mitk::MouseWheelEvent::SetWheelDelta(int delta) { m_WheelDelta = delta; } mitk::InteractionEvent::ModifierKeys mitk::MouseWheelEvent::GetModifiers() const { return m_Modifiers; } mitk::InteractionEvent::MouseButtons mitk::MouseWheelEvent::GetButtonStates() const { return m_ButtonStates; } void mitk::MouseWheelEvent::SetModifiers(ModifierKeys modifiers) { m_Modifiers = modifiers; } void mitk::MouseWheelEvent::SetButtonStates(MouseButtons buttons) { m_ButtonStates = buttons; } mitk::MouseWheelEvent::~MouseWheelEvent() { } bool mitk::MouseWheelEvent::IsEqual(const mitk::InteractionEvent& interactionEvent) const { const mitk::MouseWheelEvent& mwe = static_cast(interactionEvent); return ((this->GetWheelDelta() * mwe.GetWheelDelta() > 0) // Consider WheelEvents to be equal if the scrolling is done in the same direction. && this->GetModifiers() == mwe.GetModifiers() && this->GetButtonStates() == mwe.GetButtonStates() && Superclass::IsEqual(interactionEvent)); } bool mitk::MouseWheelEvent::IsSuperClassOf(const InteractionEvent::Pointer& baseClass) const { return (dynamic_cast(baseClass.GetPointer()) != NULL) ; } diff --git a/Core/Code/Resources/Interactions/DisplayConfig.xml b/Core/Code/Resources/Interactions/DisplayConfig.xml index 5e76716816..55c6f6b6d1 100644 --- a/Core/Code/Resources/Interactions/DisplayConfig.xml +++ b/Core/Code/Resources/Interactions/DisplayConfig.xml @@ -1,73 +1,73 @@ - + - + diff --git a/Core/Code/Resources/Interactions/DisplayInteraction.xml b/Core/Code/Resources/Interactions/DisplayInteraction.xml index f891ca261f..b6505dc89a 100644 --- a/Core/Code/Resources/Interactions/DisplayInteraction.xml +++ b/Core/Code/Resources/Interactions/DisplayInteraction.xml @@ -1,65 +1,65 @@ - + - + - + - + - + - + - + - - + + - + - + diff --git a/Core/Code/Resources/Interactions/PointSet.xml b/Core/Code/Resources/Interactions/PointSet.xml index 8451ccb211..bc35dcedd5 100644 --- a/Core/Code/Resources/Interactions/PointSet.xml +++ b/Core/Code/Resources/Interactions/PointSet.xml @@ -1,49 +1,49 @@ - + - + diff --git a/Core/Code/Resources/Interactions/PointSetConfig.xml b/Core/Code/Resources/Interactions/PointSetConfig.xml index f38f80b075..96743379bf 100644 --- a/Core/Code/Resources/Interactions/PointSetConfig.xml +++ b/Core/Code/Resources/Interactions/PointSetConfig.xml @@ -1,40 +1,40 @@ - + diff --git a/Core/Code/Resources/Interactions/globalConfig.xml b/Core/Code/Resources/Interactions/globalConfig.xml index 34d543270c..0915aa1fcf 100644 --- a/Core/Code/Resources/Interactions/globalConfig.xml +++ b/Core/Code/Resources/Interactions/globalConfig.xml @@ -1,70 +1,70 @@ - + - + diff --git a/Core/Code/Testing/Resources/Interactions/AddAndRemovePoints.xml b/Core/Code/Testing/Resources/Interactions/AddAndRemovePoints.xml index 28e50b86fa..6ee546e0f5 100644 --- a/Core/Code/Testing/Resources/Interactions/AddAndRemovePoints.xml +++ b/Core/Code/Testing/Resources/Interactions/AddAndRemovePoints.xml @@ -1,31 +1,31 @@ - + - + diff --git a/Core/Code/Testing/Resources/Interactions/StatemachineConfigTest.xml b/Core/Code/Testing/Resources/Interactions/StatemachineConfigTest.xml index b0e0095457..ab95481f68 100644 --- a/Core/Code/Testing/Resources/Interactions/StatemachineConfigTest.xml +++ b/Core/Code/Testing/Resources/Interactions/StatemachineConfigTest.xml @@ -1,19 +1,19 @@ - + diff --git a/Core/Code/Testing/Resources/Interactions/globalConfig.xml b/Core/Code/Testing/Resources/Interactions/globalConfig.xml index 34d543270c..0915aa1fcf 100644 --- a/Core/Code/Testing/Resources/Interactions/globalConfig.xml +++ b/Core/Code/Testing/Resources/Interactions/globalConfig.xml @@ -1,70 +1,70 @@ - + - + diff --git a/Core/Documentation/Doxygen/Concepts/NewEvents.dox b/Core/Documentation/Doxygen/Concepts/NewEvents.dox index cb42cb550a..9a3545d9cf 100644 --- a/Core/Documentation/Doxygen/Concepts/NewEvents.dox +++ b/Core/Documentation/Doxygen/Concepts/NewEvents.dox @@ -1,72 +1,67 @@ /** \page ImplementNewEventsPage What needs to be done to implement a new event? \tableofcontents \section WhenUseEvents When to use this guide In general there are no restriction about how events are created and processed. The intention of this page is to provide how to contruct Events in order to use them with the MITK Interaction Concept. \section OriginEvents Origin and processing of Events \ref DataInteractionPage gives a brief description of how events are generated and proccessed. After events are created they need to be send via mitk::RenderWindowBase::HandleEvent() so that they are relayed to a Dispatcher which handles the event distribution to the DataInteractors. \section EventImplementation Event Implementation The only prerequisite for a new event to be accepted is that it is derived from the mitk::InteractionEvent class. -Further it is neccessary to provide a unique string identifier for this event class -(this string will identify this class/super-class in a state machine pattern by the event_class tag), and it needs to implement at least two functions, -as shown at the example of the MousePressEvent. +The new event must implement at least two functions, +as shown in the example of the MousePressEvent. -Implementation of equality for each event class by implementing isEqual. +Implementation of equality for each event class by implementing IsEqual. Equality does \b not mean an exact copy or pointer equality. Equality is determined by agreement in all attributes that are necessary to describe the event for a state machine transition. Here, for example, for the a mouse event press event, it is important which modifiers are used, which mouse button was used to triggered the event, but the mouse position is irrelevant. \code -bool mitk::MousePressEvent::MatchesTemplate(mitk::InteractionEvent::Pointer interactionEvent) +bool mitk::MousePressEvent::IsEqual(const mitk::InteractionEvent& interactionEvent) { - // cast to your event class here - mitk::MousePressEvent* mpe = dynamic_cast(interactionEvent.GetPointer()); - if (mpe == NULL) - { - return false; - } + // cast to your event class here (this is guaranteed to work + const mitk::MousePressEvent& mpe = static_cast(interactionEvent); // Checking relevant attributes, such as EventButton and Modifiers, but omitting the mouse coordinates // Replace this to check for your event class' attributes - return (this->GetEventButton() == mpe->GetEventButton() && this->GetModifiers() == mpe->GetModifiers() - && this->GetButtonStates() == mpe->GetButtonStates()); + return (this->GetEventButton() == mpe.GetEventButton() && this->GetModifiers() == mpe.GetModifiers() + && this->GetButtonStates() == mpe.GetButtonStates()) && Superclass::IsEqual(interactionEvent); } \endcode The function IsSuperClassOf() implements an up cast to check if the provided baseClass object is derived from this class. This function is used to support polymorphism on state machine pattern (XML) level. \code bool mitk::MousePressEvent::IsSuperClassOf(InteractionEvent::Pointer baseClass) { // Replace with your class, or implement some rules which classes are accepted. MousePressEvent* event = dynamic_cast(baseClass.GetPointer()); if (event != NULL) { return true; } return false; } \endcode \section EventConstruction Configuration File & Event Factory Regarding the state machine and the state machine pattern parser there are no changes necessary. To be able to parse the new event, changes have to be made in the mitk::EventConfig and mitk::EventFactory classes. The mitk::EventConfig class has to be adapted in a way such that the parameters that describe the event are parsed. The parsing can be easily extended, and should provide a mitk::PropertyList for each event parsed. Lastly the mitk::EventFactory has to be extended to use this mitk::PropertyList and construct an event based on it. */ diff --git a/Documentation/Doxygen/DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox b/Documentation/Doxygen/DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox index 493da74bde..542dd477c9 100644 --- a/Documentation/Doxygen/DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox +++ b/Documentation/Doxygen/DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox @@ -1,432 +1,432 @@ /** \page Step10Page MITK Tutorial - Step 10: Adding new Interaction \tableofcontents \section HowToUseDataInteractor How to use an existing DataInteractor MITK provides finished DataInteractors for a variety of tasks, they can be found in Code/Core/Interactors. They can be used with state machine patterns and config files located under Resources/Interaction. A DataInteractor consists of four parts. The class describing the functionality and two XML files; one describes the state machine pattern, that is the workflow of an interaction and the second describes the user events which trigger an action. Lastly every DataInteractor works on a DataNode in which it stores and manipulates data. To use a DataInteractor these parts have to be brought together. This code demonstrates the use of an existing DataInteractor exemplary for the PointSetDataInteractor: First we need a DataNode that is added to the DataStorage. \code mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); GetDataStorage()->Add(dataNode.GetPointer()); \endcode Then we create an instance of to PointSetDataInteractor and load a statemachine pattern as well as a configuration for it: \code m_CurrentInteractor = mitk::PointSetDataInteractor::New(); m_CurrentInteractor->LoadStateMachine("PointSet.xml"); m_CurrentInteractor->LoadEventConfig("PointSetConfig.xml"); \endcode Lastly the DataNode is added to the DataInteractor \code m_CurrentInteractor->SetDataNode(dataNode); \endcode now the DataInteractor is ready for usage. \section SectionImplementationDataInteractor How to implement a new DataInteractor This second part of the tutorial step goes beyond the activation of an interactor, that modifies data by user interaction) as shown above. It shows what needs to be implemented to add a new way of interaction within your MITK application. Please see \ref DataInteractionPage as an introduction to the MITK interaction mechanism, you may also want to read \ref DataInteractionTechnicalPage. This tutorial is structured as follows: The first section deals with config files, describing all the parameters of events and how to use them in a configuration file. In the second section the basics are described that are needed to write a state machine pattern. The last section deals with brining configuration, state machine pattern and code together and gives an examplary implementation of a DataInteractor. \section ConfigFileDescriptionSection How to create a Config-File \subsection EventDescriptionSection Event Description Events are described by their parameters. Each event type has its own set of parameters that can be set in the configuration file. If a parameter is ommitted it is set to its default value. Following all possible parameters are listed and described, to which parameters events have is decribed in their respective documentation. Mandatory for each event description is the event class and the event variant. The parameters of an event are set by attribute tags. \note Refer to \ref EventClassSection for the meaning of event class. \b Mouse \b Buttons \n mitk::MouseButtons represent the buttons. They can be used for two attributes. First the EventButton which describes the button that triggered the event, this allways is a single button. Secondly the ButtonState attribute that describes which buttons were pressed at the moment the event has been generated. For example assume the right mouse button and the middle mouse button are already pressed, now also the left mouse button is pressed too and generates a second event, this would be described as follows: \code \endcode Note: Technically the LeftMouseButton is also pressed and should be listed in the ButtonState, but this is taken care of by the mitk:EventFactory . Key Events \n mitk::InteractionKeyEvent represents a pressed key, which key is pressed is provided with the Key attribute like this \code \endcode or \code \endcode \note Key Events do not require an explicit configuration all, for all key events there exists a predefined event variant with the name 'Std' + value, that is key a is named 'StdA'. The names for special keys are listed here: \dontinclude mitkInteractionEventConst.h \skipline // Special Keys \until // End special keys Modifier Keys \n mitk::ModifierKeys represent the combination of pressed modifier keys, several modifier keys pressed at the same time are denoted by listing them all separated by commas. \code \endcode \b ScrollDirection \n This attribute is unique to the mitk::MouseWheelEvent and describes the direction in which the mouse wheel is rotated. In the event description actual only the direction is provided, but the event is generated with the actual value, and this value can be retrieved from the object. \code \endcode \subsection ExamplesSection Examples Examples for key events: \code - + - + \endcode Examples for MousePress events: \code \endcode There exists a standard configuration file for the most common events called GlobalConfig.xml that can be used to as a default and can be extended by a specific definition. \subsection ParameterDescriptionSection Parameter Description It is also possible to store parameters in the config file. Those are stored using the param-tag, like this: \code \endcode Within the application these properties can then be access via a mitk::PropertyList like this: \code // sm - state machine loaded with config file example2 mitk::PropertyList::Pointer properties = GetAttributes(); std::string prop1; properties->GetStringProperty("property1",prop1); \endcode \section HowToStateMachine HowTo Write a State Machine A state machine pattern is described in a XML file. \subsection StateSection States States are described using the state-tag. Each state has to have a name. Exactly one state has to be as start state in each state machine to indicate the state in which the state machine is set when it is constructed. So a valid, but rather useless state machine would like like this: \code \endcode Optionally a state can be assigned a special mode that influences the event distribution. These modes are GRAB_INPUT , PREFER_INPUT and REGULAR (where REGULAR is default and does not need to be indicated). See \ref DispatcherEventDistSection for a description of these modes. Use the special modes only when necessary as they prevent other DataInteractors to receive events. \code \endcode \subsection TransitionSection Transitions Transitions are part of a state and describe all possible state switches, and are therefore important for modelling an interaction scheme. Transitions consist a part that describes the event which triggers the transition (event class and event variant) and a target which is state to which the state machine switches after executing a transition. An event class describes the event type (see mitk::InteractionEvent for the different classes) and the event variant is a specification thereof and the exact description is taken from a config file. Together they determine which event can trigger this transition. For example this state machine will switch from state A to state B when the StdMousePressPrimaryButton event (left mouse button is pressed) occurs. \subsubsection EventClassSection Event Class The event class description supports the polymorphism of the event classes. Therefore state machine patters should be written in the most general ways possible. So for a given class hierarchy like this: \dot digraph { node [shape=record, fontname=Helvetica, fontsize=10]; a [ label="{PositionEvent}"]; b [ label="{MousePressEvent}" ]; c [ label="MouseReleaseEvent" ]; d [ label="TouchEvent", style=dotted ]; a -> b; a -> c; a -> d; } \enddot in the state machine pattern the PositionEvent can be declared as event class to restrict to the events which hold a position information. The actual implementation is then given in the configuration file. In this case it allows to define event of the classes PositionEvent itself, or MousePressEvent,MouseReleaseEvent,TouchEvent. This has the advantage that the patterns remain the same no matter what input devices are used, and the state machine patterns can be configured for newly added event classes as long as they match the class hierachy (this ensures they hold the neccessary properties). \code \endcode \subsection ActionSection Actions Actions can be added to transitions and represent functions in the DataInteractor that are executed on taking a transition. The following simple state machine will listen for left mouse clicks and execute two actions (and actually never stop). \code \endcode In order to tell the DataInteractor which function to execute these actions are made known to the DataInteractor using the CONNECT_FUNCTION macro. This example assumes that there exists an ExampleInteractor which inherits from DataInteractor. This class implements the functions AddPoint and CountClicks. The actions are introduced by implementing the virtual method ConnectActionsAndFunctions(): \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("countClicks", CountClicks); } \endcode \section HowToDataInteractor Implementation a new DataInteractor DataInteractors are to inherit from mitk::DataInteractor. Their functionality is implemented in functions that follow this interface: \code bool SomeFunctionality(StateMachineAction* , InteractionEvent*); \endcode Your functions are connected with actions by implementing the function ConnectActionsAndFunctions(), e.g. \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("enoughPoints", EnoughPoints); } \endcode Now all that is left it to write a state machine pattern and a config file as is described in the tutorials. \subsection ExampleInternalEvent PointSetDataInteractor To provide a useful example the mitk::PointSetDataInteractor is annotated with comments that describe the important parts for an implementation of a DataInteractor. This step assumes knowlege of the Interaction concept described in \ref DataInteractionPage and some background of the implementation which is described in \ref DataInteractionPageTechnical. Please refer to these pages before proceeding. DataInteractor are to inherit from mitk::DataInteractor. Their functionality is implemented in functions that follow this interface: \code bool SomeFunctionality(StateMachineAction* , InteractionEvent*); \endcode Your functions are connected with actions by implementing the function ConnectActionsAndFunctions(), e.g. \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("enoughPoints", EnoughPoints); } \endcode Now all that is left it to write a state machine pattern and a config file as is described in the tutorials. \subsection ExampleInternalEvent Example Interactor using InternalEvent A useful tool in creating DataInteractors are mitk::InternalEvents which allow to the DataInteractor send signals on its own. The following will describe how to build a DataInteractor that allows to add points until a certain number of points is reached. The number of accepted points is provided in the config file as a parameter. So we start by writing a state machine pattern that add points until it receives an InternalEvent telling it, that enough points have been added. \code <--! dead state, nothing happens any more, once we reached this --> \endcode In our config file we set the number of maximal points to 10, and define AddPointClick as a right mouse click with the ctrl button pressed. \code \endcode The implementation is desribed in the following. \see Step10.h \see Step10.cpp \dontinclude Step10.h Implementation of protected functions: \skipline protected: \until virtual void ConfigurationChanged(); ConnectActionsAndFunctions - Is inherited from mitk::InteractionStateMachine, here action strings from the xml are connected with functions in the Interactor (as seen above). In our example this looks like this: \dontinclude Step10.cpp \skipline void mitk::ExampleInteractor::ConnectActionsAndFunctions() \until } ConfigurationChanged - Is called whenever a new configuration file is loaded (by the mitk::InteractionEventHandler super class), this function allows to implement initialization code that depends on configuration values. In our example we want to set the limit of allowed points: \dontinclude Step10.cpp \skipline void mitk::ExampleInteractor::ConfigurationChang \until } Next the actual functionality of the DataInteractor is implemented, by providing one function per action, following this prototype: \code bool FUNCTION_NAME (StateMachineAction* , InteractionEvent*); \endcode \dontinclude Step10.h \skipline private: \until bool EnoughPoints(StateMac Each function has to return a boolean value. True if the action has been executed, and false if the action has not been executed. \dontinclude Step10.cpp \skipline bool mitk::ExampleInteractor::AddPoint(StateM \until //- Here we see an internal event used to signal that the point set reached the maximal number of allowed points. The event is created and added to the Dispatchers event queue. \dontinclude Step10.cpp \skipline // create internal \until positionEvent->GetSender( \note That internal events do not need any mapping to event variants. Their signal same is equivalent with the event variant. There are also two documented classes implementing a DataInteractor and an InteractionEventObserver which can be looked at for further understanding: \see mitk::PointSetDataInteractor \see mitk::DisplayInteractor Have fun with creating your own interaction and please think about contributing it to MITK! If you meet any difficulties during this step, don't hesitate to ask on the MITK mailing list mitk-users@lists.sourceforge.net! People there are kind and will try to help you. \ref Step09Page "[Previous step]" \ref TutorialPage "[Main tutorial page]" -*/ \ No newline at end of file +*/