瀏覽代碼

ctkWorkflow - evaluateValidationResults and processingAfterOnEntry connected by default

Jean-Christophe Fillion-Robin 14 年之前
父節點
當前提交
c5fcb47ae1

+ 21 - 5
Libs/Core/Testing/Cpp/ctkExampleWorkflowStepUsingSignalsAndSlots.cpp

@@ -37,6 +37,8 @@ public:
   // and onExit() functions
   int numberOfTimesRanOnEntry;
   int numberOfTimesRanOnExit;
+
+  ctkWorkflowStep * Step;
 };
 
 //-----------------------------------------------------------------------------
@@ -47,15 +49,19 @@ ctkExampleWorkflowStepUsingSignalsAndSlotsPrivate::ctkExampleWorkflowStepUsingSi
 {
   this->numberOfTimesRanOnEntry = 0;
   this->numberOfTimesRanOnExit = 0;
+  this->Step = 0;
 }
 
 //-----------------------------------------------------------------------------
 // ctkExampleWorkflowStepUsingSignalsAndSlots methods
 
 //-----------------------------------------------------------------------------
-ctkExampleWorkflowStepUsingSignalsAndSlots::ctkExampleWorkflowStepUsingSignalsAndSlots(QObject* _parent) : Superclass(_parent)
+ctkExampleWorkflowStepUsingSignalsAndSlots::ctkExampleWorkflowStepUsingSignalsAndSlots(
+    ctkWorkflowStep * newStep, QObject* newParent) : Superclass(newParent)
   , d_ptr(new ctkExampleWorkflowStepUsingSignalsAndSlotsPrivate)
 {
+  Q_D(ctkExampleWorkflowStepUsingSignalsAndSlots);
+  d->Step = newStep;
 }
 
 //-----------------------------------------------------------------------------
@@ -66,15 +72,23 @@ ctkExampleWorkflowStepUsingSignalsAndSlots::~ctkExampleWorkflowStepUsingSignalsA
 //-----------------------------------------------------------------------------
 void ctkExampleWorkflowStepUsingSignalsAndSlots::validate(const QString& desiredBranchId)const
 {
+  Q_D(const ctkExampleWorkflowStepUsingSignalsAndSlots);
   // Always returns true in this simple example
-  emit validationComplete(true, desiredBranchId);
+
+  QObject::staticMetaObject.invokeMethod(
+      d->Step->ctkWorkflowStepQObject(), "validationComplete",
+      Q_ARG(bool, true), Q_ARG(QString, desiredBranchId));
 }
 
 //-----------------------------------------------------------------------------
 void ctkExampleWorkflowStepUsingSignalsAndSlots::validateFails()const
 {
+  Q_D(const ctkExampleWorkflowStepUsingSignalsAndSlots);
+
   // Always returns false in this simple example
-  emit validationComplete(false);
+  QObject::staticMetaObject.invokeMethod(
+      d->Step->ctkWorkflowStepQObject(), "validationComplete",
+      Q_ARG(bool, false));
 }
 
 //-----------------------------------------------------------------------------
@@ -89,7 +103,8 @@ void ctkExampleWorkflowStepUsingSignalsAndSlots::onEntry(const ctkWorkflowStep*
   d->numberOfTimesRanOnEntry++;
 
   // signals that we are finished
-  emit onEntryComplete();
+  QObject::staticMetaObject.invokeMethod(
+      d->Step->ctkWorkflowStepQObject(), "onEntryComplete");
 }
 
 //-----------------------------------------------------------------------------
@@ -104,7 +119,8 @@ void ctkExampleWorkflowStepUsingSignalsAndSlots::onExit(const ctkWorkflowStep* g
   d->numberOfTimesRanOnExit++;
 
   // signals that we are finished
-  emit onExitComplete();
+  QObject::staticMetaObject.invokeMethod(
+      d->Step->ctkWorkflowStepQObject(), "onExitComplete");
 }
 
 //-----------------------------------------------------------------------------

+ 2 - 1
Libs/Core/Testing/Cpp/ctkExampleWorkflowStepUsingSignalsAndSlots.h

@@ -74,7 +74,8 @@ class ctkExampleWorkflowStepUsingSignalsAndSlots : public QObject
 
 public:
   typedef QObject Superclass;
-  explicit ctkExampleWorkflowStepUsingSignalsAndSlots(QObject* parent = 0);
+  explicit ctkExampleWorkflowStepUsingSignalsAndSlots(ctkWorkflowStep * newStep,
+                                                      QObject* newParent = 0);
   virtual ~ctkExampleWorkflowStepUsingSignalsAndSlots();
 
   /// Get the values for the counters of the number of times we have

+ 37 - 28
Libs/Core/Testing/Cpp/ctkWorkflowTest2.cpp

@@ -139,42 +139,51 @@ int ctkWorkflowTest2(int argc, char * argv [] )
 
   // create the qObjects that implement the required functions, and
   // communicate with the workflow using signals and slots
-  ctkExampleWorkflowStepUsingSignalsAndSlots* qObject1 = new ctkExampleWorkflowStepUsingSignalsAndSlots;
-  ctkExampleWorkflowStepUsingSignalsAndSlots* qObject2 = new ctkExampleWorkflowStepUsingSignalsAndSlots;
-  ctkExampleWorkflowStepUsingSignalsAndSlots* qObject3 = new ctkExampleWorkflowStepUsingSignalsAndSlots;
-  ctkExampleWorkflowStepUsingSignalsAndSlots* qObject4 = new ctkExampleWorkflowStepUsingSignalsAndSlots;
+  ctkExampleWorkflowStepUsingSignalsAndSlots* qObject1 = new ctkExampleWorkflowStepUsingSignalsAndSlots(step1);
+  ctkExampleWorkflowStepUsingSignalsAndSlots* qObject2 = new ctkExampleWorkflowStepUsingSignalsAndSlots(step2);
+  ctkExampleWorkflowStepUsingSignalsAndSlots* qObject3 = new ctkExampleWorkflowStepUsingSignalsAndSlots(step3);
+  ctkExampleWorkflowStepUsingSignalsAndSlots* qObject4 = new ctkExampleWorkflowStepUsingSignalsAndSlots(step4);
 
   // use the qObjects for validation
-  QObject::connect(step1->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)), qObject1, SLOT(validate(const QString&)));
-  QObject::connect(qObject1, SIGNAL(validationComplete(bool, const QString&)), workflow, SLOT(evaluateValidationResults(bool, const QString&)));
-  QObject::connect(step2->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)), qObject2, SLOT(validate(const QString&)));
-  QObject::connect(qObject2, SIGNAL(validationComplete(bool, const QString&)), workflow, SLOT(evaluateValidationResults(bool, const QString&)));
+  QObject::connect(step1->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)),
+                   qObject1, SLOT(validate(const QString&)));
+  QObject::connect(step2->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)),
+                   qObject2, SLOT(validate(const QString&)));
   // step 3's validation will always fail
