23 #include "IsisDebug.h"
28 #include <QFutureWatcher>
30 #include <QtConcurrentRun>
32 #include <QXmlStreamWriter>
36 #include "ProgressBar.h"
38 #include "XmlStackedHandlerReader.h"
52 m_status = WorkOrderNotStarted;
53 m_queuedAction = NoQueuedAction;
54 m_transparentConstMutex = NULL;
55 m_elapsedTimer = NULL;
57 m_context = NoContext;
59 m_futureWatcher =
new QFutureWatcher<void>;
60 m_transparentConstMutex =
new QMutex;
62 m_progressRangeMinValue = 0;
63 m_progressRangeMaxValue = 100;
66 m_secondsElapsed = 0.0;
70 tr(
"Work orders cannot be created without a project."),
_FILEINFO_);
73 connect(
this, SIGNAL(triggered()),
74 this, SLOT(addCloneToProject()));
75 connect(m_futureWatcher, SIGNAL(finished()),
76 this, SLOT(asyncFinished()));
80 WorkOrder::~WorkOrder() {
82 delete m_futureWatcher;
84 delete m_progressBarDeletionTimer;
85 delete m_progressBarUpdateTimer;
86 delete m_transparentConstMutex;
88 m_nextWorkOrder = NULL;
89 m_previousWorkOrder = NULL;
91 m_transparentConstMutex = NULL;
101 QAction(other.icon(), ((
QAction &)other).text(), other.parentWidget()),
103 m_transparentConstMutex = NULL;
104 m_elapsedTimer = NULL;
106 m_project = other.project();
108 m_context = other.m_context;
109 m_imageIds = other.m_imageIds;
110 m_images =
new ImageList(*other.m_images);
111 m_controls = other.m_controls;
112 m_internalData = other.m_internalData;
119 m_status = other.m_status;
120 m_queuedAction = other.m_queuedAction;
122 m_secondsElapsed = other.m_secondsElapsed;
124 m_progressRangeMinValue = other.m_progressRangeMinValue;
125 m_progressRangeMaxValue = other.m_progressRangeMaxValue;
126 m_progressValue = other.m_progressValue;
128 m_futureWatcher =
new QFutureWatcher<void>;
129 m_transparentConstMutex =
new QMutex;
131 if (!other.isInStableState()) {
133 tr(
"Can not copy work order [%1] because it is currently running")
138 connect(
this, SIGNAL(triggered()),
139 this, SLOT(addCloneToProject()));
140 connect(m_futureWatcher, SIGNAL(finished()),
141 this, SLOT(asyncFinished()));
143 listenForImageDestruction();
187 xmlReader->pushContentHandler(
new XmlHandler(
this));
207 if (!isInStableState()) {
209 tr(
"Can not store an unstable work order. The work order [%1] is currently "
210 "working").arg(bestText()),
214 stream.writeStartElement(
"workOrder");
216 stream.writeAttribute(
"actionText", QAction::text());
217 stream.writeAttribute(
"undoText", QUndoCommand::text());
219 stream.writeAttribute(
"type", metaObject()->className());
220 stream.writeAttribute(
"status", toString(m_status));
222 if (m_imageIds.count()) {
223 stream.writeStartElement(
"images");
225 foreach (QString imageId, m_imageIds) {
226 stream.writeStartElement(
"image");
227 stream.writeAttribute(
"id", imageId);
228 stream.writeEndElement();
231 stream.writeEndElement();
234 if (m_internalData.count()) {
235 stream.writeStartElement(
"internalDataValues");
237 foreach (QString str, m_internalData) {
238 stream.writeStartElement(
"dataValue");
239 stream.writeAttribute(
"value", str);
240 stream.writeEndElement();
243 stream.writeEndElement();
246 if (m_context != NoContext) {
247 stream.writeStartElement(
"context");
249 QString contextStr =
"ProjectContext";
250 stream.writeAttribute(
"value", contextStr);
252 stream.writeEndElement();
255 stream.writeEndElement();
259 void WorkOrder::setData(Context context) {
264 void WorkOrder::setData(ImageList *images) {
268 m_images =
new ImageList(*images);
269 listenForImageDestruction();
274 m_controls = controls;
278 void WorkOrder::setNext(WorkOrder *nextWorkOrder) {
279 m_nextWorkOrder = nextWorkOrder;
283 void WorkOrder::setPrevious(WorkOrder *previousWorkOrder) {
284 m_previousWorkOrder = previousWorkOrder;
288 ImageList *WorkOrder::imageList() {
290 bool anyImagesAreNull =
false;
292 m_images =
new ImageList;
294 foreach (QString
id, m_imageIds) {
295 Image *image = project()->image(
id);
296 m_images->append(image);
299 anyImagesAreNull =
true;
303 if (anyImagesAreNull) {
307 listenForImageDestruction();
320 const ImageList *WorkOrder::imageList()
const {
321 QMutexLocker lock(m_transparentConstMutex);
322 return const_cast<WorkOrder *
>(
this)->imageList();
326 bool WorkOrder::dependsOn(WorkOrder *)
const {
331 QString WorkOrder::bestText()
const {
332 QString result = QUndoCommand::text().remove(
"&").remove(
"...");
343 if (result.isEmpty()) {
345 result = QString(metaObject()->className()).remove(
"Isis::").remove(
"WorkOrder")
346 .replace(QRegExp(
"([a-z0-9])([A-Z])"),
"\\1 \\2");
347 qWarning() << QString(
"WorkOrder::bestText(): Work order [%1] has no QUndoCommand text").arg(result);
354 bool WorkOrder::createsCleanState()
const {
359 QDateTime WorkOrder::executionTime()
const {
364 bool WorkOrder::isFinished()
const {
369 bool WorkOrder::isRedoing()
const {
370 return m_status == WorkOrderRedoing;
374 bool WorkOrder::isRedone()
const {
375 return m_status == WorkOrderRedone;
379 bool WorkOrder::isUndoing()
const {
380 return m_status == WorkOrderUndoing;
384 bool WorkOrder::isUndone()
const {
385 return m_status == WorkOrderUndone;
389 bool WorkOrder::modifiesDiskState()
const {
394 WorkOrder *WorkOrder::next()
const {
395 return m_nextWorkOrder;
399 WorkOrder *WorkOrder::previous()
const {
400 return m_previousWorkOrder;
404 QString WorkOrder::statusText()
const {
405 QString result = toString(m_status);
407 if (m_secondsElapsed) {
411 int seconds = qRound(m_secondsElapsed) % 60;
412 int minutes = qRound(m_secondsElapsed) / 60;
413 result += tr(
" (elapsed: %1:%2)").arg(minutes).arg(seconds, 2, 10, QChar(
'0'));
420 ProgressBar *WorkOrder::progressBar() {
421 return m_progressBar;
426 statusString = statusString.toUpper();
430 possibleResult <= WorkOrderLastStatus;
432 if (statusString == toString(possibleResult).toUpper()) {
433 result = possibleResult;
441 QString WorkOrder::toString(WorkOrderStatus status) {
445 case WorkOrderUnknownStatus:
446 result = tr(
"Unknown");
448 case WorkOrderNotStarted:
449 result = tr(
"Not Started");
451 case WorkOrderRedoing:
452 result = tr(
"In Progress");
454 case WorkOrderRedone:
455 result = tr(
"Completed");
457 case WorkOrderUndoing:
458 result = tr(
"Undoing");
460 case WorkOrderUndone:
461 result = tr(
"Undone");
464 result = tr(
"Finished");
476 if (!isInStableState()) {
477 m_queuedAction = RedoQueuedAction;
481 bool mustQueueThisRedo =
false;
485 while (current->previous() && !dependency) {
486 if (!current->previous()->isRedone() && !current->previous()->isFinished()) {
487 WorkOrder *possibleDependency = current->previous();
489 if (dependsOn(possibleDependency)) {
490 connect(possibleDependency, SIGNAL(finished(
WorkOrder *)),
491 this, SLOT(attemptQueuedAction()));
492 dependency = possibleDependency;
493 mustQueueThisRedo =
true;
497 current = current->previous();
501 connect(project(), SIGNAL(imagesAdded(
ImageList *)),
502 this, SLOT(attemptQueuedAction()));
503 mustQueueThisRedo =
true;
506 if (mustQueueThisRedo && !isUndoing() && !isRedoing()) {
507 m_queuedAction = RedoQueuedAction;
509 QString queueStatusText;
512 QString dependencyText = dependency->bestText();
514 if (dependencyText.count() > 5) {
515 dependencyText = dependencyText.mid(0, 5) +
"...";
518 queueStatusText = tr(
"Wait for [%1]").arg(dependencyText);
520 else if (!imageList()) {
521 queueStatusText = tr(
"Wait for images");
525 m_progressBar->setValue(m_progressBar->minimum());
526 m_progressBar->setText(queueStatusText);
527 m_progressBar->update();
530 if (m_queuedAction == NoQueuedAction) {
531 m_status = WorkOrderRedoing;
532 emit statusChanged(
this);
535 m_progressBar->setText(
"Starting...");
536 m_progressBar->update();
538 delete m_elapsedTimer;
539 m_elapsedTimer =
new QTime;
540 m_elapsedTimer->start();
544 m_progressBar->setText(
"Running...");
545 m_progressBar->update();
547 m_futureWatcher->setFuture(future);
551 setProgressToFinalText();
560 if (!isInStableState()) {
561 m_queuedAction = UndoQueuedAction;
564 if (!isUndone() && m_status != WorkOrderNotStarted) {
567 while (current->next() && !dependency) {
568 if (!current->next()->isUndone() && !current->next()->isFinished() &&
569 current->next()->m_status != WorkOrderNotStarted) {
570 connect(current->next(), SIGNAL(finished(
WorkOrder *)),
571 this, SLOT(attemptQueuedAction()));
572 m_queuedAction = UndoQueuedAction;
573 dependency = current->next();
576 current = current->next();
579 if (dependency && !isUndoing() && !isRedoing()) {
580 QString prevText = dependency->bestText();
582 if (prevText.count() > 5) {
583 prevText = prevText.mid(0, 5) +
"...";
587 m_progressBar->setValue(m_progressBar->minimum());
588 m_progressBar->setText(tr(
"Undo after [%1]").arg(prevText));
589 m_progressBar->update();
592 if (m_queuedAction == NoQueuedAction) {
593 m_status = WorkOrderUndoing;
594 emit statusChanged(
this);
597 m_progressBar->setText(
"Starting Undo...");
598 m_progressBar->update();
600 delete m_elapsedTimer;
601 m_elapsedTimer =
new QTime;
602 m_elapsedTimer->start();
606 m_progressBar->setText(
"Undoing...");
607 m_progressBar->update();
609 m_futureWatcher->setFuture(future);
613 setProgressToFinalText();
644 if (createsCleanState()) {
647 emit statusChanged(
this);
654 if (createsCleanState()) {
655 setProgressToFinalText();
658 m_progressBar->setText(
"Initializing...");
665 Directory *WorkOrder::directory()
const {
670 Project *WorkOrder::project()
const {
673 "This work order no longer has a project.",
_FILEINFO_);
680 void WorkOrder::setInternalData(
QStringList data) {
681 m_internalData = data;
685 int WorkOrder::progressMin()
const {
686 return m_progressRangeMinValue;
690 int WorkOrder::progressMax()
const {
691 return m_progressRangeMaxValue;
695 int WorkOrder::progressValue()
const {
696 return m_progressValue;
700 void WorkOrder::setProgressRange(
int minValue,
int maxValue) {
701 m_progressRangeMinValue = minValue;
702 m_progressRangeMaxValue = maxValue;
706 void WorkOrder::setProgressValue(
int value) {
707 m_progressValue = value;
712 return m_internalData;
811 void WorkOrder::addCloneToProject() {
818 bool WorkOrder::isInStableState()
const {
821 if (isRedoing() || isUndoing() || m_queuedAction != NoQueuedAction) {
829 void WorkOrder::listenForImageDestruction() {
831 foreach (Image *image, *m_images) {
833 m_imageIds.append(image->id());
837 connect(image, SIGNAL(destroyed(
QObject *)),
838 this, SLOT(clearImageList()));
844 void WorkOrder::resetProgressBar() {
845 delete m_progressBarDeletionTimer;
847 if (!m_progressBar) {
848 m_progressBar =
new ProgressBar;
849 emit creatingProgress(
this);
852 if (!m_progressBarUpdateTimer) {
853 m_progressBarUpdateTimer =
new QTimer;
854 connect(m_progressBarUpdateTimer, SIGNAL(timeout()),
855 this, SLOT(updateProgress()));
856 m_progressBarUpdateTimer->start(100);
859 m_progressRangeMinValue = 0;
860 m_progressRangeMaxValue = 100;
865 void WorkOrder::setProgressToFinalText() {
868 m_progressBar->setText(tr(
"Finished"));
870 else if (isUndone() || m_status == WorkOrderNotStarted) {
871 m_progressBar->setText(tr(
"Undone"));
874 if (m_progressBar->minimum() != 0 || m_progressBar->maximum() != 0) {
875 m_progressBar->setValue(m_progressBar->maximum());
878 m_progressBar->setRange(0, 100);
879 m_progressBar->setValue(100);
882 delete m_progressBarDeletionTimer;
883 m_progressBarDeletionTimer =
new QTimer;
884 m_progressBarDeletionTimer->setSingleShot(
true);
885 connect(m_progressBarDeletionTimer, SIGNAL(timeout()),
886 this, SLOT(deleteProgress()));
888 m_progressBarDeletionTimer->start(5 * 1000);
890 m_progressBar->update();
895 void WorkOrder::attemptQueuedAction() {
896 QueuedWorkOrderAction queued = m_queuedAction;
897 m_queuedAction = NoQueuedAction;
899 if (queued == RedoQueuedAction && m_status != WorkOrderRedone) {
902 else if (queued == UndoQueuedAction && m_status != WorkOrderUndone) {
908 void WorkOrder::asyncFinished() {
909 delete m_progressBarUpdateTimer;
915 finishedStatus = WorkOrderUndone;
919 (this->*postSyncMethod)();
921 m_status = finishedStatus;
923 m_secondsElapsed = m_elapsedTimer->elapsed() / 1000.0;
925 delete m_elapsedTimer;
926 m_elapsedTimer = NULL;
928 emit statusChanged(
this);
929 setProgressToFinalText();
932 attemptQueuedAction();
936 void WorkOrder::clearImageList() {
941 void WorkOrder::deleteProgress() {
942 ProgressBar *progress = m_progressBar;
945 m_progressBar = NULL;
946 emit deletingProgress(
this);
952 void WorkOrder::updateProgress() {
953 if (m_progressBar && (isRedoing() || isUndoing())) {
954 m_progressBar->setRange(m_progressRangeMinValue, m_progressRangeMaxValue);
955 m_progressBar->setValue(m_progressValue);
960 void WorkOrder::startRedo() {
976 void WorkOrder::setModifiesDiskState(
bool changesProjectOnDisk) {
981 WorkOrder::XmlHandler::XmlHandler(WorkOrder *workOrder) {
982 m_workOrder = workOrder;
992 const QString &qName,
const QXmlAttributes &atts) {
993 if (XmlStackedHandler::startElement(namespaceURI, localName, qName, atts)) {
994 if (localName ==
"workOrder") {
995 QString actionText = atts.value(
"actionText");
996 QString undoText = atts.value(
"undoText");
997 QString executionTime = atts.value(
"executionTime");
998 QString statusStr = atts.value(
"status");
1000 if (!actionText.isEmpty()) {
1001 ((
QAction *)m_workOrder)->setText(actionText);
1004 if (!undoText.isEmpty()) {
1008 if (!executionTime.isEmpty()) {
1009 m_workOrder->m_executionTime = QDateTime::fromString(executionTime);
1012 if (!statusStr.isEmpty()) {
1013 m_workOrder->m_status = fromStatusString(statusStr);
1016 if (m_workOrder->createsCleanState()) {
1020 m_workOrder->m_status = WorkOrderRedone;
1024 else if (localName ==
"dataValue") {
1025 m_workOrder->m_internalData.append(atts.value(
"value"));
1027 else if (localName ==
"context") {
1028 if (atts.value(
"value") ==
"ProjectContext") {
1029 m_workOrder->m_context = ProjectContext;