123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- /*=========================================================================
- 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.apache.org/licenses/LICENSE-2.0.txt
- 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.
- =========================================================================*/
- // Qt includes
- #include <QApplication>
- #include <QDebug>
- #include <QPointer>
- #include <QStyle>
- // CTK includes
- #include "ctkPushButton.h"
- #include "ctkWorkflowWidget.h"
- #include "ctkWorkflowStep.h"
- #include "ctkWorkflowWidgetStep.h"
- #include "ctkWorkflow.h"
- #include "ctkWorkflowButtonBoxWidget.h"
- #include "ctkWorkflowGroupBox.h"
- // STD includes
- #include <iostream>
- //-----------------------------------------------------------------------------
- class ctkWorkflowWidgetPrivate
- {
- public:
- ctkWorkflowWidgetPrivate();
- ~ctkWorkflowWidgetPrivate();
- QPointer<ctkWorkflow> Workflow;
- ctkWorkflowGroupBox* WorkflowGroupBox;
- ctkWorkflowButtonBoxWidget* ButtonBoxWidget;
- bool ShowButtonBoxWidget;
- };
- // --------------------------------------------------------------------------
- // ctkWorkflowWidgetPrivate methods
- //---------------------------------------------------------------------------
- ctkWorkflowWidgetPrivate::ctkWorkflowWidgetPrivate()
- {
- this->WorkflowGroupBox = 0;
- this->ButtonBoxWidget = 0;
- this->ShowButtonBoxWidget = true;
- }
- //---------------------------------------------------------------------------
- ctkWorkflowWidgetPrivate::~ctkWorkflowWidgetPrivate()
- {
- if (!this->Workflow.isNull())
- {
- foreach(ctkWorkflowStep* step, this->Workflow.data()->steps())
- {
- ctkWorkflowWidgetStep * widgetStep = dynamic_cast<ctkWorkflowWidgetStep*>(step);
- if (widgetStep)
- {
- widgetStep->setVisible(false);
- widgetStep->setParent(0);
- }
- }
- }
- }
- // --------------------------------------------------------------------------
- // ctkWorkflowWidgetMethods
- // --------------------------------------------------------------------------
- ctkWorkflowWidget::ctkWorkflowWidget(QWidget* _parent) : Superclass(_parent)
- , d_ptr(new ctkWorkflowWidgetPrivate)
- {
- Q_D(ctkWorkflowWidget);
- d->WorkflowGroupBox = new ctkWorkflowGroupBox(this);
- d->ButtonBoxWidget = new ctkWorkflowButtonBoxWidget();
- }
- // --------------------------------------------------------------------------
- ctkWorkflowWidget::~ctkWorkflowWidget()
- {
- }
- // --------------------------------------------------------------------------
- ctkWorkflow* ctkWorkflowWidget::workflow()const
- {
- Q_D(const ctkWorkflowWidget);
- return d->Workflow.data();
- }
- // --------------------------------------------------------------------------
- CTK_GET_CPP(ctkWorkflowWidget, ctkWorkflowGroupBox*, workflowGroupBox, WorkflowGroupBox);
- CTK_GET_CPP(ctkWorkflowWidget, bool, showButtonBoxWidget, ShowButtonBoxWidget);
- CTK_SET_CPP(ctkWorkflowWidget, bool, setShowButtonBoxWidget, ShowButtonBoxWidget);
- CTK_GET_CPP(ctkWorkflowWidget, ctkWorkflowButtonBoxWidget*, buttonBoxWidget, ButtonBoxWidget);
- // --------------------------------------------------------------------------
- void ctkWorkflowWidget::setWorkflow(ctkWorkflow* newWorkflow)
- {
- Q_D(ctkWorkflowWidget);
- if (!newWorkflow)
- {
- qWarning() << "setWorkflow - cannot set workflow to NULL";
- return;
- }
- if (!d->Workflow.isNull())
- {
- QObject::disconnect(d->Workflow.data(), SIGNAL(currentStepChanged(ctkWorkflowStep*)),
- this, SLOT(onCurrentStepChanged(ctkWorkflowStep)));
- QObject::disconnect(d->Workflow.data(), SIGNAL(stepRegistered(ctkWorkflowStep*)),
- this, SLOT(onStepRegistered(ctkWorkflowStep)));
- }
- d->Workflow = newWorkflow;
- if (!d->Workflow.isNull())
- {
- foreach(ctkWorkflowStep* step, d->Workflow.data()->steps())
- {
- this->onStepRegistered(step);
- }
- }
- QObject::connect(newWorkflow, SIGNAL(currentStepChanged(ctkWorkflowStep*)), this, SLOT(onCurrentStepChanged(ctkWorkflowStep*)));
- QObject::connect(newWorkflow, SIGNAL(stepRegistered(ctkWorkflowStep*)), this, SLOT(onStepRegistered(ctkWorkflowStep*)));
- d->ButtonBoxWidget->setWorkflow(newWorkflow);
- }
- // --------------------------------------------------------------------------
- ctkWorkflowWidgetStep* ctkWorkflowWidget::widgetStep(const QString& id)const
- {
- Q_D(const ctkWorkflowWidget);
- return dynamic_cast<ctkWorkflowWidgetStep*>(
- !d->Workflow.isNull() ? d->Workflow.data()->step(id) : 0);
- }
- // --------------------------------------------------------------------------
- void ctkWorkflowWidget::onCurrentStepChanged(ctkWorkflowStep* currentStep)
- {
- if (currentStep)
- {
- this->updateStepUI(currentStep);
- this->updateButtonBoxUI(currentStep);
- }
- }
- // --------------------------------------------------------------------------
- void ctkWorkflowWidget::onStepRegistered(ctkWorkflowStep* step)
- {
- if (step->isWidgetType())
- {
- QWidget * widget = dynamic_cast<QWidget*>(step);
- Q_ASSERT(widget);
- widget->setParent(this);
- widget->setVisible(false);
- }
- }
- // --------------------------------------------------------------------------
- void ctkWorkflowWidget::updateStepUI(ctkWorkflowStep* currentStep)
- {
- Q_D(ctkWorkflowWidget);
- Q_ASSERT(currentStep);
- Q_ASSERT(d->WorkflowGroupBox);
- // Create layout and WorkflowGroupBox if this is our first time here
- if (!this->layout())
- {
- QVBoxLayout* layout = new QVBoxLayout();
- this->setLayout(layout);
- layout->addWidget(d->WorkflowGroupBox);
- if (d->ShowButtonBoxWidget)
- {
- layout->addWidget(d->ButtonBoxWidget);
- }
- layout->setContentsMargins(0,0,0,0);
- }
- d->WorkflowGroupBox->updateGroupBox(currentStep);
- }
- // --------------------------------------------------------------------------
- void ctkWorkflowWidget::updateButtonBoxUI(ctkWorkflowStep* currentStep)
- {
- Q_D(ctkWorkflowWidget);
- Q_ASSERT(currentStep);
- // Update the button box widget if we want to show it
- if (d->ShowButtonBoxWidget)
- {
- d->ButtonBoxWidget->updateButtons(currentStep);
- }
- }
- //-----------------------------------------------------------------------------
- QVariant ctkWorkflowWidget::buttonItem(QString item,
- ctkWorkflowWidgetStep* step)
- {
- QRegExp backRegExp("^[\\{\\(\\[]back:(.*)[\\}\\)\\]]$");
- QRegExp nextRegExp("^[\\{\\(\\[]next:(.*)[\\}\\)\\]]$");
- QRegExp currentRegExp("^[\\{\\(\\[]current:(.*)[\\}\\)\\]]$");
- if (backRegExp.exactMatch(item))
- {
- QList<ctkWorkflowStep*> backs =
- (step ? step->workflow()->backwardSteps(step) : QList<ctkWorkflowStep*>());
- step = (backs.size() ? dynamic_cast<ctkWorkflowWidgetStep*>(backs[0]) : 0);
- item.remove("back:");
- return ctkWorkflowWidget::buttonItem(item, step);
- }
- else if (nextRegExp.exactMatch(item))
- {
- QList<ctkWorkflowStep*> nexts =
- step ? step->workflow()->forwardSteps(step) : QList<ctkWorkflowStep*>();
- step = (nexts.size() ? dynamic_cast<ctkWorkflowWidgetStep*>(nexts[0]) : 0);
- item.remove("next:");
- return ctkWorkflowWidget::buttonItem(item, step);
- }
- else if (currentRegExp.exactMatch(item))
- {
- item.remove("current:");
- }
- QVariant res;
- QRegExp quotesRegExp("^\"(.*)\"$");
- QRegExp propsRegExp("^[\\{\\(\\[](.*)[\\}\\)\\]]$");
- QStyle* style = (step ? step->style() : qApp->style());
- if (item == "[<-]")
- {
- res.setValue(style->standardIcon(QStyle::SP_ArrowLeft));
- }
- else if (item == "[->]")
- {
- res.setValue(style->standardIcon(QStyle::SP_ArrowRight));
- }
- else if (item == "{#}" || item == "(#)")
- {
- res = QVariant(step ? step->workflow()->backwardDistanceToStep(step) + 1 : 0);
- }
- else if (item == "{!#}" || item == "(!#)")
- {
- res = QVariant(step ? step->workflow()->steps().count() : 0);
- }
- else if (quotesRegExp.exactMatch(item))
- {
- res = quotesRegExp.cap(1);
- }
- else if (propsRegExp.exactMatch(item))
- {
- item = propsRegExp.cap(1);
- if (quotesRegExp.exactMatch(item))
- {
- res = quotesRegExp.cap(1);
- }
- else
- {
- res = step ? step->property(item.toLatin1()) : QVariant();
- if (res.isValid() && res.type() == QVariant::String && res.toString().isEmpty())
- {
- res = QVariant();
- }
- }
- }
- else
- {
- qWarning() << "Item" << item << "not supported";
- }
- return res;
- }
- //-----------------------------------------------------------------------------
- void ctkWorkflowWidget
- ::formatButton(QAbstractButton* button, const QString& buttonFormat,
- ctkWorkflowWidgetStep* step)
- {
- QMap<QString, QVariant> formats =
- ctkWorkflowWidget::parse(buttonFormat, step);
- button->setIcon(formats["icon"].value<QIcon>());
- if (qobject_cast<ctkPushButton*>(button))
- {
- qobject_cast<ctkPushButton*>(button)->setIconAlignment(
- static_cast<Qt::Alignment>(formats["iconalignment"].toInt()));
- }
- button->setText(formats["text"].toString());
- button->setToolTip(formats["tooltip"].toString());
- }
- //-----------------------------------------------------------------------------
- QString ctkWorkflowWidget
- ::formatText(const QString& textFormat, ctkWorkflowWidgetStep* step)
- {
- QMap<QString, QVariant> formats =
- ctkWorkflowWidget::parse(textFormat, step);
- return formats["text"].toString();
- }
- //-----------------------------------------------------------------------------
- QMap<QString, QVariant> ctkWorkflowWidget
- ::parse(const QString& format, ctkWorkflowWidgetStep* step)
- {
- QIcon buttonIcon;
- Qt::Alignment buttonIconAlignment = Qt::AlignLeft | Qt::AlignVCenter;
- QString buttonText;
- QString buttonToolTip;
- QString textRegExp("\\{[^{}]+\\}");
- QString simpleTextRegExp("\"[^\"]+\"");
- QString toolTipRegExp("\\([^\\(\\)]+\\)");
- QString iconRegExp("\\[[^\\[\\]]+\\]");
- //QRegExp splitBrackets("\\{([^}]+)\\}");
- //QRegExp splitBrackets("(\\{[^{}]+\\}|\\([^\\(\\)]+\\)|\"[^\"]+\")");
- QRegExp splitBrackets(QString("(%1|%2|%3|%4)")
- .arg(textRegExp).arg(simpleTextRegExp)
- .arg(toolTipRegExp)
- .arg(iconRegExp));
- QStringList brackets;
- int pos = 0;
- while ((pos = splitBrackets.indexIn(format, pos)) != -1)
- {
- brackets << splitBrackets.cap(1);
- pos += splitBrackets.matchedLength();
- }
- foreach(const QString& withBracket, brackets)
- {
- bool isSimpleText =
- QRegExp(QString("^") + simpleTextRegExp + QString("$")).exactMatch(withBracket);
- QString withoutBracket = withBracket.mid(1, withBracket.size() - 2);
- // If the item is empty, then check the next item. For example:
- // {next:description|next:name|next:stepid}: if 'next:description' is empty, then
- // use 'next:name' if not empty, otherwise 'next:stepid'
- // Don't split simple text, it is never empty.
- QStringList tokens = isSimpleText ? QStringList(withoutBracket) : withoutBracket.split('|');
- QIcon icon;
- Qt::Alignment iconAlignment = buttonIconAlignment;
- QString text;
- foreach (const QString& token, tokens)
- {
- QString tokenWithBracket = withBracket[0] + token + withBracket[withBracket.size()-1];
- QVariant item = ctkWorkflowWidget::buttonItem(tokenWithBracket, step);
- if (item.isValid())
- {
- switch (item.type())
- {
- case QVariant::Icon:
- icon = item.value<QIcon>();
- if (!buttonText.isEmpty())
- {
- iconAlignment = Qt::AlignRight | Qt::AlignVCenter;
- }
- break;
- case QVariant::String:
- case QVariant::Int:
- text += item.toString();
- break;
- default:
- break;
- }
- // skip the other cases if the item was valid, otherwise keep on searching
- break;
- }
- }
- if (QRegExp(QString("^") + textRegExp + QString("$")).exactMatch(withBracket) ||
- isSimpleText)
- {
- buttonText += text;
- }
- else if (QRegExp(QString("^") + iconRegExp + QString("$")).exactMatch(withBracket))
- {
- buttonIcon = icon;
- buttonIconAlignment = iconAlignment;
- }
- else if (QRegExp(QString("^") + toolTipRegExp + QString("$")).exactMatch(withBracket))
- {
- buttonToolTip = text;
- }
- }
- QMap<QString, QVariant> formats;
- formats["icon"] = buttonIcon;
- formats["iconalignment"] = static_cast<int>(buttonIconAlignment);
- formats["text"] = buttonText;
- formats["tooltip"] = buttonToolTip;
- return formats;
- }
|