-  QObject::connect(step3->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)), qObject3, SLOT(validateFails()));
-  QObject::connect(qObject3, SIGNAL(validationComplete(bool, const QString&)), workflow, SLOT(evaluateValidationResults(bool, const QString&)));
+  QObject::connect(step3->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)),
+                   qObject3, SLOT(validateFails()));
 
-  QObject::connect(step4->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)), qObject4, SLOT(validate(const QString&)));
-  QObject::connect(qObject4, SIGNAL(validationComplete(bool, const QString&)), workflow, SLOT(evaluateValidationResults(bool, const QString&)));
+  QObject::connect(step4->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)),
+                   qObject4, SLOT(validate(const QString&)));
 
   // use the qObjects for entry processing
-  QObject::connect(step1->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject1, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject1, SIGNAL(onEntryComplete()), workflow, SLOT(processingAfterOnEntry()));
-  QObject::connect(step2->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject2, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject2, SIGNAL(onEntryComplete()), workflow, SLOT(processingAfterOnEntry()));
-  QObject::connect(step3->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject3, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject3, SIGNAL(onEntryComplete()), workflow, SLOT(processingAfterOnEntry()));
-  QObject::connect(step4->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject4, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject4, SIGNAL(onEntryComplete()), workflow, SLOT(processingAfterOnEntry()));
+  QObject::connect(
+      step1->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)),
+      qObject1, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
+  QObject::connect(
+      step2->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)),
+      qObject2, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
+  QObject::connect(
+      step3->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)),
+      qObject3, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
+  QObject::connect(
+      step4->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)),
+      qObject4, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
+
 
   // use the qObjects for exit processing
-  QObject::connect(step1->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject1, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject1, SIGNAL(onExitComplete()), workflow, SLOT(processingAfterOnExit()));
-  QObject::connect(step2->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject2, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject2, SIGNAL(onExitComplete()), workflow, SLOT(processingAfterOnExit()));
-  QObject::connect(step3->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject3, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject3, SIGNAL(onExitComplete()), workflow, SLOT(processingAfterOnExit()));
-  QObject::connect(step4->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject4, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject4, SIGNAL(onExitComplete()), workflow, SLOT(processingAfterOnExit()));
+  QObject::connect(
+      step1->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)),
+      qObject1, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
+  QObject::connect(
+      step2->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)),
+      qObject2, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
+  QObject::connect(
+      step3->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)),
+      qObject3, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
+  QObject::connect(
+      step4->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)),
+      qObject4, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
 
   step1->setHasValidateCommand(1);
   step2->setHasValidateCommand(1);

+ 86 - 343
Libs/Core/ctkWorkflow.cpp

@@ -36,250 +36,6 @@
 static ctkLogger logger("org.commontk.libs.core.ctkWorkflow");
 //--------------------------------------------------------------------------
 
