ctkWorkflow.cpp 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.commontk.org/LICENSE
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // Qt includes
  15. #include <QStateMachine>
  16. #include <QState>
  17. // CTK includes
  18. #include "ctkWorkflow.h"
  19. #include "ctkWorkflowStep.h"
  20. #include "ctkWorkflowStep_p.h"
  21. #include "ctkWorkflowTransitions.h"
  22. #include "ctkLogger.h"
  23. // STD includes
  24. #include <iostream>
  25. //--------------------------------------------------------------------------
  26. static ctkLogger logger("org.commontk.libs.core.ctkWorkflow");
  27. //--------------------------------------------------------------------------
  28. namespace
  29. {
  30. //-----------------------------------------------------------------------------
  31. struct forwardAndBackwardSteps
  32. {
  33. QList<ctkWorkflowStep*> forwardSteps()
  34. {
  35. return this->ForwardSteps;
  36. }
  37. QList<ctkWorkflowStep*> backwardSteps()
  38. {
  39. return this->BackwardSteps;
  40. }
  41. QList<QString> forwardBranchIds()
  42. {
  43. return this->ForwardBranchIds;
  44. }
  45. QList<QString> backwardBranchIds()
  46. {
  47. return this->BackwardBranchIds;
  48. }
  49. void appendForwardStep(ctkWorkflowStep* step, QString id)
  50. {
  51. this->ForwardSteps.append(step);
  52. this->ForwardBranchIds.append(id);
  53. }
  54. void appendBackwardStep(ctkWorkflowStep* step, QString id)
  55. {
  56. this->BackwardSteps.append(step);
  57. this->BackwardBranchIds.append(id);
  58. }
  59. QString firstForwardBranchId()
  60. {
  61. if (this->ForwardBranchIds.isEmpty())
  62. {
  63. return QString();
  64. }
  65. else
  66. {
  67. return this->ForwardBranchIds.first();
  68. }
  69. }
  70. ctkWorkflowStep* forwardStep(QString branchId)
  71. {
  72. int index = this->ForwardBranchIds.indexOf(branchId);
  73. if (index != -1)
  74. {
  75. return ForwardSteps.at(index);
  76. }
  77. else
  78. {
  79. return 0;
  80. }
  81. }
  82. QString backwardBranchId(ctkWorkflowStep* step)
  83. {
  84. int index = this->BackwardSteps.indexOf(step);
  85. if (index != -1)
  86. {
  87. return BackwardBranchIds.at(index);
  88. }
  89. else
  90. {
  91. return QString();
  92. }
  93. }
  94. QString forwardBranchId(ctkWorkflowStep* step)
  95. {
  96. int index = this->ForwardSteps.indexOf(step);
  97. if (index != -1)
  98. {
  99. return ForwardBranchIds.at(index);
  100. }
  101. else
  102. {
  103. return QString();
  104. }
  105. }
  106. private:
  107. QList<ctkWorkflowStep*> ForwardSteps;
  108. QList<ctkWorkflowStep*> BackwardSteps;
  109. QList<QString> ForwardBranchIds;
  110. QList<QString> BackwardBranchIds;
  111. };
  112. }
  113. //-----------------------------------------------------------------------------
  114. class ctkWorkflowPrivate
  115. {
  116. Q_DECLARE_PUBLIC(ctkWorkflow);
  117. protected:
  118. ctkWorkflow* const q_ptr;
  119. public:
  120. ctkWorkflowPrivate(ctkWorkflow& object);
  121. ~ctkWorkflowPrivate();
  122. /// \brief Add a step to the workflow
  123. ///
  124. /// \note The step's components will be automatically be added to the state machine
  125. /// (i.e. the processingState state, validationState state, validationTransition transition
  126. /// and validationFailedtransition transition.
  127. ///
  128. /// \return True or False indicating whether the method was successful.
  129. void addStep(ctkWorkflowStep* step);
  130. /// \brief Returns whether a transition has been previously added with the same origin,
  131. /// destination and directionality
  132. bool hasDuplicateTransition(ctkWorkflowStep* origin, ctkWorkflowStep* destination,
  133. const ctkWorkflow::TransitionDirectionality directionality);
  134. /// \brief Returns whether a transition has been previously added with the same origin and branch
  135. /// id (for forward transitions) or with the same destination and branch id (for backward transitions
  136. bool hasTransitionWithSameBranchId(ctkWorkflowStep* origin, ctkWorkflowStep* destination,
  137. const QString& branchId,
  138. const ctkWorkflow::TransitionDirectionality directionality);
  139. /// \brief Creates a transition from the origin to the destinatio.
  140. ///
  141. /// More specifically, the transition is from the \a origin's validation state to the \a
  142. /// destination's processing state, and is of type ctkWorkflowTransition::TransitionToNextStep
  143. ///
  144. /// The destination step should semantically be a next step, i.e. from a workflow perspective, the
  145. /// \a destination step is meant to appear after the \a origin step.
  146. ///
  147. /// Returns true/false indicating whether the method was successful.
  148. void createTransitionToNextStep(ctkWorkflowStep* origin,
  149. ctkWorkflowStep* destination,
  150. const QString& branchId = "");
  151. /// \brief Creates a transition from the destination to the origin
  152. ///
  153. /// More specifically, the transition is from the \a destination's processing state to the \a
  154. /// origin's processing state, and is of type ctkWorkflowTransition::TransitionToPreviousStep
  155. ///
  156. /// The destination step should semantically be a next step, i.e. from a workflow perspective, the
  157. /// \a destination step is meant to appear after the \a origin step.
  158. ///
  159. /// Returns true/false indicating whether the method was successful.
  160. void createTransitionToPreviousStep(ctkWorkflowStep* origin,
  161. ctkWorkflowStep* destination,
  162. const QString& branchId = "");
  163. /// \brief Creates a transition from the goTo step to the step from which the attempt to go to the
  164. /// goTo step was initiated.
  165. ///
  166. /// More specifically, the transition is from the \a goTo step's processing state to the \a
  167. /// starting step's processing state, and is of type ctkWorkflowTransition::TransitionToPreviousStartingStep
  168. ///
  169. /// Returns true/false indicating whether the method was successful.
  170. void createTransitionToPreviousStartingStep(ctkWorkflowStep* startingStep,
  171. ctkWorkflowStep* currentStep);
  172. ///
  173. void validateInternal(ctkWorkflowStep* step);
  174. /// \brief Performs computation required when entering this step.
  175. ///
  176. /// Does some sanity checks and then either calls onEntry() or emits the invokeOnEntryCommand(),
  177. /// depending on whether the user indicates that there is an onEntryCommand.
  178. void onEntryInternal(ctkWorkflowStep* step, ctkWorkflowStep* comingFrom,
  179. const ctkWorkflowInterstepTransition::InterstepTransitionType& transitionType);
  180. /// \brief Performs computation required when exiting this step.
  181. ///
  182. /// Does some sanity checks and then either calls onExit() or emits the invokeOnExitCommand(),
  183. /// depending on whether the user indicates that there is an onExitCommand.
  184. void onExitInternal(ctkWorkflowStep* step, ctkWorkflowStep* goingTo,
  185. const ctkWorkflowInterstepTransition::InterstepTransitionType& transitionType);
  186. /// Get the step in the workflow with a given id.
  187. ctkWorkflowStep* stepFromId(const QString& id)const;
  188. /// Get the step that a state belongs to (if any)
  189. ctkWorkflowStep* stepFromState(const QAbstractState* state);
  190. /// Get the number of forward steps from the given step
  191. int numberOfForwardSteps(ctkWorkflowStep* step);
  192. /// Get the number of backward steps from the given step
  193. int numberOfBackwardSteps(ctkWorkflowStep* step);
  194. /// Get the ids of the steps that directly follow the given step.
  195. QList<QString> forwardBranchIds(ctkWorkflowStep* step)const;
  196. /// Get the ids of the steps that directly preceed the given step.
  197. QList<QString> backwardBranchIds(ctkWorkflowStep* step)const;
  198. /// Determines whether there exists a path from the origin step (the current step by default) to
  199. /// the step with the given goalId
  200. bool pathExists(const QString& goalId, ctkWorkflowStep* origin = 0)const;
  201. /// Determines whether there exists a path from the current step's next step (as given by the
  202. /// branchId) to the step with the given goalId
  203. bool pathExistsFromNextStep(const QString& goalId, const QString& branchId)const;
  204. QStateMachine* StateMachine;
  205. // Maintain a list of pointers to the steps in the workflow,
  206. // along with their forward and backward transitions
  207. QMap<ctkWorkflowStep*, forwardAndBackwardSteps*> StepToForwardAndBackwardStepMap;
  208. // ... and its associated convenient typedef
  209. typedef QMap<ctkWorkflowStep*, forwardAndBackwardSteps*> StepToForwardAndBackwardStepMapType;
  210. typedef QList<ctkWorkflowStep*> StepListType;
  211. // Maintain a map of <state, step> key/value pairs, to find the step
  212. // that a given state belongs to
  213. typedef QMap<const QAbstractState*, ctkWorkflowStep*> StateToStepMapType;
  214. typedef QMap<const QAbstractState*, ctkWorkflowStep*>::iterator StateToStepMapIterator;
  215. StateToStepMapType StateToStepMap;
  216. ctkWorkflowStep* InitialStep;
  217. ctkWorkflowStep* CurrentStep;
  218. QMap<ctkWorkflowStep*, ctkWorkflowStep*> StepToPreviousStepMap;
  219. // Used when performing a transition
  220. ctkWorkflowStep* OriginStep;
  221. ctkWorkflowStep* DestinationStep;
  222. ctkWorkflowInterstepTransition::InterstepTransitionType TransitionType;
  223. QString DesiredBranchId; // Desired branchId specified when invoking goForward
  224. ctkWorkflowStep* GoToStep; // Desired step when attempting to go to a finish step
  225. ctkWorkflowStep* StartingStep; // Current step when we began the attempt to go to the desired finish step
  226. // Temporary transition after successfully going to finish step, to get us back to the starting step
  227. ctkWorkflowInterstepTransition* TransitionToPreviousStartingStep;
  228. QString ARTIFICIAL_BRANCH_ID_PREFIX;
  229. };
  230. // --------------------------------------------------------------------------
  231. // ctkWorkflowPrivate methods
  232. // --------------------------------------------------------------------------
  233. ctkWorkflowPrivate::ctkWorkflowPrivate(ctkWorkflow& object)
  234. :q_ptr(&object)
  235. {
  236. this->InitialStep = 0;
  237. this->CurrentStep = 0;
  238. this->OriginStep = 0;
  239. this->DestinationStep = 0;
  240. this->GoToStep = 0;
  241. this->StartingStep = 0;
  242. this->TransitionToPreviousStartingStep = 0;
  243. this->ARTIFICIAL_BRANCH_ID_PREFIX = "ctkWorkflowArtificialBranchId_";
  244. }
  245. // --------------------------------------------------------------------------
  246. ctkWorkflowPrivate::~ctkWorkflowPrivate()
  247. {
  248. }
  249. // --------------------------------------------------------------------------
  250. void ctkWorkflowPrivate::addStep(ctkWorkflowStep* step)
  251. {
  252. Q_Q(ctkWorkflow);
  253. Q_ASSERT(step);
  254. Q_ASSERT(!q->hasStep(step->id()));
  255. Q_ASSERT(!this->StateMachine->isRunning());
  256. // Add the states, creating them if necessary
  257. this->StateMachine->addState(step->processingState());
  258. this->StateMachine->addState(step->validationState());
  259. // Update the map of steps to transitions and the <state,step> map
  260. this->StepToForwardAndBackwardStepMap.insert(step, new forwardAndBackwardSteps);
  261. this->StateToStepMap[step->processingState()] = step;
  262. this->StateToStepMap[step->validationState()] = step;
  263. // Setup the signal/slot that triggers the attempt to go to the next step
  264. QObject::connect(step->validationState(), SIGNAL(entered()),
  265. q, SLOT(attemptToGoToNextStep()));
  266. // Setup the signal/slot that triggers the evaluation of the validation results
  267. // after validate(const QString&) is called
  268. q->connectStep(step);
  269. }
  270. // --------------------------------------------------------------------------
  271. bool ctkWorkflowPrivate::hasDuplicateTransition(ctkWorkflowStep* origin, ctkWorkflowStep* destination,
  272. const ctkWorkflow::TransitionDirectionality directionality)
  273. {
  274. Q_Q(ctkWorkflow);
  275. Q_ASSERT(origin);
  276. Q_ASSERT(destination);
  277. Q_ASSERT(directionality == ctkWorkflow::Forward || ctkWorkflow::Backward);
  278. ctkWorkflowPrivate::StepListType stepList;
  279. ctkWorkflowStep* targetStep = 0;
  280. if (directionality == ctkWorkflow::Forward)
  281. {
  282. stepList = q->forwardSteps(origin);
  283. targetStep = destination;
  284. }
  285. else if (directionality == ctkWorkflow::Backward)
  286. {
  287. stepList = q->backwardSteps(destination);
  288. targetStep = origin;
  289. }
  290. foreach(ctkWorkflowStep * step, stepList)
  291. {
  292. if (step == targetStep)
  293. {
  294. return true;
  295. }
  296. }
  297. return false;
  298. }
  299. // --------------------------------------------------------------------------
  300. bool ctkWorkflowPrivate::hasTransitionWithSameBranchId(ctkWorkflowStep* origin, ctkWorkflowStep* destination,
  301. const QString& branchId,
  302. const ctkWorkflow::TransitionDirectionality directionality)
  303. {
  304. Q_ASSERT(origin);
  305. Q_ASSERT(destination);
  306. Q_ASSERT(directionality == ctkWorkflow::Forward || ctkWorkflow::Backward);
  307. Q_ASSERT(!branchId.isEmpty());
  308. QList<QString> branchIdList;
  309. if (directionality == ctkWorkflow::Forward)
  310. {
  311. branchIdList = this->forwardBranchIds(origin);
  312. }
  313. else if (directionality == ctkWorkflow::Backward)
  314. {
  315. branchIdList = this->backwardBranchIds(destination);
  316. }
  317. foreach(QString id, branchIdList)
  318. {
  319. if (QString::compare(id, branchId, Qt::CaseInsensitive) == 0)
  320. {
  321. return true;
  322. }
  323. }
  324. return false;
  325. }
  326. // --------------------------------------------------------------------------
  327. void ctkWorkflowPrivate::createTransitionToNextStep(ctkWorkflowStep* origin,
  328. ctkWorkflowStep* destination,
  329. const QString& branchId)
  330. {
  331. Q_Q(ctkWorkflow);
  332. Q_ASSERT(origin);
  333. Q_ASSERT(destination);
  334. Q_ASSERT(!q->hasTransition(origin, destination, branchId, ctkWorkflow::Forward));
  335. QString id;
  336. // create an artificial branchId if one is not given
  337. if (branchId.isEmpty())
  338. {
  339. id.setNum(this->numberOfForwardSteps(origin));
  340. id.prepend(this->ARTIFICIAL_BRANCH_ID_PREFIX);
  341. }
  342. else
  343. {
  344. id = branchId;
  345. }
  346. // Create the transition
  347. ctkWorkflowInterstepTransition* transition = new ctkWorkflowInterstepTransition(ctkWorkflowInterstepTransition::TransitionToNextStep, id);
  348. transition->setTargetState(destination->processingState());
  349. origin->validationState()->addTransition(transition);
  350. // Update the step to transitions map
  351. this->StepToForwardAndBackwardStepMap.value(origin)->appendForwardStep(destination, id);
  352. // Setup the signal/slot that shows and hides the steps' user interfaces
  353. // on transition to the next step
  354. QObject::connect(transition, SIGNAL(triggered()), q, SLOT(performTransitionBetweenSteps()));
  355. }
  356. // --------------------------------------------------------------------------
  357. void ctkWorkflowPrivate::createTransitionToPreviousStep(ctkWorkflowStep* origin,
  358. ctkWorkflowStep* destination,
  359. const QString& branchId)
  360. {
  361. Q_Q(ctkWorkflow);
  362. Q_ASSERT(origin);
  363. Q_ASSERT(destination);
  364. Q_ASSERT(!q->hasTransition(origin, destination, branchId, ctkWorkflow::Backward));
  365. QString id;
  366. // create an artificial branchId if one is not given
  367. if (branchId.isEmpty())
  368. {
  369. id.setNum(this->numberOfBackwardSteps(destination));
  370. id.prepend(this->ARTIFICIAL_BRANCH_ID_PREFIX);
  371. }
  372. else
  373. {
  374. id = branchId;
  375. }
  376. ctkWorkflowInterstepTransition* transition = new ctkWorkflowInterstepTransition(ctkWorkflowInterstepTransition::TransitionToPreviousStep, id);
  377. transition->setTargetState(origin->processingState());
  378. destination->processingState()->addTransition(transition);
  379. // Update the step to transitions map
  380. this->StepToForwardAndBackwardStepMap.value(destination)->appendBackwardStep(origin, id);
  381. // Setup the signal/slot that shows and hides the steps' user
  382. // interfaces on transition to the previous step
  383. QObject::connect(transition, SIGNAL(triggered()), q, SLOT(performTransitionBetweenSteps()));
  384. }
  385. // --------------------------------------------------------------------------
  386. void ctkWorkflowPrivate::createTransitionToPreviousStartingStep(ctkWorkflowStep* startingStep,
  387. ctkWorkflowStep* currentStep)
  388. {
  389. Q_Q(ctkWorkflow);
  390. Q_ASSERT(startingStep);
  391. Q_ASSERT(currentStep);
  392. if (!this->TransitionToPreviousStartingStep)
  393. {
  394. ctkWorkflowInterstepTransition* transition = new ctkWorkflowInterstepTransition(
  395. ctkWorkflowInterstepTransition::TransitionToPreviousStartingStepAfterSuccessfulGoToFinishStep);
  396. // Setup the signal/slot that shows and hides the steps' user interfaces
  397. // on transition to the previous step
  398. QObject::connect(transition, SIGNAL(triggered()), q, SLOT(performTransitionBetweenSteps()));
  399. this->TransitionToPreviousStartingStep = transition;
  400. }
  401. QState* currentState;
  402. // looping on the finish step
  403. if (startingStep == currentStep)
  404. {
  405. currentState = currentStep->validationState();
  406. }
  407. else
  408. {
  409. currentState = currentStep->processingState();
  410. }
  411. this->TransitionToPreviousStartingStep->setTargetState(startingStep->processingState());
  412. currentState->addTransition(this->TransitionToPreviousStartingStep);
  413. }
  414. // --------------------------------------------------------------------------
  415. ctkWorkflowStep* ctkWorkflowPrivate::stepFromId(const QString& id)const
  416. {
  417. foreach(ctkWorkflowStep* step, this->StepToForwardAndBackwardStepMap.keys())
  418. {
  419. Q_ASSERT(step);
  420. if (QString::compare(step->id(), id, Qt::CaseInsensitive) == 0)
  421. {
  422. return step;
  423. }
  424. }
  425. return 0;
  426. }
  427. // --------------------------------------------------------------------------
  428. QList<QString> ctkWorkflowPrivate::forwardBranchIds(ctkWorkflowStep* step)const
  429. {
  430. Q_ASSERT(step);
  431. return this->StepToForwardAndBackwardStepMap.value(step)->forwardBranchIds();
  432. }
  433. // --------------------------------------------------------------------------
  434. QList<QString> ctkWorkflowPrivate::backwardBranchIds(ctkWorkflowStep* step)const
  435. {
  436. Q_ASSERT(step);
  437. return this->StepToForwardAndBackwardStepMap.value(step)->backwardBranchIds();
  438. }
  439. //---------------------------------------------------------------------------
  440. void ctkWorkflowPrivate::validateInternal(ctkWorkflowStep* step)
  441. {
  442. Q_ASSERT(step);
  443. logger.debug(QString("validateInternal - validating input from %1").arg(step->name()));
  444. if (step->hasValidateCommand())
  445. {
  446. step->invokeValidateCommand(this->DesiredBranchId);
  447. }
  448. else
  449. {
  450. step->validate(this->DesiredBranchId);
  451. }
  452. }
  453. // --------------------------------------------------------------------------
  454. void ctkWorkflowPrivate::onEntryInternal(
  455. ctkWorkflowStep* step,
  456. ctkWorkflowStep* comingFrom,
  457. const ctkWorkflowInterstepTransition::InterstepTransitionType& transitionType)
  458. {
  459. Q_ASSERT(step);
  460. logger.debug(QString("onEntryInternal - entering input from %1").arg(step->name()));
  461. //Ensure we are transitioning between steps or starting the workflow
  462. Q_ASSERT(transitionType == ctkWorkflowInterstepTransition::TransitionToNextStep
  463. || transitionType == ctkWorkflowInterstepTransition::TransitionToPreviousStep
  464. || transitionType == ctkWorkflowInterstepTransition::StartingWorkflow
  465. || transitionType == ctkWorkflowInterstepTransition::TransitionToPreviousStartingStepAfterSuccessfulGoToFinishStep);
  466. if (step->hasOnEntryCommand())
  467. {
  468. step->invokeOnEntryCommand(comingFrom, transitionType);
  469. }
  470. else
  471. {
  472. step->onEntry(comingFrom, transitionType);
  473. }
  474. }
  475. // --------------------------------------------------------------------------
  476. void ctkWorkflowPrivate::onExitInternal(
  477. ctkWorkflowStep* step,
  478. ctkWorkflowStep* goingTo,
  479. const ctkWorkflowInterstepTransition::InterstepTransitionType& transitionType)
  480. {
  481. Q_ASSERT(step);
  482. logger.debug(QString("onExitInternal - exiting %1").arg(step->name()));
  483. // Ensure we are transitioning between steps or starting the workflow
  484. Q_ASSERT (transitionType == ctkWorkflowInterstepTransition::TransitionToNextStep ||
  485. transitionType == ctkWorkflowInterstepTransition::TransitionToPreviousStep ||
  486. transitionType == ctkWorkflowInterstepTransition::StoppingWorkflow ||
  487. transitionType == ctkWorkflowInterstepTransition::TransitionToPreviousStartingStepAfterSuccessfulGoToFinishStep);
  488. if (step->hasOnExitCommand())
  489. {
  490. step->invokeOnExitCommand(goingTo, transitionType);
  491. }
  492. else
  493. {
  494. step->onExit(goingTo, transitionType);
  495. }
  496. }
  497. // --------------------------------------------------------------------------
  498. ctkWorkflowStep* ctkWorkflowPrivate::stepFromState(const QAbstractState* state)
  499. {
  500. if (state)
  501. {
  502. return this->StateToStepMap.value(state);
  503. }
  504. return 0;
  505. }
  506. // --------------------------------------------------------------------------
  507. int ctkWorkflowPrivate::numberOfForwardSteps(ctkWorkflowStep* step)
  508. {
  509. Q_Q(ctkWorkflow);
  510. return q->forwardSteps(step).length();
  511. }
  512. // --------------------------------------------------------------------------
  513. int ctkWorkflowPrivate::numberOfBackwardSteps(ctkWorkflowStep* step)
  514. {
  515. Q_Q(ctkWorkflow);
  516. return q->backwardSteps(step).length();
  517. }
  518. // --------------------------------------------------------------------------
  519. bool ctkWorkflowPrivate::pathExists(const QString& goalId, ctkWorkflowStep* origin)const
  520. {
  521. Q_Q(const ctkWorkflow);
  522. Q_ASSERT(!goalId.isEmpty());
  523. Q_ASSERT(this->CurrentStep);
  524. QString originId;
  525. if (origin)
  526. {
  527. originId = origin->id();
  528. }
  529. else
  530. {
  531. originId = this->CurrentStep->id();
  532. }
  533. // there exists a path from the origin to the goal if:
  534. // - there is a goal AND
  535. // - either:
  536. // - the origin is already the goal
  537. // - there is a path from at least one of the origin's successors to the goal
  538. return (q->hasStep(goalId)
  539. && ((QString::compare(goalId, originId, Qt::CaseInsensitive) == 0)
  540. || (q->canGoForward(origin)))); // <-- TODO insert logic here for looking at graph
  541. }
  542. // --------------------------------------------------------------------------
  543. bool ctkWorkflowPrivate::pathExistsFromNextStep(const QString& goalId, const QString& branchId)const
  544. {
  545. Q_ASSERT(!goalId.isEmpty());
  546. Q_ASSERT(!branchId.isEmpty());
  547. Q_ASSERT(this->CurrentStep);
  548. // return whether there exists a path from the the step that will be followed (given the branchId) to the goal
  549. ctkWorkflowStep* nextStep = this->StepToForwardAndBackwardStepMap.value(this->CurrentStep)->forwardStep(branchId);
  550. if (!nextStep)
  551. {
  552. return false;
  553. }
  554. else
  555. {
  556. return this->pathExists(goalId, nextStep);
  557. }
  558. }
  559. // --------------------------------------------------------------------------
  560. // ctkWorkflow methods
  561. // --------------------------------------------------------------------------
  562. ctkWorkflow::ctkWorkflow(QObject* _parent) : Superclass(_parent)
  563. , d_ptr(new ctkWorkflowPrivate(*this))
  564. {
  565. Q_D(ctkWorkflow);
  566. d->StateMachine = new QStateMachine(this);
  567. }
  568. // --------------------------------------------------------------------------
  569. ctkWorkflow::~ctkWorkflow()
  570. {
  571. Q_D(ctkWorkflow);
  572. if (d->StateMachine->isRunning())
  573. {
  574. d->StateMachine->stop();
  575. }
  576. }
  577. // --------------------------------------------------------------------------
  578. bool ctkWorkflow::addTransition(ctkWorkflowStep* origin, ctkWorkflowStep* destination,
  579. const QString& branchId,
  580. const ctkWorkflow::TransitionDirectionality directionality)
  581. {
  582. Q_D(ctkWorkflow);
  583. if (d->StateMachine->isRunning())
  584. {
  585. logger.warn("addTransition - Cannot add a transition while the workflow is started !");
  586. return false;
  587. }
  588. // cannot currently create a transition between two steps of the same id, which is equivalent to
  589. // adding a transition from a step to itself
  590. if (origin && destination && (QString::compare(origin->id(), destination->id(), Qt::CaseInsensitive) == 0))
  591. {
  592. logger.error("addTransition - Workflow does not currently support a transition"
  593. " from a step to itself. Use GoToStep instead !");
  594. return false;
  595. }
  596. // add the origin step if it doesn't exist in the workflow yet
  597. if (origin && !this->hasStep(origin->id()))
  598. {
  599. d->addStep(origin);
  600. }
  601. // add the destination step if it doesn't exist in the workflow yet
  602. if (destination && !this->hasStep(destination->id()))
  603. {
  604. d->addStep(destination);
  605. }
  606. if (origin && destination)
  607. {
  608. // ensure we haven't already added a transition with the same origin, destination and directionality
  609. if (this->hasTransition(origin, destination, branchId, directionality))
  610. {
  611. logger.warn("addTransition - Cannot create a transition that matches a "
  612. "previously created transtiion");
  613. return false;
  614. }
  615. // create the forward transition
  616. if (directionality == ctkWorkflow::Forward
  617. || directionality == ctkWorkflow::Bidirectional)
  618. {
  619. d->createTransitionToNextStep(origin, destination, branchId);
  620. }
  621. // create the backward transition
  622. if (directionality == ctkWorkflow::Backward
  623. || directionality == ctkWorkflow::Bidirectional)
  624. {
  625. d->createTransitionToPreviousStep(origin, destination, branchId);
  626. }
  627. }
  628. return true;
  629. }
  630. // --------------------------------------------------------------------------
  631. bool ctkWorkflow::hasTransition(ctkWorkflowStep* origin, ctkWorkflowStep* destination,
  632. const QString& branchId,
  633. const ctkWorkflow::TransitionDirectionality directionality)
  634. {
  635. Q_D(ctkWorkflow);
  636. // we have a bidirectional transition if we have both a forward and a backward transition
  637. if (directionality == ctkWorkflow::Bidirectional)
  638. {
  639. return this->hasTransition(origin, destination, branchId, ctkWorkflow::Forward)
  640. && this->hasTransition(origin, destination, branchId, ctkWorkflow::Backward);
  641. }
  642. else
  643. {
  644. if (branchId.isEmpty())
  645. {
  646. return d->hasDuplicateTransition(origin, destination, directionality);
  647. }
  648. else
  649. {
  650. return d->hasDuplicateTransition(origin, destination, directionality)
  651. || d->hasTransitionWithSameBranchId(origin, destination, branchId, directionality);
  652. }
  653. }
  654. }
  655. // --------------------------------------------------------------------------
  656. void ctkWorkflow::connectStep(ctkWorkflowStep* step)
  657. {
  658. Q_ASSERT(step);
  659. if (!step->hasValidateCommand())
  660. {
  661. QObject::connect(step->ctkWorkflowStepQObject(), SIGNAL(validationComplete(bool, const QString&)), this, SLOT(evaluateValidationResults(bool, const QString&)));
  662. }
  663. if (!step->hasOnEntryCommand())
  664. {
  665. QObject::connect(step->ctkWorkflowStepQObject(), SIGNAL(onEntryComplete()), this, SLOT(processingAfterOnEntry()));
  666. }
  667. if (!step->hasOnExitCommand())
  668. {
  669. QObject::connect(step->ctkWorkflowStepQObject(), SIGNAL(onExitComplete()), this, SLOT(processingAfterOnExit()));
  670. }
  671. }
  672. // --------------------------------------------------------------------------
  673. QList<ctkWorkflowStep*> ctkWorkflow::forwardSteps(ctkWorkflowStep* step)const
  674. {
  675. Q_D(const ctkWorkflow);
  676. // use the given step if provided, otherwise use the workflow's current step
  677. if (step)
  678. {
  679. return d->StepToForwardAndBackwardStepMap.value(step)->forwardSteps();
  680. }
  681. else if (d->CurrentStep)
  682. {
  683. return d->StepToForwardAndBackwardStepMap.value(d->CurrentStep)->forwardSteps();
  684. }
  685. else
  686. {
  687. return QList<ctkWorkflowStep*>();
  688. }
  689. }
  690. // --------------------------------------------------------------------------
  691. QList<ctkWorkflowStep*> ctkWorkflow::backwardSteps(ctkWorkflowStep* step)const
  692. {
  693. Q_D(const ctkWorkflow);
  694. // use the current step if provided, otherwise use the workflow's current step
  695. if (step)
  696. {
  697. return d->StepToForwardAndBackwardStepMap.value(step)->backwardSteps();
  698. }
  699. else if (d->CurrentStep)
  700. {
  701. return d->StepToForwardAndBackwardStepMap.value(d->CurrentStep)->backwardSteps();
  702. }
  703. else
  704. {
  705. return QList<ctkWorkflowStep*>();
  706. }
  707. }
  708. // --------------------------------------------------------------------------
  709. QList<ctkWorkflowStep*> ctkWorkflow::finishSteps()const
  710. {
  711. Q_D(const ctkWorkflow);
  712. // iterate through our list of steps, and keep the steps that don't have anything following them
  713. QList<ctkWorkflowStep*> finishSteps;
  714. foreach (ctkWorkflowStep* step, d->StepToForwardAndBackwardStepMap.keys())
  715. {
  716. if (!this->canGoForward(step))
  717. {
  718. finishSteps.append(step);
  719. }
  720. }
  721. return finishSteps;
  722. }
  723. // --------------------------------------------------------------------------
  724. bool ctkWorkflow::canGoForward(ctkWorkflowStep* step)const
  725. {
  726. return (!this->forwardSteps(step).isEmpty());
  727. }
  728. // --------------------------------------------------------------------------
  729. bool ctkWorkflow::canGoBackward(ctkWorkflowStep* step)const
  730. {
  731. return (!this->backwardSteps(step).isEmpty());
  732. }
  733. // --------------------------------------------------------------------------
  734. bool ctkWorkflow::canGoToStep(const QString& targetId, ctkWorkflowStep* step)const
  735. {
  736. Q_D(const ctkWorkflow);
  737. return d->pathExists(targetId, step);
  738. }
  739. // --------------------------------------------------------------------------
  740. bool ctkWorkflow::hasStep(const QString& id)const
  741. {
  742. Q_D(const ctkWorkflow);
  743. return d->stepFromId(id);
  744. }
  745. // --------------------------------------------------------------------------
  746. CTK_GET_CPP(ctkWorkflow, ctkWorkflowStep*, initialStep, InitialStep);
  747. CTK_SET_CPP(ctkWorkflow, ctkWorkflowStep*, setInitialStep, InitialStep);
  748. CTK_GET_CPP(ctkWorkflow, ctkWorkflowStep*, currentStep, CurrentStep);
  749. // --------------------------------------------------------------------------
  750. void ctkWorkflow::start()
  751. {
  752. Q_D(ctkWorkflow);
  753. if (!d->InitialStep)
  754. {
  755. logger.warn("start - Cannot start workflow without an initial step");
  756. return;
  757. }
  758. // Setup to do the entry processing for the initial setp
  759. d->StateMachine->setInitialState(d->InitialStep->processingState());
  760. d->OriginStep = 0;
  761. d->DestinationStep = d->InitialStep;
  762. d->TransitionType = ctkWorkflowInterstepTransition::StartingWorkflow;
  763. d->onEntryInternal(d->DestinationStep, d->OriginStep, d->TransitionType);
  764. d->StateMachine->start();
  765. }
  766. // --------------------------------------------------------------------------
  767. bool ctkWorkflow::isRunning()const
  768. {
  769. Q_D(const ctkWorkflow);
  770. return d->StateMachine->isRunning();
  771. }
  772. // --------------------------------------------------------------------------
  773. void ctkWorkflow::stop()
  774. {
  775. Q_D(ctkWorkflow);
  776. if (!d->StateMachine->isRunning())
  777. {
  778. return;
  779. }
  780. // Setup to do the exit processing for the current step
  781. if (d->CurrentStep)
  782. {
  783. d->OriginStep = d->CurrentStep;
  784. d->DestinationStep = 0;
  785. d->TransitionType = ctkWorkflowInterstepTransition::StoppingWorkflow;
  786. d->onExitInternal(d->OriginStep, d->DestinationStep, d->TransitionType);
  787. }
  788. d->StateMachine->stop();
  789. }
  790. // --------------------------------------------------------------------------
  791. void ctkWorkflow::goForward(const QString& desiredBranchId)
  792. {
  793. Q_D(ctkWorkflow);
  794. if (!this->isRunning())
  795. {
  796. logger.warn("goForward - The workflow is not running !");
  797. return;
  798. }
  799. // if we're just going to the next step and not to a 'goTo' step, then check to make sure that
  800. // there exists a step following the current step
  801. if (!d->GoToStep)
  802. {
  803. if (!this->canGoForward())
  804. {
  805. logger.warn("goForward - Attempt to goForward from a finish step !");
  806. return;
  807. }
  808. }
  809. d->DesiredBranchId = desiredBranchId;
  810. logger.info("goForward - posting ValidationTransition");
  811. d->StateMachine->postEvent(
  812. new ctkWorkflowIntrastepTransitionEvent(ctkWorkflowIntrastepTransition::ValidationTransition));
  813. }
  814. // --------------------------------------------------------------------------
  815. void ctkWorkflow::goBackward(const QString& desiredBranchId)
  816. {
  817. Q_D(ctkWorkflow);
  818. if (!this->isRunning())
  819. {
  820. logger.warn("goBackward - The workflow is not running !");
  821. return;
  822. }
  823. if (!this->canGoBackward())
  824. {
  825. logger.warn("goBackward - Attempt to goBackward from first step !");
  826. return;
  827. }
  828. ctkWorkflowStep* previousStep = d->StepToPreviousStepMap[d->CurrentStep];
  829. Q_ASSERT(previousStep);
  830. QString branchId = d->StepToForwardAndBackwardStepMap.value(d->CurrentStep)->backwardBranchId(previousStep);
  831. Q_ASSERT(!branchId.isEmpty());
  832. d->DesiredBranchId = desiredBranchId;
  833. logger.info("goBackward - posting TransitionToPreviousStep");
  834. d->StateMachine->postEvent(
  835. new ctkWorkflowInterstepTransitionEvent(ctkWorkflowInterstepTransition::TransitionToPreviousStep, branchId));
  836. }
  837. // --------------------------------------------------------------------------
  838. void ctkWorkflow::goToStep(const QString& targetId)
  839. {
  840. Q_D(ctkWorkflow);
  841. if (!this->isRunning())
  842. {
  843. logger.warn("goToStep - The workflow is not running !");
  844. return;
  845. }
  846. // TODO currently returns true only if the workflow is running - need logic here
  847. if (!this->canGoToStep(targetId))
  848. {
  849. logger.warn(QString("goToStep - Cannot goToStep %1 ").arg(targetId));
  850. return;
  851. }
  852. #ifndef QT_NO_DEBUG
  853. ctkWorkflowStep* step = d->stepFromId(targetId);
  854. Q_ASSERT(step);
  855. #endif
  856. logger.info(QString("goToStep - Attempting to go to finish step %1").arg(targetId));
  857. // if (step == d->CurrentStep)
  858. // {
  859. // qDebug() << "we are already in the desired finish step";
  860. // return;
  861. // }
  862. d->GoToStep = d->stepFromId(targetId);
  863. d->StartingStep = d->CurrentStep;
  864. this->goForward();
  865. }
  866. // --------------------------------------------------------------------------
  867. void ctkWorkflow::attemptToGoToNextStep()
  868. {
  869. logger.info("attemptToGoToNextStep - Attempting to go to the next step ");
  870. Q_D(ctkWorkflow);
  871. Q_ASSERT(d->CurrentStep);
  872. //Q_ASSERT(this->canGoForward(d->CurrentStep));
  873. d->validateInternal(d->CurrentStep);
  874. }
  875. // --------------------------------------------------------------------------
  876. void ctkWorkflow::evaluateValidationResults(bool validationSucceeded, const QString& branchId)
  877. {
  878. if (validationSucceeded)
  879. {
  880. this->goToNextStepAfterSuccessfulValidation(branchId);
  881. }
  882. else
  883. {
  884. this->goToProcessingStateAfterValidationFailed();
  885. }
  886. }
  887. // --------------------------------------------------------------------------
  888. // if ctkWorkflowStep::validationComplete() did not provide a branchId, then:
  889. // - if there is one step following the current step, we will follow that transition
  890. // - if there are multiple steps following the current step, then we will follow the first
  891. // transition that was added
  892. // (either way this corresponds to following the first forwardBranchId we've recorded)
  893. // if ctkWorkflowStep::validationComplete() provided a branchId, then:
  894. // - if there is one transition following the current step that was not created using a branchId,
  895. // then we will follow it
  896. // - otherwise do a conditional branching based on the branchId provided by validationComplete()
  897. void ctkWorkflow::goToNextStepAfterSuccessfulValidation(const QString& branchId)
  898. {
  899. Q_D(ctkWorkflow);
  900. logger.debug("goToNextStepAfterSuccessfulValidation - Calidation succeeded");
  901. logger.info("goToNextStepAfterSuccessfulValidation - Posting TransitionToNextStep");
  902. // we may already be in the 'goTo' step - i.e. looping on a finish step
  903. if (d->GoToStep && d->CurrentStep == d->GoToStep)
  904. {
  905. this->goToStepSucceeded();
  906. return;
  907. }
  908. QString transitionBranchId;
  909. // these values are helpful for the logic below
  910. QString firstForwardBranchId = d->StepToForwardAndBackwardStepMap.value(d->CurrentStep)->firstForwardBranchId();
  911. int numberOfForwardSteps = d->numberOfForwardSteps(d->CurrentStep);
  912. Q_ASSERT(!firstForwardBranchId.isEmpty());
  913. Q_ASSERT(numberOfForwardSteps);
  914. // validationComplete() does not give us a branchId
  915. if (branchId.isEmpty())
  916. {
  917. transitionBranchId = firstForwardBranchId;
  918. if (numberOfForwardSteps > 1)
  919. {
  920. logger.warn("goToNextStepAfterSuccessfulValidation - ctkWorkflowStep::ValidatComplete() "
  921. "did not provide branchId at a branch in the workflow - will follow first "
  922. "transition that was created");
  923. }
  924. }
  925. // validationComplete() gives us a branchId
  926. else
  927. {
  928. if (numberOfForwardSteps == 1 && firstForwardBranchId.contains(d->ARTIFICIAL_BRANCH_ID_PREFIX))
  929. {
  930. transitionBranchId = firstForwardBranchId;
  931. logger.warn("goToNextStepAfterSuccessfulValidation - ctkWorkflowStep::ValidationComplete()"
  932. " returns a branchId, but was overridden by the workflow");
  933. }
  934. else
  935. {
  936. transitionBranchId = branchId;
  937. }
  938. }
  939. // if we are trying to go to a 'goTo' step, check that the selected branch will still take us along a path that leads to the 'goTo' step, and fail if not
  940. if (d->GoToStep && !d->pathExistsFromNextStep(d->GoToStep->id(), transitionBranchId))
  941. {
  942. this->goToProcessingStateAfterValidationFailed();
  943. return;
  944. }
  945. d->StateMachine->postEvent(new ctkWorkflowInterstepTransitionEvent(ctkWorkflowInterstepTransition::TransitionToNextStep, transitionBranchId));
  946. }
  947. // --------------------------------------------------------------------------
  948. void ctkWorkflow::goToProcessingStateAfterValidationFailed()
  949. {
  950. logger.debug("goToNextStepAfterSuccessfulValidation - Validation failed");
  951. Q_D(ctkWorkflow);
  952. // Validation failed in the process of attempting to go to the finish step
  953. if (d->GoToStep)
  954. {
  955. this->goToStepFailed();
  956. }
  957. logger.info("goToNextStepAfterSuccessfulValidation - Posting ValidationFailedTransition");
  958. d->StateMachine->postEvent(new ctkWorkflowIntrastepTransitionEvent(ctkWorkflowIntrastepTransition::ValidationFailedTransition));
  959. }
  960. // --------------------------------------------------------------------------
  961. void ctkWorkflow::performTransitionBetweenSteps()
  962. {
  963. Q_D(ctkWorkflow);
  964. logger.debug("performTransitionBetweenSteps - Performing transition between steps");
  965. // Alternative: could find the origin and destination step based on
  966. // d->CurrentStep rather than QObject::sender(), but would require
  967. // keeping track of an origin step's destination step (and would be
  968. // tricky in an extension to branching workflows, unless we change
  969. // this method signature)
  970. ctkWorkflowInterstepTransition* transition = qobject_cast<ctkWorkflowInterstepTransition*>(QObject::sender());
  971. Q_ASSERT(transition);
  972. d->OriginStep = d->stepFromState(transition->sourceState());
  973. d->DestinationStep = d->stepFromState(transition->targetState());
  974. d->TransitionType = transition->transitionType();
  975. Q_ASSERT(d->TransitionType == ctkWorkflowInterstepTransition::TransitionToNextStep
  976. || d->TransitionType == ctkWorkflowInterstepTransition::TransitionToPreviousStep
  977. || d->TransitionType == ctkWorkflowInterstepTransition::TransitionToPreviousStartingStepAfterSuccessfulGoToFinishStep);
  978. // update the map from the step to the previous step if we are going forward
  979. if (d->TransitionType == ctkWorkflowInterstepTransition::TransitionToNextStep)
  980. {
  981. d->StepToPreviousStepMap.insert(d->DestinationStep, d->OriginStep);
  982. }
  983. // exit the destination step
  984. d->onExitInternal(d->OriginStep, d->DestinationStep, d->TransitionType);
  985. }
  986. // --------------------------------------------------------------------------
  987. void ctkWorkflow::processingAfterOnExit()
  988. {
  989. Q_D(ctkWorkflow);
  990. // enter the destination step if we have one
  991. if (d->DestinationStep)
  992. {
  993. d->onEntryInternal(d->DestinationStep, d->OriginStep, d->TransitionType);
  994. }
  995. // reset the pointers used internally for performing a transition if we're done
  996. else
  997. {
  998. d->OriginStep = 0;
  999. d->DestinationStep = 0;
  1000. // we've exited the CurrentStep and haven't gone into another step, so we no longer have a
  1001. // currentStep.
  1002. d->CurrentStep = 0;
  1003. }
  1004. }
  1005. // --------------------------------------------------------------------------
  1006. void ctkWorkflow::processingAfterOnEntry()
  1007. {
  1008. logger.debug("processingAfterOnEntry");
  1009. Q_D(ctkWorkflow);
  1010. if (!d->DestinationStep)
  1011. {
  1012. logger.error("processingAfterOnEntry - Called processingAfterOnEntry without "
  1013. "having set a destination step");
  1014. return;
  1015. }
  1016. // Update the currentStep and previous step
  1017. d->CurrentStep = d->DestinationStep;
  1018. // Reset the pointers used internally for performing a transition
  1019. d->OriginStep = 0;
  1020. d->DestinationStep = 0;
  1021. // // Reset the pointers used internally for performing a transition
  1022. // // back to the starting step
  1023. // if (d->TransitionToPreviousStartingStep)
  1024. // {
  1025. // std::cout << "TRANSITION TO PREVIOUS STARTING STEP EXISTS" << std::endl;
  1026. // //d->TransitionToPreviousStartingStep->sourceState()->removeTransition(d->TransitionToPreviousStartingStep);
  1027. // //std::cout << "removed" << std::endl;
  1028. // // d->TransitionToPreviousStartingStep = 0;
  1029. // //destination->processingState()->removeTransition(d->TransitionToPreviousStartingStep);
  1030. // //delete d->TransitionToPreviousStartingStep;
  1031. // // d->TransitionToPreviousStartingStep = 0;
  1032. // std::cout << "here" << std::endl;
  1033. // }
  1034. // If we are trying to get to the finish step, then check if we are
  1035. // finished.
  1036. if (d->GoToStep)
  1037. {
  1038. if (d->CurrentStep == d->GoToStep)
  1039. {
  1040. this->goToStepSucceeded();
  1041. }
  1042. // if we're not finished, continue transitioning to the next step
  1043. else
  1044. {
  1045. this->goForward();
  1046. }
  1047. }
  1048. else
  1049. {
  1050. emit this->currentStepChanged(d->CurrentStep);
  1051. }
  1052. }
  1053. // --------------------------------------------------------------------------
  1054. void ctkWorkflow::goToStepSucceeded()
  1055. {
  1056. Q_D(ctkWorkflow);
  1057. logger.debug("goToStepSucceeded");
  1058. // after success, go back to the step at which we begin looking for
  1059. // the finish step (will exit the current step and enter the starting step)
  1060. d->createTransitionToPreviousStartingStep(d->StartingStep, d->CurrentStep);
  1061. d->GoToStep = 0;
  1062. d->StartingStep->setStatusText("Attempt to go to the finish step succeeded");
  1063. d->StartingStep = 0;
  1064. this->goFromGoToStepToStartingStep();
  1065. }
  1066. // --------------------------------------------------------------------------
  1067. void ctkWorkflow::goFromGoToStepToStartingStep()
  1068. {
  1069. Q_D(ctkWorkflow);
  1070. logger.info("goFromGoToStepToStartingStep - Posting TransitionToPreviousStartingStep");
  1071. d->StateMachine->postEvent(new ctkWorkflowInterstepTransitionEvent(ctkWorkflowInterstepTransition::TransitionToPreviousStartingStepAfterSuccessfulGoToFinishStep));
  1072. }
  1073. // --------------------------------------------------------------------------
  1074. void ctkWorkflow::goToStepFailed()
  1075. {
  1076. // Abort attempt to get to the finish step
  1077. Q_D(ctkWorkflow);
  1078. d->GoToStep = 0;
  1079. d->StartingStep = 0;
  1080. // We don't need to transition between steps - leave the user at the
  1081. // point of failure, so that they can try to continue manually
  1082. // Emit the signal that we have changed the current step, since it wasn't emitted in the process
  1083. // of going to the 'goTo' step.
  1084. emit this->currentStepChanged(d->CurrentStep);
  1085. // if (failedOnBranch)
  1086. // {
  1087. // this->goToProcessingStateAfterValidationFailed();
  1088. // }
  1089. }