ctkWorkflowWidget.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) 2010 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 <QWidget>
  16. #include <QBoxLayout>
  17. #include <QLabel>
  18. #include <QPushButton>
  19. #include <QList>
  20. #include <QDebug>
  21. // CTK includes
  22. #include "ctkWorkflowWidget.h"
  23. #include "ctkWorkflowStep.h"
  24. #include "ctkWorkflowWidgetStep.h"
  25. #include "ctkWorkflow.h"
  26. #include "ctkWorkflowButtonBoxWidget.h"
  27. #include "ctkLogger.h"
  28. // STD includes
  29. #include <iostream>
  30. //--------------------------------------------------------------------------
  31. static ctkLogger logger("org.commontk.libs.widgets.ctkWorkflowWidget");
  32. //--------------------------------------------------------------------------
  33. //-----------------------------------------------------------------------------
  34. class ctkWorkflowWidgetPrivate: public ctkPrivate<ctkWorkflowWidget>
  35. {
  36. public:
  37. CTK_DECLARE_PUBLIC(ctkWorkflowWidget);
  38. ctkWorkflowWidgetPrivate();
  39. ~ctkWorkflowWidgetPrivate();
  40. ctkWorkflow* workflow;
  41. // These are place-holders, so that widgets appear in the correct
  42. // place given the uncertain order in which they are created by the
  43. // user using the set() functions. The layouts that are assigned to
  44. // them in the set() functions are arbirary, since each section will
  45. // hold only one widget.
  46. QWidget* titleSection;
  47. QWidget* subTitleSection;
  48. QWidget* preTextSection;
  49. QWidget* clientSection;
  50. QWidget* postTextSection;
  51. QWidget* errorSection;
  52. QWidget* buttonSection;
  53. QLabel* titleLabel;
  54. QLabel* subTitleLabel;
  55. QLabel* preTextLabel;
  56. QWidget* clientArea;
  57. QLabel* postTextLabel;
  58. QLabel* errorLabel;
  59. // orientation for layout of this widget, and of the client area
  60. // (for use with QBoxLayout only)
  61. QBoxLayout::Direction direction;
  62. QBoxLayout::Direction clientAreaDirection;
  63. int hideWidgetsOfNonCurrentSteps;
  64. ctkWorkflowStep* stepShownPreviously;
  65. ctkWorkflowStep* stepShownCurrently;
  66. ctkWorkflowButtonBoxWidget* buttonBoxWidget;
  67. bool hasButtonBoxWidget;
  68. };
  69. // --------------------------------------------------------------------------
  70. // ctkWorkflowWidgetPrivate methods
  71. //---------------------------------------------------------------------------
  72. ctkWorkflowWidgetPrivate::ctkWorkflowWidgetPrivate()
  73. {
  74. this->workflow = 0;
  75. this->titleSection = new QWidget;
  76. this->subTitleSection = new QWidget;
  77. this->preTextSection = new QWidget;
  78. this->clientSection = new QWidget;
  79. this->postTextSection = new QWidget;
  80. this->errorSection = new QWidget;
  81. this->buttonSection = new QWidget;
  82. this->titleLabel = 0;
  83. this->subTitleLabel = 0;
  84. this->preTextLabel = 0;
  85. this->clientArea = 0;
  86. this->postTextLabel = 0;
  87. this->errorLabel = 0;
  88. direction = QBoxLayout::TopToBottom;
  89. clientAreaDirection = QBoxLayout::TopToBottom;
  90. this->hideWidgetsOfNonCurrentSteps = 0;
  91. this->stepShownPreviously = 0;
  92. this->stepShownCurrently = 0;
  93. this->hasButtonBoxWidget = true;
  94. this->buttonBoxWidget = 0;
  95. }
  96. //---------------------------------------------------------------------------
  97. ctkWorkflowWidgetPrivate::~ctkWorkflowWidgetPrivate()
  98. {
  99. }
  100. // --------------------------------------------------------------------------
  101. // ctkWorkflowWidgetMethods
  102. // --------------------------------------------------------------------------
  103. ctkWorkflowWidget::ctkWorkflowWidget(QWidget* _parent) : Superclass(_parent)
  104. {
  105. CTK_INIT_PRIVATE(ctkWorkflowWidget);
  106. CTK_D(ctkWorkflowWidget);
  107. // layout components vertically by default
  108. QBoxLayout* layout = new QBoxLayout(d->direction);
  109. this->setLayout(layout);
  110. layout->addWidget(d->titleSection);
  111. layout->addWidget(d->subTitleSection);
  112. layout->addWidget(d->preTextSection);
  113. layout->addWidget(d->clientSection);
  114. layout->addWidget(d->postTextSection);
  115. layout->addWidget(d->errorSection);
  116. this->setErrorText("");
  117. layout->addWidget(d->buttonSection);
  118. }
  119. // --------------------------------------------------------------------------
  120. ctkWorkflowWidget::~ctkWorkflowWidget()
  121. {
  122. }
  123. // --------------------------------------------------------------------------
  124. CTK_GET_CXX(ctkWorkflowWidget, ctkWorkflow*, workflow, workflow);
  125. CTK_GET_CXX(ctkWorkflowWidget, QBoxLayout::Direction, direction, direction);
  126. CTK_GET_CXX(ctkWorkflowWidget, QBoxLayout::Direction, clientAreaDirection, clientAreaDirection);
  127. CTK_GET_CXX(ctkWorkflowWidget, int, hideWidgetsOfNonCurrentSteps, hideWidgetsOfNonCurrentSteps);
  128. CTK_SET_CXX(ctkWorkflowWidget, int, setHideWidgetsOfNonCurrentSteps, hideWidgetsOfNonCurrentSteps);
  129. CTK_GET_CXX(ctkWorkflowWidget, bool, hasButtonBoxWidget, hasButtonBoxWidget);
  130. CTK_SET_CXX(ctkWorkflowWidget, bool, setHasButtonBoxWidget, hasButtonBoxWidget);
  131. CTK_GET_CXX(ctkWorkflowWidget, ctkWorkflowButtonBoxWidget*, buttonBoxWidget, buttonBoxWidget);
  132. // --------------------------------------------------------------------------
  133. void ctkWorkflowWidget::setWorkflow(ctkWorkflow* newWorkflow)
  134. {
  135. CTK_D(ctkWorkflowWidget);
  136. if (d->workflow)
  137. {
  138. QObject::disconnect(d->workflow, SIGNAL(currentStepChanged(ctkWorkflowStep*)), this, SLOT(updateClientArea(ctkWorkflowStep)));
  139. }
  140. d->workflow = newWorkflow;
  141. QObject::connect(newWorkflow, SIGNAL(currentStepChanged(ctkWorkflowStep*)), this, SLOT(updateClientArea(ctkWorkflowStep*)));
  142. // TODO workflow should be a parameter of ctkWorkflowWidget
  143. // setup the buttonBoxWidget
  144. if (d->hasButtonBoxWidget)
  145. {
  146. if (!d->buttonSection->layout())
  147. {
  148. d->buttonSection->setLayout(new QBoxLayout(QBoxLayout::TopToBottom));
  149. }
  150. d->buttonBoxWidget = new ctkWorkflowButtonBoxWidget(d->workflow);
  151. d->buttonSection->layout()->addWidget(d->buttonBoxWidget);
  152. }
  153. }
  154. // --------------------------------------------------------------------------
  155. void ctkWorkflowWidget::changeWidgetDirection(QWidget* widget, const QBoxLayout::Direction& direction)
  156. {
  157. // easy to change layout direction if we don't already have a layout
  158. if (!widget->layout())
  159. {
  160. widget->setLayout(new QBoxLayout(direction));
  161. }
  162. // do nothing if the page's layout cannot be cast to a QBoxLayout
  163. else if (QBoxLayout* widgetLayout = qobject_cast<QBoxLayout*>(widget->layout()))
  164. {
  165. widgetLayout->setDirection(direction);
  166. }
  167. }
  168. // --------------------------------------------------------------------------
  169. void ctkWorkflowWidget::setDirection(const QBoxLayout::Direction& direction)
  170. {
  171. CTK_D(ctkWorkflowWidget);
  172. this->changeWidgetDirection(this, direction);
  173. d->direction = direction;
  174. }
  175. // --------------------------------------------------------------------------
  176. void ctkWorkflowWidget::setClientAreaDirection(const QBoxLayout::Direction& direction)
  177. {
  178. this->changeWidgetDirection(this->clientArea(), direction);
  179. this->setPrivateClientAreaDirection(direction);
  180. }
  181. // --------------------------------------------------------------------------
  182. void ctkWorkflowWidget::setPrivateClientAreaDirection(const QBoxLayout::Direction& direction)
  183. {
  184. CTK_D(ctkWorkflowWidget);
  185. d->clientAreaDirection = direction;
  186. }
  187. // --------------------------------------------------------------------------
  188. // reimplement in derived classes
  189. QWidget* ctkWorkflowWidget::clientArea()
  190. {
  191. CTK_D(ctkWorkflowWidget);
  192. if (!d->clientArea)
  193. {
  194. d->clientArea = new QWidget;
  195. if (!d->clientSection->layout())
  196. {
  197. d->clientSection->setLayout(new QBoxLayout(QBoxLayout::TopToBottom));
  198. }
  199. d->clientSection->layout()->addWidget(d->clientArea);
  200. }
  201. return d->clientArea;
  202. }
  203. // --------------------------------------------------------------------------
  204. // reimplement in derived classes
  205. void ctkWorkflowWidget::addWidget(QWidget* widget)
  206. {
  207. if (widget)
  208. {
  209. QWidget* clientArea = this->clientArea();
  210. // set the default client area layout, if there isn't one set.
  211. CTK_D(ctkWorkflowWidget);
  212. if (!clientArea->layout())
  213. {
  214. clientArea->setLayout(new QBoxLayout(d->clientAreaDirection));
  215. }
  216. clientArea->layout()->addWidget(widget);
  217. }
  218. }
  219. // --------------------------------------------------------------------------
  220. // TODO needs work
  221. void ctkWorkflowWidget::updateClientArea(ctkWorkflowStep* currentStep)
  222. {
  223. // if we are trying to get to the finish step, then we've completely
  224. // exited the origin - we can set its changeUserInterface flag back
  225. // to normal, for the next time
  226. CTK_D(ctkWorkflowWidget);
  227. if (currentStep)
  228. {
  229. d->stepShownPreviously = d->stepShownCurrently;
  230. d->stepShownCurrently = currentStep;
  231. this->setTitle(currentStep->name());
  232. this->setSubTitle(currentStep->description());
  233. this->setErrorText(currentStep->statusText());
  234. // disable/hide the previously shown step
  235. if (ctkWorkflowWidgetStep* prevStep = qobject_cast<ctkWorkflowWidgetStep*>(d->stepShownPreviously))
  236. {
  237. logger.debug(QString("updateClientArea - hiding %1").arg(prevStep->name()));
  238. if (QWidget* stepArea = prevStep->stepArea())
  239. {
  240. stepArea->setEnabled(false);
  241. if (d->hideWidgetsOfNonCurrentSteps)
  242. {
  243. stepArea->hide();
  244. }
  245. }
  246. }
  247. ctkWorkflowWidgetStep* currentWidgetStep = qobject_cast<ctkWorkflowWidgetStep*>(currentStep);
  248. // show/enable the current step
  249. if (currentWidgetStep)
  250. {
  251. currentWidgetStep->showUserInterface();
  252. if (QWidget* stepArea = currentWidgetStep->stepArea())
  253. {
  254. // add the step's client area to the widget if we haven't before
  255. if (!this->isAncestorOf(stepArea))
  256. {
  257. this->addWidget(stepArea);
  258. }
  259. stepArea->setEnabled(true);
  260. this->showWidget(stepArea);
  261. }
  262. }
  263. // update the button box widget if we have one
  264. if (d->buttonBoxWidget)
  265. {
  266. // Hide button bar if specified by the steps
  267. bool hideButtonBar = false;
  268. if(currentWidgetStep)
  269. {
  270. hideButtonBar = currentWidgetStep->buttonBoxHints() & ctkWorkflowWidgetStep::ButtonBoxHidden;
  271. }
  272. d->buttonBoxWidget->setHidden(hideButtonBar);
  273. d->buttonBoxWidget->updateButtons();
  274. }
  275. }
  276. }
  277. // --------------------------------------------------------------------------
  278. CTK_GET_CXX(ctkWorkflowWidget, QWidget*, clientSection, clientSection);
  279. // --------------------------------------------------------------------------
  280. // reimplement in derived classes
  281. void ctkWorkflowWidget::showWidget(QWidget* widget)
  282. {
  283. if (widget)
  284. {
  285. widget->show();
  286. }
  287. }
  288. // --------------------------------------------------------------------------
  289. QString ctkWorkflowWidget::title()const
  290. {
  291. CTK_D(const ctkWorkflowWidget);
  292. if (!d->titleLabel)
  293. {
  294. return 0;
  295. }
  296. return d->titleLabel->text();
  297. }
  298. // --------------------------------------------------------------------------
  299. void ctkWorkflowWidget::setTitle(const QString& titleText)
  300. {
  301. if (!titleText.isEmpty())
  302. {
  303. CTK_D(ctkWorkflowWidget);
  304. if (!d->titleLabel)
  305. {
  306. d->titleLabel = new QLabel;
  307. if (!d->titleSection->layout())
  308. {
  309. d->titleSection->setLayout(new QBoxLayout(QBoxLayout::TopToBottom));
  310. }
  311. d->titleSection->layout()->addWidget(d->titleLabel);
  312. }
  313. d->titleLabel->setText(titleText);
  314. }
  315. }
  316. // --------------------------------------------------------------------------
  317. QString ctkWorkflowWidget::subTitle()const
  318. {
  319. CTK_D(const ctkWorkflowWidget);
  320. if (!d->subTitleLabel)
  321. {
  322. return 0;
  323. }
  324. return d->subTitleLabel->text();
  325. }
  326. // --------------------------------------------------------------------------
  327. void ctkWorkflowWidget::setSubTitle(const QString& subTitleText)
  328. {
  329. if (!subTitleText.isEmpty())
  330. {
  331. CTK_D(ctkWorkflowWidget);
  332. if (!d->subTitleLabel)
  333. {
  334. d->subTitleLabel = new QLabel;
  335. if (!d->subTitleSection->layout())
  336. {
  337. d->subTitleSection->setLayout(new QBoxLayout(QBoxLayout::TopToBottom));
  338. }
  339. d->subTitleSection->layout()->addWidget(d->subTitleLabel);
  340. }
  341. d->subTitleLabel->setText(subTitleText);
  342. }
  343. }
  344. // --------------------------------------------------------------------------
  345. QString ctkWorkflowWidget::preText()const
  346. {
  347. CTK_D(const ctkWorkflowWidget);
  348. if (!d->preTextLabel)
  349. {
  350. return 0;
  351. }
  352. return d->preTextLabel->text();
  353. }
  354. // --------------------------------------------------------------------------
  355. void ctkWorkflowWidget::setPreText(const QString& preText)
  356. {
  357. if (!preText.isEmpty())
  358. {
  359. CTK_D(ctkWorkflowWidget);
  360. if (!d->preTextLabel)
  361. {
  362. d->preTextLabel = new QLabel;
  363. if (!d->preTextSection->layout())
  364. {
  365. d->preTextSection->setLayout(new QBoxLayout(QBoxLayout::TopToBottom));
  366. }
  367. d->preTextSection->layout()->addWidget(d->preTextLabel);
  368. }
  369. d->preTextLabel->setText(preText);
  370. }
  371. }
  372. // --------------------------------------------------------------------------
  373. QString ctkWorkflowWidget::postText()const
  374. {
  375. CTK_D(const ctkWorkflowWidget);
  376. if (!d->postTextLabel)
  377. {
  378. return 0;
  379. }
  380. return d->postTextLabel->text();
  381. }
  382. // --------------------------------------------------------------------------
  383. void ctkWorkflowWidget::setPostText(const QString& postText)
  384. {
  385. if (!postText.isEmpty())
  386. {
  387. CTK_D(ctkWorkflowWidget);
  388. if (!d->postTextLabel)
  389. {
  390. d->postTextLabel = new QLabel;
  391. if (!d->postTextSection->layout())
  392. {
  393. d->postTextSection->setLayout(new QBoxLayout(QBoxLayout::TopToBottom));
  394. }
  395. d->postTextSection->layout()->addWidget(d->postTextLabel);
  396. }
  397. d->postTextLabel->setText(postText);
  398. }
  399. }
  400. // --------------------------------------------------------------------------
  401. QString ctkWorkflowWidget::errorText()const
  402. {
  403. CTK_D(const ctkWorkflowWidget);
  404. if (!d->errorLabel)
  405. {
  406. return 0;
  407. }
  408. return d->errorLabel->text();
  409. }
  410. // --------------------------------------------------------------------------
  411. void ctkWorkflowWidget::setErrorText(const QString& errorText)
  412. {
  413. CTK_D(ctkWorkflowWidget);
  414. if (!d->errorLabel)
  415. {
  416. d->errorLabel = new QLabel;
  417. if (!d->errorSection->layout())
  418. {
  419. d->errorSection->setLayout(new QBoxLayout(QBoxLayout::TopToBottom));
  420. }
  421. d->errorSection->layout()->addWidget(d->errorLabel);
  422. }
  423. d->errorLabel->setText("Status: " + errorText);
  424. }