-namespace
-{
-//-----------------------------------------------------------------------------
-struct forwardAndBackwardSteps
-{
-  QList<ctkWorkflowStep*> forwardSteps()
-  {
-    return this->ForwardSteps;
-  }
-
-  QList<ctkWorkflowStep*> backwardSteps()
-  {
-    return this->BackwardSteps;
-  }
-
-  QList<QString> forwardBranchIds()
-  {
-    return this->ForwardBranchIds;
-  }
-
-  QList<QString> backwardBranchIds()
-  {
-    return this->BackwardBranchIds;
-  }
-
-  void appendForwardStep(ctkWorkflowStep* step, QString id)
-  {
-    this->ForwardSteps.append(step);
-    this->ForwardBranchIds.append(id);
-  }
-
-  void appendBackwardStep(ctkWorkflowStep* step, QString id)
-  {
-    this->BackwardSteps.append(step);
-    this->BackwardBranchIds.append(id);
-  }
-
-  QString firstForwardBranchId()
-  {
-    if (this->ForwardBranchIds.isEmpty())
-      {
-      return QString();
-      }
-    else
-      {
-    return this->ForwardBranchIds.first();
-      }
-  }
-
-  ctkWorkflowStep* forwardStep(QString branchId)
-  {
-    int index = this->ForwardBranchIds.indexOf(branchId);
-    if (index != -1)
-      {
-      return ForwardSteps.at(index);
-      }
-    else
-      {
-      return 0;
-      }
-  }
-
-  QString backwardBranchId(ctkWorkflowStep* step)
-  {
-    int index = this->BackwardSteps.indexOf(step);
-    if (index != -1)
-      {
-      return BackwardBranchIds.at(index);
-      }
-    else
-      {
-      return QString();
-      }
-  }
-
-  QString forwardBranchId(ctkWorkflowStep* step)
-  {
-    int index = this->ForwardSteps.indexOf(step);
-    if (index != -1)
-      {
-      return ForwardBranchIds.at(index);
-      }
-    else
-      {
-      return QString();
-      }
-  }
-
-private:
-  QList<ctkWorkflowStep*> ForwardSteps;
-  QList<ctkWorkflowStep*> BackwardSteps;
-
-  QList<QString> ForwardBranchIds;
-  QList<QString> BackwardBranchIds;
-
-};
-}
-
-//-----------------------------------------------------------------------------
-class ctkWorkflowPrivate
-{
-  Q_DECLARE_PUBLIC(ctkWorkflow);
-protected:
-  ctkWorkflow* const q_ptr;
-public:
-  ctkWorkflowPrivate(ctkWorkflow& object);
-  ~ctkWorkflowPrivate();
-
-  /// \brief Add a step to the workflow
-  ///
-  /// \note The step's components will be automatically be added to the state machine
-  /// (i.e. the processingState state, validationState state, validationTransition transition
-  /// and validationFailedtransition transition.
-  ///
-  /// \return True or False indicating whether the method was successful.
-  void addStep(ctkWorkflowStep* step);
-
-  /// \brief Returns whether a transition has been previously added with the same origin,
-  /// destination and directionality
-  bool hasDuplicateTransition(ctkWorkflowStep* origin, ctkWorkflowStep* destination,
-                              const ctkWorkflow::TransitionDirectionality directionality);
-
-  /// \brief Returns whether a transition has been previously added with the same origin and branch
-  /// id (for forward transitions) or with the same destination and branch id (for backward transitions
-  bool hasTransitionWithSameBranchId(ctkWorkflowStep* origin, ctkWorkflowStep* destination,
-                                     const QString& branchId,
-                                     const ctkWorkflow::TransitionDirectionality directionality);
-
-  /// \brief Creates a transition from the origin to the destinatio.
-  ///
-  /// More specifically, the transition is from the \a origin's validation state to the \a
-  /// destination's processing state, and is of type ctkWorkflowTransition::TransitionToNextStep
-  ///
-  /// The destination step should semantically be a next step, i.e. from a workflow perspective, the
-  /// \a destination step is meant to appear after the \a origin step.
-  ///
-  /// Returns true/false indicating whether the method was successful.
-  void createTransitionToNextStep(ctkWorkflowStep* origin,
-                                  ctkWorkflowStep* destination,
-                                  const QString& branchId = "");
-
-  /// \brief Creates a transition from the destination to the origin
-  ///
-  /// More specifically, the transition is from the \a destination's processing state to the \a
-  /// origin's processing state, and is of type ctkWorkflowTransition::TransitionToPreviousStep
-  ///
-  /// The destination step should semantically be a next step, i.e. from a workflow perspective, the
-  /// \a destination step is meant to appear after the \a origin step.
-  ///
-  /// Returns true/false indicating whether the method was successful.
-  void createTransitionToPreviousStep(ctkWorkflowStep* origin,
-                                      ctkWorkflowStep* destination,
-                                      const QString& branchId = "");
-
-  /// \brief Creates a transition from the goTo step to the step from which the attempt to go to the
-  /// goTo step was initiated.
-  ///
-  /// More specifically, the transition is from the \a goTo step's processing state to the \a
-  /// starting step's processing state, and is of type ctkWorkflowTransition::TransitionToPreviousStartingStep
-  ///
-  /// Returns true/false indicating whether the method was successful.
-  void createTransitionToPreviousStartingStep(ctkWorkflowStep* startingStep,
-                                              ctkWorkflowStep* currentStep);
-  ///
-  void validateInternal(ctkWorkflowStep* step);
-
-  /// \brief Performs computation required when entering this step.
-  ///
-  /// Does some sanity checks and then either calls onEntry() or emits the invokeOnEntryCommand(),
-  /// depending on whether the user indicates that there is an onEntryCommand.
-  void onEntryInternal(ctkWorkflowStep* step, ctkWorkflowStep* comingFrom,
-                       const ctkWorkflowInterstepTransition::InterstepTransitionType& transitionType);
-
-  /// \brief Performs computation required when exiting this step.
-  ///
-  /// Does some sanity checks and then either calls onExit() or emits the invokeOnExitCommand(),
-  /// depending on whether the user indicates that there is an onExitCommand.
-  void onExitInternal(ctkWorkflowStep* step, ctkWorkflowStep* goingTo,
-                      const ctkWorkflowInterstepTransition::InterstepTransitionType& transitionType);
-
-  /// Get the step in the workflow with a given id.
-  ctkWorkflowStep* stepFromId(const QString& id)const;
-
-  /// Get the step that a state belongs to (if any)
-  ctkWorkflowStep* stepFromState(const QAbstractState* state);
-
-  /// Get the number of forward steps from the given step
-  int numberOfForwardSteps(ctkWorkflowStep* step);
-
-  /// Get the number of backward steps from the given step
-  int numberOfBackwardSteps(ctkWorkflowStep* step);
-
-  /// Get the ids of the steps that directly follow the given step.
-  QList<QString> forwardBranchIds(ctkWorkflowStep* step)const;
-
-  /// Get the ids of the steps that directly preceed the given step.
-  QList<QString> backwardBranchIds(ctkWorkflowStep* step)const;
-
-  /// Determines whether there exists a path from the origin step (the current step by default) to
-  /// the step with the given goalId
-  bool pathExists(const QString& goalId, ctkWorkflowStep* origin = 0)const;
-
-  /// Determines whether there exists a path from the current step's next step (as given by the
-  /// branchId) to the step with the given goalId
-  bool pathExistsFromNextStep(const QString& goalId, const QString& branchId)const;
-
-  QStateMachine* StateMachine;
-
-  // Maintain a list of pointers to the steps in the workflow,
-  // along with their forward and backward transitions
-  QMap<ctkWorkflowStep*, forwardAndBackwardSteps*>         StepToForwardAndBackwardStepMap;
-
-  // ... and its associated convenient typedef
-  typedef QMap<ctkWorkflowStep*, forwardAndBackwardSteps*> StepToForwardAndBackwardStepMapType;
-  typedef QList<ctkWorkflowStep*>                          StepListType;
-
-  // Maintain a map of <state, step> key/value pairs, to find the step
-  // that a given state belongs to
-  typedef QMap<const QAbstractState*, ctkWorkflowStep*>           StateToStepMapType;
-  typedef QMap<const QAbstractState*, ctkWorkflowStep*>::iterator StateToStepMapIterator;
-  StateToStepMapType                                              StateToStepMap;
-
-  ctkWorkflowStep*                         InitialStep;
-  ctkWorkflowStep*                         CurrentStep;
-  QMap<ctkWorkflowStep*, ctkWorkflowStep*> StepToPreviousStepMap;
-
-  // Used when performing a transition
-  ctkWorkflowStep*                                        OriginStep;
-  ctkWorkflowStep*                                        DestinationStep;
-  ctkWorkflowInterstepTransition::InterstepTransitionType TransitionType;
-
-  QString          DesiredBranchId; // Desired branchId specified when invoking goForward
-
-  ctkWorkflowStep* GoToStep;   // Desired step when attempting to go to a finish step
-
-  ctkWorkflowStep* StartingStep; // Current step when we began the attempt to go to the desired finish step
-
-  // Temporary transition after successfully going to finish step, to get us back to the starting step
-  ctkWorkflowInterstepTransition* TransitionToPreviousStartingStep;
-
-  QString ARTIFICIAL_BRANCH_ID_PREFIX;
-
-};
-
 // --------------------------------------------------------------------------
 // ctkWorkflowPrivate methods
 
@@ -330,7 +86,17 @@ void ctkWorkflowPrivate::addStep(ctkWorkflowStep* step)
 
   // Setup the signal/slot that triggers the evaluation of the validation results
   // after validate(const QString&) is called
-  q->connectStep(step);
+  this->connect(
+      step->ctkWorkflowStepQObject(), SIGNAL(validationComplete(bool, const QString&)),
+      q, SLOT(evaluateValidationResults(bool, const QString&)));
+
+  this->connect(
+      step->ctkWorkflowStepQObject(), SIGNAL(onEntryComplete()),
+      SLOT(processingAfterOnEntry()));
+
+  this->connect(
+      step->ctkWorkflowStepQObject(), SIGNAL(onExitComplete()),
+      SLOT(processingAfterOnExit()));
 }
 
 // --------------------------------------------------------------------------
@@ -577,6 +343,62 @@ void ctkWorkflowPrivate::onEntryInternal(
 }
 
 // --------------------------------------------------------------------------
