Medical Imaging Interaction Toolkit  2018.4.99-08619e4f
Medical Imaging Interaction Toolkit
mitkEventStateMachine.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
13 #include "mitkEventStateMachine.h"
14 #include "mitkApplicationCursor.h"
15 #include "mitkInteractionEvent.h"
16 #include "mitkStateMachineAction.h"
19 #include "mitkStateMachineState.h"
21 #include "mitkUndoController.h"
22 
24  : m_IsActive(true),
25  m_UndoController(nullptr),
26  m_StateMachineContainer(nullptr),
27  m_CurrentState(nullptr),
28  m_MouseCursorSet(false)
29 {
30  if (!m_UndoController)
31  {
33 
37  m_UndoEnabled = true;
38  }
39 }
40 
41 bool mitk::EventStateMachine::LoadStateMachine(const std::string &filename, const us::Module *module)
42 {
43  if (m_StateMachineContainer != nullptr)
44  {
45  m_StateMachineContainer->Delete();
46  }
47  m_StateMachineContainer = StateMachineContainer::New();
48 
49  if (m_StateMachineContainer->LoadBehavior(filename, module))
50  {
51  m_CurrentState = m_StateMachineContainer->GetStartState();
52 
53  for (auto i = m_ConditionDelegatesMap.begin(); i != m_ConditionDelegatesMap.end();
54  ++i)
55  {
56  delete i->second;
57  }
58  m_ConditionDelegatesMap.clear();
59 
60  // clear actions map ,and connect all actions as declared in sub-class
61  for (auto i = m_ActionFunctionsMap.begin();
62  i != m_ActionFunctionsMap.end();
63  ++i)
64  {
65  delete i->second;
66  }
67  m_ActionFunctionsMap.clear();
68  for (auto i = m_ActionDelegatesMap.begin(); i != m_ActionDelegatesMap.end(); ++i)
69  {
70  delete i->second;
71  }
72  m_ActionDelegatesMap.clear();
73 
75  return true;
76  }
77  else
78  {
79  MITK_WARN << "Unable to load StateMachine from file: " << filename;
80  return false;
81  }
82 }
83 
85 {
86  if (m_StateMachineContainer != nullptr)
87  {
88  m_StateMachineContainer->Delete();
89  }
90 }
91 
92 void mitk::EventStateMachine::AddActionFunction(const std::string &action, mitk::TActionFunctor *functor)
93 {
94  if (!functor)
95  return;
96  // make sure double calls for same action won't cause memory leaks
97  delete m_ActionFunctionsMap[action];
98  auto i = m_ActionDelegatesMap.find(action);
99  if (i != m_ActionDelegatesMap.end())
100  {
101  delete i->second;
102  m_ActionDelegatesMap.erase(i);
103  }
104  m_ActionFunctionsMap[action] = functor;
105 }
106 
107 void mitk::EventStateMachine::AddActionFunction(const std::string &action, const ActionFunctionDelegate &delegate)
108 {
109  auto i = m_ActionFunctionsMap.find(action);
110  if (i != m_ActionFunctionsMap.end())
111  {
112  delete i->second;
113  m_ActionFunctionsMap.erase(i);
114  }
115 
116  delete m_ActionDelegatesMap[action];
117  m_ActionDelegatesMap[action] = delegate.Clone();
118 }
119 
120 void mitk::EventStateMachine::AddConditionFunction(const std::string &condition,
121  const ConditionFunctionDelegate &delegate)
122 {
123  m_ConditionDelegatesMap[condition] = delegate.Clone();
124 }
125 
127 {
128  if (!m_IsActive)
129  return false;
130 
131  if (!FilterEvents(event, dataNode))
132  {
133  return false;
134  }
135 
136  // Get the transition that can be executed
138 
139  // check if the current state holds a transition that works with the given event.
140  if (transition.IsNotNull())
141  {
142  // all conditions are fulfilled so we can continue with the actions
143  m_CurrentState = transition->GetNextState();
144 
145  // iterate over all actions in this transition and execute them
146  const ActionVectorType actions = transition->GetActions();
147  for (auto it = actions.cbegin(); it != actions.cend(); ++it)
148  {
149  try
150  {
151  ExecuteAction(*it, event);
152  }
153  catch (const std::exception &e)
154  {
155  MITK_ERROR << "Unhandled excaption caught in ExecuteAction(): " << e.what();
156  return false;
157  }
158  catch (...)
159  {
160  MITK_ERROR << "Unhandled excaption caught in ExecuteAction()";
161  return false;
162  }
163  }
164 
165  return true;
166  }
167  return false;
168 }
169 
171 {
172  MITK_WARN << "ConnectActionsAndFunctions in DataInteractor not implemented.\n DataInteractor will not be able to "
173  "process any events.";
174 }
175 
177 {
178  bool retVal = false;
179  ConditionDelegatesMapType::const_iterator delegateIter = m_ConditionDelegatesMap.find(condition.GetConditionName());
180  if (delegateIter != m_ConditionDelegatesMap.cend())
181  {
182  retVal = delegateIter->second->Execute(event);
183  }
184  else
185  {
186  MITK_WARN << "No implementation of condition '" << condition.GetConditionName() << "' has been found.";
187  }
188 
189  return retVal;
190 }
191 
193 {
194  if (action == nullptr)
195  {
196  return;
197  }
198 
199  // Maps Action-Name to Functor and executes the Functor.
200  ActionDelegatesMapType::const_iterator delegateIter = m_ActionDelegatesMap.find(action->GetActionName());
201  if (delegateIter != m_ActionDelegatesMap.cend())
202  {
203  delegateIter->second->Execute(action, event);
204  }
205  else
206  {
207  // try the legacy system
208  std::map<std::string, TActionFunctor *>::const_iterator functionIter =
209  m_ActionFunctionsMap.find(action->GetActionName());
210  if (functionIter != m_ActionFunctionsMap.cend())
211  {
212  functionIter->second->DoAction(action, event);
213  }
214  else
215  {
216  MITK_WARN << "No implementation of action '" << action->GetActionName() << "' has been found.";
217  }
218  }
219 }
220 
222 {
223  return m_CurrentState.GetPointer();
224 }
225 
227 {
228  if (dataNode == nullptr)
229  {
230  MITK_WARN << "EventStateMachine: Empty DataNode received along with this Event " << interactionEvent;
231  return false;
232  }
233  bool visible = false;
234  if (dataNode->GetBoolProperty("visible", visible, interactionEvent->GetSender()) == false)
235  { // property doesn't exist
236  return false;
237  }
238  return visible;
239 }
240 
242 {
243  // Map that will contain all conditions that are possibly used by the
244  // transitions
245  std::map<std::string, bool> conditionsMap;
246 
247  // Get a list of all transitions that match the given event
248  const mitk::StateMachineState::TransitionVector transitionList =
249  m_CurrentState->GetTransitionList(event->GetNameOfClass(), MapToEventVariant(event));
250 
251  // if there are not transitions, we can return nullptr here.
252  if (transitionList.empty())
253  {
254  return nullptr;
255  }
256 
257  StateMachineState::TransitionVector::const_iterator transitionIter;
258  ConditionVectorType::const_iterator conditionIter;
259  for (transitionIter = transitionList.cbegin(); transitionIter != transitionList.cend(); ++transitionIter)
260  {
261  bool allConditionsFulfilled(true);
262 
263  // Get all conditions for the current transition
264  const ConditionVectorType conditions = (*transitionIter)->GetConditions();
265  for (conditionIter = conditions.cbegin(); conditionIter != conditions.cend(); ++conditionIter)
266  {
267  bool currentConditionFulfilled(false);
268 
269  // sequentially check all conditions that we have evaluated above
270  const std::string conditionName = (*conditionIter).GetConditionName();
271 
272  // Check if the condition has already been evaluated
273  if (conditionsMap.find(conditionName) == conditionsMap.cend())
274  {
275  // if the condition has not been evaluated yet, do it now and store
276  // the result in the map
277  try
278  {
279  currentConditionFulfilled = CheckCondition((*conditionIter), event);
280  conditionsMap.insert(std::pair<std::string, bool>(conditionName, currentConditionFulfilled));
281  }
282  catch (const std::exception &e)
283  {
284  MITK_ERROR << "Unhandled excaption caught in CheckCondition(): " << e.what();
285  currentConditionFulfilled = false;
286  break;
287  }
288  catch (...)
289  {
290  MITK_ERROR << "Unhandled excaption caught in CheckCondition()";
291  currentConditionFulfilled = false;
292  break;
293  }
294  }
295  else
296  {
297  // if the condition has been evaluated before, use that result
298  currentConditionFulfilled = conditionsMap[conditionName];
299  }
300 
301  // set 'allConditionsFulfilled' under consideration of a possible
302  // inversion of the condition
303  if (currentConditionFulfilled == (*conditionIter).IsInverted())
304  {
305  allConditionsFulfilled = false;
306  break;
307  }
308  }
309 
310  // If all conditions are fulfilled, we execute this transition
311  if (allConditionsFulfilled)
312  {
313  return (*transitionIter);
314  }
315  }
316 
317  // We have found no transition that can be executed, return nullptr
318  return nullptr;
319 }
320 
322 {
323  m_CurrentState = m_StateMachineContainer->GetStartState();
324 }
325 
326 void mitk::EventStateMachine::SetMouseCursor(const char *xpm[], int hotspotX, int hotspotY)
327 {
328  // Remove previously set mouse cursor
329  if (m_MouseCursorSet)
330  {
332  }
333 
334  ApplicationCursor::GetInstance()->PushCursor(xpm, hotspotX, hotspotY);
335  m_MouseCursorSet = true;
336 }
337 
339 {
340  if (m_MouseCursorSet)
341  {
343  m_MouseCursorSet = false;
344  }
345 }
bool LoadBehavior(const std::string &fileName, const us::Module *module)
Loads XML resource.
virtual MessageAbstractDelegate1 * Clone() const =0
StateMachineTransition * GetExecutableTransition(InteractionEvent *event)
Returns the executable transition for the given event.
std::vector< mitk::StateMachineAction::Pointer > ActionVectorType
std::string GetConditionName() const
Returns the String-Id of this action.
Base class of ActionFunctors, to provide an easy to connect actions with functions.
#define MITK_ERROR
Definition: mitkLogMacros.h:20
bool HandleEvent(InteractionEvent *event, DataNode *dataNode)
virtual bool FilterEvents(InteractionEvent *interactionEvent, DataNode *dataNode)
bool LoadStateMachine(const std::string &filename, const us::Module *module=nullptr)
Loads XML resource.
void PopCursor()
Restore the previous cursor.
bool GetBoolProperty(const char *propertyKey, bool &boolValue, const mitk::BaseRenderer *renderer=nullptr) const
Convenience access method for bool properties (instances of BoolProperty)
void AddActionFunction(const std::string &action, TActionFunctor *functor)
BaseRenderer * GetSender() const
#define MITK_WARN
Definition: mitkLogMacros.h:19
void AddConditionFunction(const std::string &condition, const ConditionFunctionDelegate &delegate)
static ApplicationCursor * GetInstance()
This class is a singleton.
static StateMachineContainer * New()
virtual void ExecuteAction(StateMachineAction *action, InteractionEvent *interactionEvent)
Represents an action, that is executed after a certain event (in statemachine-mechanism) TODO: implem...
StateMachineState * GetCurrentState() const
void ResetMouseCursor()
Resets the mouse cursor (if modified by the SlicesCoordinator) to its original state.
Connects two states, and holds references to corresponding actions and conditions.
void ResetToStartState()
ResetToStartState Reset state machine to it initial starting state.
void PushCursor(const char *XPM[], int hotspotX=-1, int hotspotY=-1)
Change the current application cursor.
StateMachineState::Pointer GetStartState() const
Returns the StartState of the StateMachine.
void SetMouseCursor(const char *xpm[], int hotspotX, int hotspotY)
Sets the specified mouse cursor.
virtual MessageAbstractDelegate2 * Clone() const =0
virtual bool CheckCondition(const StateMachineCondition &condition, const InteractionEvent *interactionEvent)
std::string GetActionName() const
Returns the String-Id of this action.
Represents a condition, that has to be fulfilled in order to execute a state machine transition after...
std::vector< StateMachineTransition::Pointer > TransitionVector
std::string MapToEventVariant(InteractionEvent *interactionEvent)
Class for nodes of the DataTree.
Definition: mitkDataNode.h:57
std::vector< StateMachineCondition > ConditionVectorType