+void ctkWorkflowPrivate::processingAfterOnEntry()
+{
+  Q_Q(ctkWorkflow);
+
+  logger.debug("processingAfterOnEntry");
+
+  if (!this->DestinationStep)
+    {
+    logger.error("processingAfterOnEntry - Called processingAfterOnEntry without "
+                 "having set a destination step");
+    return;
+    }
+
+  // Update the currentStep and previous step
+  this->CurrentStep = this->DestinationStep;
+
+  // Reset the pointers used internally for performing a transition
+  this->OriginStep = 0;
+  this->DestinationStep = 0;
+
+  // // Reset the pointers used internally for performing a transition
+  // // back to the starting step
+  // if (d->TransitionToPreviousStartingStep)
+  //   {
+
+  //   std::cout << "TRANSITION TO PREVIOUS STARTING STEP EXISTS" << std::endl;
+  //   //d->TransitionToPreviousStartingStep->sourceState()->removeTransition(d->TransitionToPreviousStartingStep);
+  //   //std::cout << "removed" << std::endl;
+  //   // d->TransitionToPreviousStartingStep = 0;
+  //   //destination->processingState()->removeTransition(d->TransitionToPreviousStartingStep);
+  //   //delete d->TransitionToPreviousStartingStep;
+  //   // d->TransitionToPreviousStartingStep = 0;
+  //   std::cout << "here" << std::endl;
+  //   }
+
+  // If we are trying to get to the finish step, then check if we are
+  // finished.
+  if (this->GoToStep)
+    {
+    if (this->CurrentStep == this->GoToStep)
+      {
+      q->goToStepSucceeded();
+      }
+    // if we're not finished, continue transitioning to the next step
+    else
+      {
+      q->goForward();
+      }
+    }
+  else
+    {
+    emit q->currentStepChanged(this->CurrentStep);
+    }
+}
+
+// --------------------------------------------------------------------------
 void ctkWorkflowPrivate::onExitInternal(
     ctkWorkflowStep* step,
     ctkWorkflowStep* goingTo,
@@ -603,6 +425,25 @@ void ctkWorkflowPrivate::onExitInternal(
 }
 
 // --------------------------------------------------------------------------
+void ctkWorkflowPrivate::processingAfterOnExit()
+{
+  // enter the destination step if we have one
+  if (this->DestinationStep)
+    {
+    this->onEntryInternal(this->DestinationStep, this->OriginStep, this->TransitionType);
+    }
+  // reset the pointers used internally for performing a transition if we're done
+  else
+    {
+    this->OriginStep = 0;
+    this->DestinationStep = 0;
+    // we've exited the CurrentStep and haven't gone into another step, so we no longer have a
+    // currentStep.
+    this->CurrentStep = 0;
+    }
+}
+
+// --------------------------------------------------------------------------
 ctkWorkflowStep* ctkWorkflowPrivate::stepFromState(const QAbstractState* state)
 {
   if (state)
@@ -786,27 +627,6 @@ bool ctkWorkflow::hasTransition(ctkWorkflowStep* origin, ctkWorkflowStep* destin
 }
 
 // --------------------------------------------------------------------------
-void ctkWorkflow::connectStep(ctkWorkflowStep* step)
-{
-  Q_ASSERT(step);
-
-  if (!step->hasValidateCommand())
-    {
-    QObject::connect(step->ctkWorkflowStepQObject(), SIGNAL(validationComplete(bool, const QString&)), this, SLOT(evaluateValidationResults(bool, const QString&)));
-    }
-
-  if (!step->hasOnEntryCommand())
-    {
-    QObject::connect(step->ctkWorkflowStepQObject(), SIGNAL(onEntryComplete()), this, SLOT(processingAfterOnEntry()));
-    }
-
-  if (!step->hasOnExitCommand())
-    {
-    QObject::connect(step->ctkWorkflowStepQObject(), SIGNAL(onExitComplete()), this, SLOT(processingAfterOnExit()));
-    }
-}
-
-// --------------------------------------------------------------------------
 QList<ctkWorkflowStep*> ctkWorkflow::forwardSteps(ctkWorkflowStep* step)const
 {
   Q_D(const ctkWorkflow);
@@ -1175,83 +995,6 @@ void ctkWorkflow::performTransitionBetweenSteps()
 }
 
 // --------------------------------------------------------------------------
-void ctkWorkflow::processingAfterOnExit()
-{
-  Q_D(ctkWorkflow);
-
-  // enter the destination step if we have one
-  if (d->DestinationStep)
-    {
-    d->onEntryInternal(d->DestinationStep, d->OriginStep, d->TransitionType);
-    }
-  // reset the pointers used internally for performing a transition if we're done
-  else
-    {
-    d->OriginStep = 0;
-    d->DestinationStep = 0;
-    // we've exited the CurrentStep and haven't gone into another step, so we no longer have a
-    // currentStep.
-    d->CurrentStep = 0;
-    }
-}
-
-// --------------------------------------------------------------------------
-void ctkWorkflow::processingAfterOnEntry()
-{
-  logger.debug("processingAfterOnEntry");
-
-  Q_D(ctkWorkflow);
-
-  if (!d->DestinationStep)
-    {
-    logger.error("processingAfterOnEntry - Called processingAfterOnEntry without "
-                 "having set a destination step");
-    return;
-    }
-
-  // Update the currentStep and previous step
-  d->CurrentStep = d->DestinationStep;
-
-  // Reset the pointers used internally for performing a transition
-  d->OriginStep = 0;
-  d->DestinationStep = 0;
-
-  // // Reset the pointers used internally for performing a transition
-  // // back to the starting step
-  // if (d->TransitionToPreviousStartingStep)
-  //   {
-
-  //   std::cout << "TRANSITION TO PREVIOUS STARTING STEP EXISTS" << std::endl;
-  //   //d->TransitionToPreviousStartingStep->sourceState()->removeTransition(d->TransitionToPreviousStartingStep);
-  //   //std::cout << "removed" << std::endl;
-  //   // d->TransitionToPreviousStartingStep = 0;
-  //   //destination->processingState()->removeTransition(d->TransitionToPreviousStartingStep);
-  //   //delete d->TransitionToPreviousStartingStep;
-  //   // d->TransitionToPreviousStartingStep = 0;
-  //   std::cout << "here" << std::endl;
-  //   }
-
-  // If we are trying to get to the finish step, then check if we are
-  // finished.
-  if (d->GoToStep)
-    {
-    if (d->CurrentStep == d->GoToStep)
-      {
-      this->goToStepSucceeded();
-      }
-    // if we're not finished, continue transitioning to the next step
-    else
-      {
-      this->goForward();
-      }
-    }
-  else
-    {
-    emit this->currentStepChanged(d->CurrentStep);
-    }
-}
-
-// --------------------------------------------------------------------------
 void ctkWorkflow::goToStepSucceeded()
 {
   Q_D(ctkWorkflow);

+ 0 - 9
Libs/Core/ctkWorkflow.h

@@ -177,12 +177,6 @@ public slots:
   /// If the validation is successful, then this slot begins the transition to the next step.
   virtual void evaluateValidationResults(bool validationSucceeded, const QString& branchId);
 
-  /// \brief Workflow processing executed after a step's onEntry function is run.
-  virtual void processingAfterOnEntry();
-
-  /// \brief Workflow processing executed after a step's onExit function is run.
-  virtual void processingAfterOnExit();
-
 protected:
 
   void goToNextStepAfterSuccessfulValidation(const QString& branchId);
@@ -197,9 +191,6 @@ protected:
  
   /// \brief Goes to the step from which the attempt to go to the 'goTo' step was initiated
   void goFromGoToStepToStartingStep();
-
-  /// \brief Performs required connections between the step and this workflow
-  virtual void connectStep(ctkWorkflowStep* step);
  
 protected slots:
 

+ 8 - 8
Libs/Core/ctkWorkflowStep.h

@@ -27,8 +27,11 @@ class QState;
 
 // CTK includes
 #include "ctkPimpl.h"
-#include "ctkCoreExport.h"
+#include "ctkWorkflow_p.h"
 #include "ctkWorkflowTransitions.h"
+
+#include "ctkCoreExport.h"
+
 class ctkWorkflow;
 
 class ctkWorkflowStepPrivate;
@@ -172,8 +175,9 @@ protected:
   /// 1) Reimplement the validate(const QString&) method in a subclass of
   /// ctkWorkflowStep, following these instructions:
   /// <ul>
-  ///   <li>emit the signal ctkWorkflowStep::validateComplete(bool, const QString&) (true on successful validation,
-  /// false on failure; the QString is the desired branchId to use with branching workflows)</li>
+  ///   <li>invoke the superclass method ctkWorkflowStep::validateComplete(bool, const QString&)
+  /// (true on successful validation, false on failure; the QString is the desired branchId to use
+  /// with branching workflows)</li>
   /// </ul>
   //
   /// OR:
@@ -183,14 +187,10 @@ protected:
   ///  <li>Call setHasValidateCommand(1) on the step
   ///  <li>Create a slot foo() associated with any QObject*, following these instructions:</li>
   ///  <ul>
-  ///     <li>Emit a signal bar(int, const QString&) (true on successful validation, false on
-  /// failure)</li>
-  ///     <li>Set the following two connections:</li>
+  ///     <li>Set the following connection:</li>
   ///     <ul>
   ///       <li>QObject::connect(step, SIGNAL(invokeValidateCommand(const QString&)), object,
   /// SLOT(foo(const QString&)))</li>
-  ///       <li>QObject::connect(object, SIGNAl(bar(bool, const QString&)), workflow,
-  /// SLOT(evaluateValidationResults(bool, const QString&)))</li>
   ///      </ul>
   ///    </ul>
   ///  </ul>

+ 291 - 0
Libs/Core/ctkWorkflow_p.h

@@ -0,0 +1,291 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+ 
+#ifndef __ctkWorkflow_p_h
+#define __ctkWorkflow_p_h
+
+// Qt includes
+#include <QObject>
+#include <QString>
+#include <QList>
+#include <QMap>
+
+// CTK includes
+#include "ctkWorkflow.h"
+#include "ctkWorkflowTransitions.h"
+
+class QStateMachine;
+class ctkWorkflowStep;
+//class ctkWorkflow;
+//enum ctkWorkflow::TransitionDirectionality;
+
+//-----------------------------------------------------------------------------
+struct forwardAndBackwardSteps
+{
+  QList<ctkWorkflowStep*> forwardSteps()
+  {
+    return this->ForwardSteps;
+  }
+
+  QList<ctkWorkflowStep*> backwardSteps()
+  {
+    return this->BackwardSteps;
+  }
+
+  QList<QString> forwardBranchIds()
+  {
+    return this->ForwardBranchIds;
+  }
+
+  QList<QString> backwardBranchIds()
+  {
+    return this->BackwardBranchIds;
+  }
+
+  void appendForwardStep(ctkWorkflowStep* step, QString id)
+  {
+    this->ForwardSteps.append(step);
+    this->ForwardBranchIds.append(id);
+  }
+
+  void appendBackwardStep(ctkWorkflowStep* step, QString id)
+  {
+    this->BackwardSteps.append(step);
+    this->BackwardBranchIds.append(id);
+  }
+
+  QString firstForwardBranchId()
+  {
+    if (this->ForwardBranchIds.isEmpty())
+      {
+      return QString();
+      }
+    else
+      {
+    return this->ForwardBranchIds.first();
+      }
+  }
+
+  ctkWorkflowStep* forwardStep(QString branchId)
+  {
+    int index = this->ForwardBranchIds.indexOf(branchId);
+    if (index != -1)
+      {
+      return ForwardSteps.at(index);
+      }
+    else
+      {
+      return 0;
+      }
+  }
+
+  QString backwardBranchId(ctkWorkflowStep* step)
+  {
+    int index = this->BackwardSteps.indexOf(step);
+    if (index != -1)
+      {
+      return BackwardBranchIds.at(index);
+      }
+    else
+      {
+      return QString();
+      }
+  }
+
+  QString forwardBranchId(ctkWorkflowStep* step)
+  {
+    int index = this->ForwardSteps.indexOf(step);
+    if (index != -1)
+      {
+      return ForwardBranchIds.at(index);
+      }
+    else
+      {
+      return QString();
+      }
+  }
+
+private:
+  QList<ctkWorkflowStep*> ForwardSteps;
+  QList<ctkWorkflowStep*> BackwardSteps;
+
+  QList<QString> ForwardBranchIds;
+  QList<QString> BackwardBranchIds;
+
+};
+
+// --------------------------------------------------------------------------
+class ctkWorkflowPrivate : public QObject
+{
+  Q_OBJECT
+  Q_DECLARE_PUBLIC(ctkWorkflow);
+protected:
+  ctkWorkflow* const q_ptr;
+public:
+  ctkWorkflowPrivate(ctkWorkflow& object);
+  ~ctkWorkflowPrivate();
+
+  /// \brief Add a step to the workflow
+  ///
+  /// \note The step's components will be automatically be added to the state machine
+  /// (i.e. the processingState state, validationState state, validationTransition transition
+  /// and validationFailedtransition transition.
+  ///
+  /// \return True or False indicating whether the method was successful.
+  void addStep(ctkWorkflowStep* step);
+
+  /// \brief Returns whether a transition has been previously added with the same origin,
+  /// destination and directionality
+  bool hasDuplicateTransition(ctkWorkflowStep* origin, ctkWorkflowStep* destination,
+                              const ctkWorkflow::TransitionDirectionality directionality);
+
+  /// \brief Returns whether a transition has been previously added with the same origin and branch
+  /// id (for forward transitions) or with the same destination and branch id (for backward transitions
+  bool hasTransitionWithSameBranchId(ctkWorkflowStep* origin, ctkWorkflowStep* destination,
+                                     const QString& branchId,
+                                     const ctkWorkflow::TransitionDirectionality directionality);
+
+  /// \brief Creates a transition from the origin to the destinatio.
+  ///
+  /// More specifically, the transition is from the \a origin's validation state to the \a
+  /// destination's processing state, and is of type ctkWorkflowTransition::TransitionToNextStep
+  ///
+  /// The destination step should semantically be a next step, i.e. from a workflow perspective, the
+  /// \a destination step is meant to appear after the \a origin step.
+  ///
+  /// Returns true/false indicating whether the method was successful.
+  void createTransitionToNextStep(ctkWorkflowStep* origin,
+                                  ctkWorkflowStep* destination,
+                                  const QString& branchId = "");
+
+  /// \brief Creates a transition from the destination to the origin
+  ///
+  /// More specifically, the transition is from the \a destination's processing state to the \a
+  /// origin's processing state, and is of type ctkWorkflowTransition::TransitionToPreviousStep
+  ///
+  /// The destination step should semantically be a next step, i.e. from a workflow perspective, the
+  /// \a destination step is meant to appear after the \a origin step.
+  ///
+  /// Returns true/false indicating whether the method was successful.
+  void createTransitionToPreviousStep(ctkWorkflowStep* origin,
+                                      ctkWorkflowStep* destination,
+                                      const QString& branchId = "");
+
+  /// \brief Creates a transition from the goTo step to the step from which the attempt to go to the
+  /// goTo step was initiated.
+  ///
+  /// More specifically, the transition is from the \a goTo step's processing state to the \a
+  /// starting step's processing state, and is of type ctkWorkflowTransition::TransitionToPreviousStartingStep
+  ///
+  /// Returns true/false indicating whether the method was successful.
+  void createTransitionToPreviousStartingStep(ctkWorkflowStep* startingStep,
+                                              ctkWorkflowStep* currentStep);
+  ///
+  void validateInternal(ctkWorkflowStep* step);
+
+  /// \brief Performs computation required when entering this step.
+  ///
+  /// Does some sanity checks and then either calls onEntry() or emits the invokeOnEntryCommand(),
+  /// depending on whether the user indicates that there is an onEntryCommand.
+  void onEntryInternal(ctkWorkflowStep* step, ctkWorkflowStep* comingFrom,
+                       const ctkWorkflowInterstepTransition::InterstepTransitionType& transitionType);
+
+  /// \brief Performs computation required when exiting this step.
+  ///
+  /// Does some sanity checks and then either calls onExit() or emits the invokeOnExitCommand(),
+  /// depending on whether the user indicates that there is an onExitCommand.
+  void onExitInternal(ctkWorkflowStep* step, ctkWorkflowStep* goingTo,
+                      const ctkWorkflowInterstepTransition::InterstepTransitionType& transitionType);
+
+  /// Get the step in the workflow with a given id.
+  ctkWorkflowStep* stepFromId(const QString& id)const;
+
+  /// Get the step that a state belongs to (if any)
+  ctkWorkflowStep* stepFromState(const QAbstractState* state);
+
+  /// Get the number of forward steps from the given step
+  int numberOfForwardSteps(ctkWorkflowStep* step);
+
+  /// Get the number of backward steps from the given step
+  int numberOfBackwardSteps(ctkWorkflowStep* step);
+
+  /// Get the ids of the steps that directly follow the given step.
+  QList<QString> forwardBranchIds(ctkWorkflowStep* step)const;
+
+  /// Get the ids of the steps that directly preceed the given step.
+  QList<QString> backwardBranchIds(ctkWorkflowStep* step)const;
+
+  /// Determines whether there exists a path from the origin step (the current step by default) to
+  /// the step with the given goalId
+  bool pathExists(const QString& goalId, ctkWorkflowStep* origin = 0)const;
+
+  /// Determines whether there exists a path from the current step's next step (as given by the
+  /// branchId) to the step with the given goalId
+  bool pathExistsFromNextStep(const QString& goalId, const QString& branchId)const;
+
+public slots:
+
+  /// \brief Workflow processing executed after a step's onEntry function is run.
+  void processingAfterOnEntry();
+  
+  /// \brief Workflow processing executed after a step's onExit function is run.
+  void processingAfterOnExit();
+
+public:
+
+  QStateMachine* StateMachine;
+
+  // Maintain a list of pointers to the steps in the workflow,
+  // along with their forward and backward transitions
+  QMap<ctkWorkflowStep*, forwardAndBackwardSteps*>         StepToForwardAndBackwardStepMap;
+
+  // ... and its associated convenient typedef
+  typedef QMap<ctkWorkflowStep*, forwardAndBackwardSteps*> StepToForwardAndBackwardStepMapType;
+  typedef QList<ctkWorkflowStep*>                          StepListType;
+
+  // Maintain a map of <state, step> key/value pairs, to find the step
+  // that a given state belongs to
+  typedef QMap<const QAbstractState*, ctkWorkflowStep*>           StateToStepMapType;
+  typedef QMap<const QAbstractState*, ctkWorkflowStep*>::iterator StateToStepMapIterator;
+  StateToStepMapType                                              StateToStepMap;
+
+  ctkWorkflowStep*                         InitialStep;
+  ctkWorkflowStep*                         CurrentStep;
+  QMap<ctkWorkflowStep*, ctkWorkflowStep*> StepToPreviousStepMap;
+
+  // Used when performing a transition
+  ctkWorkflowStep*                                        OriginStep;
+  ctkWorkflowStep*                                        DestinationStep;
+  ctkWorkflowInterstepTransition::InterstepTransitionType TransitionType;
+
+  QString          DesiredBranchId; // Desired branchId specified when invoking goForward
+
+  ctkWorkflowStep* GoToStep;   // Desired step when attempting to go to a finish step
+
+  ctkWorkflowStep* StartingStep; // Current step when we began the attempt to go to the desired finish step
+
+  // Temporary transition after successfully going to finish step, to get us back to the starting step
+  ctkWorkflowInterstepTransition* TransitionToPreviousStartingStep;
+
+  QString ARTIFICIAL_BRANCH_ID_PREFIX;
+
+};
+
+#endif

+ 6 - 12
Libs/Widgets/Testing/Cpp/ctkExampleUseOfWorkflowWidgetUsingSignalsAndSlots.cpp

@@ -92,9 +92,12 @@ int ctkExampleUseOfWorkflowWidgetUsingSignalsAndSlots(int argc, char * argv [] )
 
   // create the qObjects that implement the required functions for
   // each step, and communicate with the workflow using signals and slots
-  ctkExampleWorkflowWidgetStepUsingSignalsAndSlots* qObject1 = new ctkExampleWorkflowWidgetStepUsingSignalsAndSlots;
-  ctkExampleWorkflowWidgetStepUsingSignalsAndSlots* qObject2 = new ctkExampleWorkflowWidgetStepUsingSignalsAndSlots;
-  ctkExampleWorkflowWidgetStepUsingSignalsAndSlots* qObject3 = new ctkExampleWorkflowWidgetStepUsingSignalsAndSlots;
+  ctkExampleWorkflowWidgetStepUsingSignalsAndSlots* qObject1 =
+      new ctkExampleWorkflowWidgetStepUsingSignalsAndSlots(testStep1);
+  ctkExampleWorkflowWidgetStepUsingSignalsAndSlots* qObject2 =
+      new ctkExampleWorkflowWidgetStepUsingSignalsAndSlots(testStep2);
+  ctkExampleWorkflowWidgetStepUsingSignalsAndSlots* qObject3 =
+      new ctkExampleWorkflowWidgetStepUsingSignalsAndSlots(testStep3);
 
   // set the widget for each qObject
   qObject1->setWidget(testStep1->stepArea());
@@ -103,27 +106,18 @@ int ctkExampleUseOfWorkflowWidgetUsingSignalsAndSlots(int argc, char * argv [] )
 
   // use the qObjects for validation
   QObject::connect(testStep1->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)), qObject1, SLOT(validate(const QString&)));
-  QObject::connect(qObject1, SIGNAL(validationComplete(bool, const QString&)), workflow, SLOT(evaluateValidationResults(bool, const QString&)));
   QObject::connect(testStep2->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)), qObject2, SLOT(validate(const QString&)));
-  QObject::connect(qObject2, SIGNAL(validationComplete(bool, const QString&)), workflow, SLOT(evaluateValidationResults(bool, const QString&)));
   QObject::connect(testStep3->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)), qObject3, SLOT(validate(const QString&)));
-  QObject::connect(qObject3, SIGNAL(validationComplete(bool, const QString&)), workflow, SLOT(evaluateValidationResults(bool, const QString&)));
 
   // use the qObjects for entry processing
   QObject::connect(testStep1->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject1, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject1, SIGNAL(onEntryComplete()), workflow, SLOT(processingAfterOnEntry()));
   QObject::connect(testStep2->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject2, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject2, SIGNAL(onEntryComplete()), workflow, SLOT(processingAfterOnEntry()));
   QObject::connect(testStep3->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject3, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject3, SIGNAL(onEntryComplete()), workflow, SLOT(processingAfterOnEntry()));
 
   // use the qObjects for exit processing
   QObject::connect(testStep1->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject1, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject1, SIGNAL(onExitComplete()), workflow, SLOT(processingAfterOnExit()));
   QObject::connect(testStep2->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject2, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject2, SIGNAL(onExitComplete()), workflow, SLOT(processingAfterOnExit()));
   QObject::connect(testStep3->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject3, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject3, SIGNAL(onExitComplete()), workflow, SLOT(processingAfterOnExit()));
 
   // use the qObjects for populating the stepWidgetsList
   QObject::connect(testStep1->ctkWorkflowAbstractWidgetStepQObject(), SIGNAL(invokeCreateUserInterfaceCommand()), qObject1, SLOT(createUserInterface()));

+ 16 - 4
Libs/Widgets/Testing/Cpp/ctkExampleWorkflowWidgetStepUsingSignalsAndSlots.cpp

@@ -50,6 +50,8 @@ public:
   int numberOfTimesRanOnEntry;
   int numberOfTimesRanOnExit;
 
+  ctkWorkflowStep * Step;
+
 };
 
 //-----------------------------------------------------------------------------
@@ -66,15 +68,21 @@ ctkExampleWorkflowWidgetStepUsingSignalsAndSlotsPrivate::ctkExampleWorkflowWidge
 
   this->numberOfTimesRanOnEntry = 0;
   this->numberOfTimesRanOnExit = 0;
+
+  this->Step = 0;
 }
 
 //-----------------------------------------------------------------------------
 // ctkExampleWorkflowWidgetStepUsingSignalsAndSlots methods
 
 //-----------------------------------------------------------------------------
-ctkExampleWorkflowWidgetStepUsingSignalsAndSlots::ctkExampleWorkflowWidgetStepUsingSignalsAndSlots(QObject* _parent) : Superclass(_parent)
+ctkExampleWorkflowWidgetStepUsingSignalsAndSlots::
+    ctkExampleWorkflowWidgetStepUsingSignalsAndSlots(ctkWorkflowStep* newStep, QObject* newParent) :
+    Superclass(newParent)
   , d_ptr(new ctkExampleWorkflowWidgetStepUsingSignalsAndSlotsPrivate)
 {
+  Q_D(ctkExampleWorkflowWidgetStepUsingSignalsAndSlots);
+  d->Step = newStep;
 }
 
 //-----------------------------------------------------------------------------
@@ -106,7 +114,8 @@ void ctkExampleWorkflowWidgetStepUsingSignalsAndSlots::onEntry(
   d->numberOfTimesRanOnEntry++;
 
   // signals that we are finished
-  emit onEntryComplete();
+  QObject::staticMetaObject.invokeMethod(
+      d->Step->ctkWorkflowStepQObject(), "onEntryComplete");
 }
 
 //-----------------------------------------------------------------------------
@@ -123,7 +132,8 @@ void ctkExampleWorkflowWidgetStepUsingSignalsAndSlots::onExit(
   d->numberOfTimesRanOnExit++;
 
   // signals that we are finished
-  emit onExitComplete();
+  QObject::staticMetaObject.invokeMethod(
+      d->Step->ctkWorkflowStepQObject(), "onExitComplete");
 }
 
 //-----------------------------------------------------------------------------
@@ -197,6 +207,8 @@ void ctkExampleWorkflowWidgetStepUsingSignalsAndSlots::validate(const QString& d
     }
 
   // return the validation results
-  emit validationComplete(retVal, desiredBranchId);
+  QObject::staticMetaObject.invokeMethod(
+      d->Step->ctkWorkflowStepQObject(), "validationComplete",
+      Q_ARG(bool, retVal), Q_ARG(QString, desiredBranchId));
 }
 

+ 2 - 1
Libs/Widgets/Testing/Cpp/ctkExampleWorkflowWidgetStepUsingSignalsAndSlots.h

@@ -80,7 +80,8 @@ class ctkExampleWorkflowWidgetStepUsingSignalsAndSlots : public QObject
 public:
 
   typedef QObject Superclass;
-  explicit ctkExampleWorkflowWidgetStepUsingSignalsAndSlots(QObject* parent = 0);
+  explicit ctkExampleWorkflowWidgetStepUsingSignalsAndSlots(ctkWorkflowStep* newStep,
+                                                            QObject* newParent = 0);
   virtual ~ctkExampleWorkflowWidgetStepUsingSignalsAndSlots();
 
   // Set/get the widget onto which this step's user interface will be placed

+ 36 - 25
Libs/Widgets/Testing/Cpp/ctkWorkflowWidgetTest2.cpp

@@ -585,36 +585,46 @@ int ctkWorkflowWidgetTest2(int argc, char * argv [] )
 
   // create the qObjects that implement the required functions, and
   // communicate with the workflow using signals and slots
-  ctkExampleWorkflowWidgetStepUsingSignalsAndSlots* qObject1 = new ctkExampleWorkflowWidgetStepUsingSignalsAndSlots;
-  ctkExampleWorkflowWidgetStepUsingSignalsAndSlots* qObject2 = new ctkExampleWorkflowWidgetStepUsingSignalsAndSlots;
+  ctkExampleWorkflowWidgetStepUsingSignalsAndSlots* qObject1 =
+      new ctkExampleWorkflowWidgetStepUsingSignalsAndSlots(step1);
+  ctkExampleWorkflowWidgetStepUsingSignalsAndSlots* qObject2 =
+      new ctkExampleWorkflowWidgetStepUsingSignalsAndSlots(step2);
 
   // set the widget for each qObject
   qObject1->setWidget(step1->stepArea());
   qObject2->setWidget(step2->stepArea());
 
   // use the qObjects for validation
-  QObject::connect(step1->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)), qObject1, SLOT(validate(const QString&)));
-  QObject::connect(qObject1, SIGNAL(validationComplete(bool, const QString&)), workflow, SLOT(evaluateValidationResults(bool, const QString&)));
-  QObject::connect(step2->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)), qObject2, SLOT(validate(const QString&)));
-  QObject::connect(qObject2, SIGNAL(validationComplete(bool, const QString&)), workflow, SLOT(evaluateValidationResults(bool, const QString&)));
+  QObject::connect(step1->ctkWorkflowStepQObject(),
+                   SIGNAL(invokeValidateCommand(const QString&)),
+                   qObject1, SLOT(validate(const QString&)));
+  QObject::connect(step2->ctkWorkflowStepQObject(),
+                   SIGNAL(invokeValidateCommand(const QString&)),
+                   qObject2, SLOT(validate(const QString&)));
 
   // use the qObjects for entry processing
-  QObject::connect(step1->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject1, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject1, SIGNAL(onEntryComplete()), workflow, SLOT(processingAfterOnEntry()));
-  QObject::connect(step2->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject2, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject2, SIGNAL(onEntryComplete()), workflow, SLOT(processingAfterOnEntry()));
+  QObject::connect(step1->ctkWorkflowStepQObject(),
+                   SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)),
+                   qObject1, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
+  QObject::connect(step2->ctkWorkflowStepQObject(),
+                   SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)),
+                   qObject2, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
 
   // use the qObjects for exit processing
-  QObject::connect(step1->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject1, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject1, SIGNAL(onExitComplete()), workflow, SLOT(processingAfterOnExit()));
-  QObject::connect(step2->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject2, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject2, SIGNAL(onExitComplete()), workflow, SLOT(processingAfterOnExit()));
+  QObject::connect(step1->ctkWorkflowStepQObject(),
+                   SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)),
+                   qObject1, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
+  QObject::connect(step2->ctkWorkflowStepQObject(),
+                   SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)),
+                   qObject2, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
 
   // use the qObjects for populating the stepWidgetsList
-  QObject::connect(step1->ctkWorkflowAbstractWidgetStepQObject(), SIGNAL(invokeCreateUserInterfaceCommand()), qObject1, SLOT(createUserInterface()));
-  QObject::connect(qObject1, SIGNAL(createUserInterfaceComplete()), step1->ctkWorkflowAbstractWidgetStepQObject(), SIGNAL(showUserInterfaceComplete()));
-  QObject::connect(step2->ctkWorkflowAbstractWidgetStepQObject(), SIGNAL(invokeCreateUserInterfaceCommand()), qObject2, SLOT(createUserInterface()));
-  QObject::connect(qObject2, SIGNAL(createUserInterfaceComplete()), step2->ctkWorkflowAbstractWidgetStepQObject(), SIGNAL(showUserInterfaceComplete()));
+  QObject::connect(step1->ctkWorkflowAbstractWidgetStepQObject(),
+                   SIGNAL(invokeCreateUserInterfaceCommand()),
+                   qObject1, SLOT(createUserInterface()));
+  QObject::connect(step2->ctkWorkflowAbstractWidgetStepQObject(),
+                   SIGNAL(invokeCreateUserInterfaceCommand()),
+                   qObject2, SLOT(createUserInterface()));
 
   step1->setHasValidateCommand(1);
   step2->setHasValidateCommand(1);
@@ -665,21 +675,22 @@ int ctkWorkflowWidgetTest2(int argc, char * argv [] )
 
   // create the qObjects that implement the required functions, and
   // communicate with the workflow using signals and slots
-  ctkExampleWorkflowWidgetStepUsingSignalsAndSlots* qObject3 = new ctkExampleWorkflowWidgetStepUsingSignalsAndSlots;
+  ctkExampleWorkflowWidgetStepUsingSignalsAndSlots* qObject3 =
+      new ctkExampleWorkflowWidgetStepUsingSignalsAndSlots(step3);
 
   qObject3->setWidget(step3->stepArea());
 
   // use the qObjects for validation
-  QObject::connect(step3->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)), qObject3, SLOT(validate(const QString&)));
-  QObject::connect(qObject3, SIGNAL(validationComplete(bool, const QString&)), workflow, SLOT(evaluateValidationResults(bool, const QString&)));
+  QObject::connect(step3->ctkWorkflowStepQObject(), SIGNAL(invokeValidateCommand(const QString&)),
+                   qObject3, SLOT(validate(const QString&)));
 
   // use the qObjects for entry processing
-  QObject::connect(step3->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject3, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject3, SIGNAL(onEntryComplete()), workflow, SLOT(processingAfterOnEntry()));
+  QObject::connect(step3->ctkWorkflowStepQObject(), SIGNAL(invokeOnEntryCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)),
+                   qObject3, SLOT(onEntry(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
 
   // use the qObjects for exit processing
-  QObject::connect(step3->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)), qObject3, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
-  QObject::connect(qObject3, SIGNAL(onExitComplete()), workflow, SLOT(processingAfterOnExit()));
+  QObject::connect(step3->ctkWorkflowStepQObject(), SIGNAL(invokeOnExitCommand(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)),
+                   qObject3, SLOT(onExit(const ctkWorkflowStep*, const ctkWorkflowInterstepTransition::InterstepTransitionType)));
 
   // use the qObjects for populating the stepWidgetsList
   QObject::connect(step3->ctkWorkflowAbstractWidgetStepQObject(), SIGNAL(invokeCreateUserInterfaceCommand()), qObject3, SLOT(createUserInterface()));