24 #include "IsisDebug.h"
29 #include <QApplication>
31 #include <QFileDialog>
33 #include <QFutureWatcher>
34 #include <QMessageBox>
36 #include <QMutexLocker>
37 #include <QProgressBar>
38 #include <QStringList>
40 #include <QXmlStreamWriter>
43 #include "ControlList.h"
49 #include "ImageList.h"
50 #include "ImageReader.h"
52 #include "ProgressBar.h"
55 #include "XmlStackedHandlerReader.h"
68 m_idToControlMap = NULL;
69 m_idToImageMap = NULL;
73 m_workOrderHistory = NULL;
74 m_isTemporaryProject =
true;
76 m_numImagesCurrentlyReading = 0;
79 m_imageReadingMutex = NULL;
87 QDir tempDir = QDir::temp();
90 QApplication::applicationName() +
"_*");
91 tempDir.setNameFilters(nameFilters);
94 bool crashedPreviously =
false;
96 foreach (QString existingProject, existingProjects) {
97 FileName existingProjectFileName(tempDir.absolutePath() +
"/" + existingProject);
98 QString pidString = QString(existingProject).replace(QRegExp(
".*_"),
"");
99 int otherPid = pidString.toInt();
102 if (!QFile::exists(
"/proc/" + pidString)) {
103 crashedPreviously =
true;
104 int status = system( (
"rm -rf '" + existingProjectFileName.expanded() +
"' &").toAscii().data() );
106 QString msg =
"Executing command [rm -rf" + existingProjectFileName.expanded() +
107 "' &] failed with return status [" +
toString(status) +
"]";
114 if (crashedPreviously &&
false) {
115 QMessageBox::information(NULL,
116 QObject::tr(
"Crashed"),
117 QObject::tr(
"It appears %1 crashed. We're sorry.").arg(QApplication::applicationName()));
122 QString tmpFolder = QDir::temp().absolutePath() +
"/"
124 + QApplication::applicationName() +
"_" + QString::number(getpid());
125 QDir temp(tmpFolder +
"/tmpProject");
126 m_projectRoot =
new QDir(temp);
133 catch (std::exception &e) {
136 tr(
"Error creating project folders [%1]").arg(e.what()),
_FILEINFO_);
140 m_mutex =
new QMutex;
144 connect(m_imageReader, SIGNAL(imagesReady(
ImageList)),
this, SLOT(imagesReady(
ImageList)));
152 m_imageReadingMutex =
new QMutex;
160 if (m_isTemporaryProject) {
164 int undoStackIndex = m_undoStack.index();
165 int undoStackCleanIndex = m_undoStack.cleanIndex();
167 int undoNeededToRestoreDiskState = undoStackIndex;
169 for (
int i = undoStackIndex - 1; i >= undoStackCleanIndex; i--) {
171 dynamic_cast<const WorkOrder *
>(m_undoStack.command(i));
172 if (currentWorkOrder && currentWorkOrder->modifiesDiskState()) {
173 undoNeededToRestoreDiskState = i;
177 if (undoNeededToRestoreDiskState != undoStackIndex) {
178 m_undoStack.setIndex(undoNeededToRestoreDiskState);
183 foreach (
ImageList *imageList, *m_images) {
184 foreach (
Image *image, *imageList) {
197 foreach (
Control *control, *controlList) {
207 delete m_idToControlMap;
208 m_idToControlMap = NULL;
210 delete m_idToImageMap;
211 m_idToImageMap = NULL;
215 delete m_projectRoot;
216 m_projectRoot = NULL;
221 delete m_imageReader;
226 delete m_workOrderHistory;
227 m_workOrderHistory = NULL;
236 if (!dir.mkpath(m_projectRoot->path())) {
237 warn(
"Cannot create project directory.");
239 tr(
"Unable to create folder [%1] when trying to initialize project")
240 .arg(m_projectRoot->path()),
246 QString msg = QString(
"Unable to create folder [%1] when trying to initialize project")
253 QString msg = QString(
"Unable to create folder [%1] when trying to initialize project")
260 warn(
"Failed to create project directory structure");
266 ImageList *Project::createOrRetrieveImageList(QString name) {
275 connect(result, SIGNAL(destroyed(
QObject *)),
278 m_images->append(result);
298 void Project::save(QXmlStreamWriter &stream,
FileName newProjectRoot)
const {
299 stream.writeStartElement(
"project");
301 stream.writeAttribute(
"name", m_name);
303 if (!m_controls->isEmpty()) {
304 stream.writeStartElement(
"controlNets");
306 for (
int i = 0; i < m_controls->count(); i++) {
307 m_controls->at(i)->save(stream,
this, newProjectRoot);
310 stream.writeEndElement();
313 if (!m_images->isEmpty()) {
314 stream.writeStartElement(
"imageLists");
316 for (
int i = 0; i < m_images->count(); i++) {
317 m_images->at(i)->save(stream,
this, newProjectRoot);
320 stream.writeEndElement();
323 stream.writeEndElement();
343 stream.writeStartElement(
"history");
345 foreach (
WorkOrder *workOrder, *m_workOrderHistory) {
347 workOrder->
save(stream);
351 stream.writeEndElement();
366 stream.writeStartElement(
"warnings");
368 foreach (QString warning, *m_warnings) {
369 stream.writeStartElement(
"warning");
370 stream.writeAttribute(
"text", warning);
371 stream.writeEndElement();
374 stream.writeEndElement();
389 foreach (QString fileName, fileNames) {
392 result.append(fileName);
420 int prefixCounter = 0;
422 QString numberedPrefix;
425 numberedPrefix = prefix.arg(QString::number(prefixCounter));
427 while (cnetFolder.exists(numberedPrefix));
429 if (!cnetFolder.mkpath(numberedPrefix)) {
431 tr(
"Could not create control network directory [%1] in [%2].")
432 .arg(numberedPrefix).arg(cnetFolder.absolutePath()),
436 cnetFolder.cd(numberedPrefix);
438 m_currentCnetFolder = cnetFolder;
452 connect(
this, SIGNAL(projectRelocated(
Project *)), control, SLOT(updateFileName(
Project *)));
454 createOrRetrieveControlList(
FileName(control->fileName()).dir().dirName())->append(control);
456 (*m_idToControlMap)[control->id()] = control;
458 emit controlAdded(control);
462 ControlList *Project::createOrRetrieveControlList(QString name) {
471 connect(result, SIGNAL(destroyed(
QObject *)),
474 emit controlListAdded(result);
475 m_controls->append(result);
490 int prefixCounter = 0;
492 QString numberedPrefix;
495 numberedPrefix = prefix.arg(QString::number(prefixCounter));
497 while (imageFolder.exists(numberedPrefix));
499 if (!imageFolder.mkpath(numberedPrefix)) {
501 tr(
"Could not create image directory [%1] in [%2].")
502 .arg(numberedPrefix).arg(imageFolder.absolutePath()),
506 imageFolder.cd(numberedPrefix);
516 if (m_numImagesCurrentlyReading == 0) {
517 m_imageReadingMutex->lock();
520 m_numImagesCurrentlyReading += imageFiles.count();
521 m_imageReader->read(imageFiles);
526 imagesReady(newImages);
531 Control *Project::control(QString
id) {
532 return (*m_idToControlMap)[id];
551 m_isTemporaryProject =
false;
553 FileName projectPath(projectPathStr);
557 reader.pushContentHandler(&handler);
558 reader.setErrorHandler(&handler);
560 QString projectXmlPath = projectPath.toString() +
"/project.xml";
561 QFile file(projectXmlPath);
563 if (!file.open(QFile::ReadOnly)) {
565 QString(
"Unable to open [%1] with read access")
566 .arg(projectXmlPath),
570 QDir oldProjectRoot(*m_projectRoot);
571 *m_projectRoot = projectPath.expanded();
573 QXmlInputSource xmlInputSource(&file);
574 if (!reader.parse(xmlInputSource)) {
575 warn(tr(
"Failed to open project [%1]").arg(projectPath.original()));
578 QString projectXmlHistoryPath = projectPath.toString() +
"/history.xml";
579 QFile historyFile(projectXmlHistoryPath);
581 if (!historyFile.open(QFile::ReadOnly)) {
583 QString(
"Unable to open [%1] with read access")
584 .arg(projectXmlHistoryPath),
588 reader.pushContentHandler(&handler);
589 QXmlInputSource xmlHistoryInputSource(&historyFile);
590 if (!reader.parse(xmlHistoryInputSource)) {
591 warn(tr(
"Failed to read history from project [%1]").arg(projectPath.original()));
594 QString projectXmlWarningsPath =
595 projectPath.toString() +
"/warnings.xml";
596 QFile warningsFile(projectXmlWarningsPath);
598 if (!warningsFile.open(QFile::ReadOnly)) {
600 QString(
"Unable to open [%1] with read access")
601 .arg(projectXmlWarningsPath),
605 reader.pushContentHandler(&handler);
606 QXmlInputSource xmlWarningsInputSource(&warningsFile);
607 if (!reader.parse(xmlWarningsInputSource)) {
608 warn(tr(
"Failed to read warnings from project [%1]").arg(projectPath.original()));
611 QString directoryXmlPath =
612 projectPath.toString() +
"/directory.xml";
613 QFile directoryFile(directoryXmlPath);
615 if (!directoryFile.open(QFile::ReadOnly)) {
617 QString(
"Unable to open [%1] with read access")
618 .arg(directoryXmlPath),
622 reader.pushContentHandler(&handler);
623 QXmlInputSource xmlDirectoryInputSource(&directoryFile);
624 if (!reader.parse(xmlDirectoryInputSource)) {
625 warn(tr(
"Failed to read GUI state from project [%1]").arg(projectPath.original()));
628 emit projectLoaded(
this);
633 return m_imageReader->progress();
637 Image *Project::image(QString
id) {
638 return (*m_idToImageMap)[id];
642 ImageList *Project::imageList(QString name) {
643 QListIterator<ImageList *> it(*m_images);
645 ImageList *result = NULL;
646 while (it.hasNext() && !result) {
647 ImageList *list = it.next();
649 if (list->name() ==
name)
657 bool Project::isTemporaryProject()
const {
658 return m_isTemporaryProject;
662 WorkOrder *Project::lastNotUndoneWorkOrder() {
663 WorkOrder *result = NULL;
664 QListIterator< QPointer<WorkOrder> > it(*m_workOrderHistory);
667 while (!result && it.hasPrevious()) {
668 WorkOrder *workOrder = it.previous();
670 if (!workOrder->isUndone() && !workOrder->isUndoing()) {
687 const WorkOrder *Project::lastNotUndoneWorkOrder()
const {
689 QListIterator< QPointer<WorkOrder> > it(*m_workOrderHistory);
692 while (!result && it.hasPrevious()) {
695 if (!workOrder->isUndone() && !workOrder->isUndoing()) {
720 return m_projectRoot->path();
729 emit nameChanged(m_name);
733 QUndoStack *Project::undoStack() {
738 QString Project::nextImageListGroupName() {
739 int numLists = m_images->size();
740 QString maxName =
"";
741 QString newGroupName =
"Group";
743 foreach (ImageList *imageList, *m_images) {
744 QString name = imageList->name();
745 if (!name.contains(
"Group"))
continue;
746 if (maxName.isEmpty()) {
749 else if (name > maxName) {
754 if (maxName.isEmpty()) {
755 newGroupName += QString::number(numLists+1);
758 int maxNum = maxName.remove(
"Group").toInt();
761 newGroupName += QString::number(maxNum);
775 void Project::waitForImageReaderFinished() {
776 QMutexLocker locker(m_imageReadingMutex);
785 foreach (
WorkOrder *workOrder, *m_workOrderHistory) {
786 result.append(workOrder);
799 return projectRoot +
"/cnets";
809 return cnetRoot(m_projectRoot->path());
819 ControlList *Project::controlList(QString name) {
820 QListIterator<ControlList *> it(*m_controls);
822 ControlList *result = NULL;
823 while (it.hasNext() && !result) {
824 ControlList *list = it.next();
826 if (list->name() ==
name)
840 return projectRoot +
"/images";
863 foreach (
ImageList *imagesInAFolder, *m_images) {
868 warn(tr(
"Did not properly clean up images folder [%1] in project").arg(
imageDataRoot()));
871 if (!m_projectRoot->rmdir(
cnetRoot())) {
872 warn(tr(
"Did not properly clean up control network folder [%1] in project")
876 if (!m_projectRoot->rmpath(m_projectRoot->path())) {
877 warn(tr(
"Did not properly clean up project in [%1]").arg(m_projectRoot->path()));
888 *m_projectRoot = newProjectRoot;
889 emit projectRelocated(
this);
893 void Project::save() {
894 if (m_isTemporaryProject) {
895 QString newDestination =
896 QFileDialog::getSaveFileName(NULL, QString(
"Project Location"), QString(
"."));
898 if (!newDestination.isEmpty()) {
899 save(QFileInfo(newDestination +
"/").absolutePath());
904 m_isTemporaryProject =
false;
908 save(m_projectRoot->absolutePath(),
false);
913 void Project::save(FileName newPath,
bool verifyPathDoesntExist) {
914 if (verifyPathDoesntExist && QFile::exists(newPath.toString())) {
916 QString(
"Projects may not be saved to an existing path [%1]; please select a new path or "
917 "delete the current folder")
918 .arg(newPath.original()),
923 if (!dir.mkpath(newPath.toString())) {
925 QString(
"Unable to save project at [%1] because we could not create the folder")
926 .arg(newPath.original()),
930 QFile projectSettingsFile(newPath.toString() +
"/project.xml");
931 if (!projectSettingsFile.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
933 QString(
"Unable to save project at [%1] because the file [%2] could not be opened for "
935 .arg(newPath.original()).arg(projectSettingsFile.fileName()),
940 QXmlStreamWriter writer(&projectSettingsFile);
941 writer.setAutoFormatting(
true);
943 writer.writeStartDocument();
946 save(writer, newPath);
948 writer.writeEndDocument();
950 QFile projectHistoryFile(newPath.toString() +
"/history.xml");
951 if (!projectHistoryFile.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
953 QString(
"Unable to save project at [%1] because the file [%2] could not be opened for "
955 .arg(newPath.original()).arg(projectHistoryFile.fileName()),
960 QXmlStreamWriter historyWriter(&projectHistoryFile);
961 historyWriter.setAutoFormatting(
true);
963 historyWriter.writeStartDocument();
965 historyWriter.writeEndDocument();
967 QFile projectWarningsFile(newPath.toString() +
"/warnings.xml");
968 if (!projectWarningsFile.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
970 QString(
"Unable to save project at [%1] because the file [%2] could not be "
971 "opened for writing")
972 .arg(newPath.original()).arg(projectWarningsFile.fileName()),
976 QXmlStreamWriter warningsWriter(&projectWarningsFile);
977 warningsWriter.setAutoFormatting(
true);
979 warningsWriter.writeStartDocument();
981 warningsWriter.writeEndDocument();
983 QFile directoryStateFile(newPath.toString() +
"/directory.xml");
984 if (!directoryStateFile.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
986 QString(
"Unable to save project at [%1] because the file [%2] could not be "
987 "opened for writing")
988 .arg(newPath.original()).arg(directoryStateFile.fileName()),
992 QXmlStreamWriter directoryStateWriter(&directoryStateFile);
993 directoryStateWriter.setAutoFormatting(
true);
995 directoryStateWriter.writeStartDocument();
996 m_directory->save(directoryStateWriter, newPath);
997 directoryStateWriter.writeEndDocument();
1015 connect(workOrder, SIGNAL(finished(
WorkOrder *)),
1016 this, SIGNAL(workOrderFinished(
WorkOrder *)));
1018 workOrder->setPrevious(lastNotUndoneWorkOrder());
1021 if (workOrder->previous())
1022 workOrder->previous()->setNext(workOrder);
1024 m_workOrderHistory->append(workOrder);
1026 emit workOrderStarting(workOrder);
1030 if (workOrder->createsCleanState()) {
1031 m_undoStack.setClean();
1035 m_undoStack.push(workOrder);
1039 m_workOrderHistory->removeAll(NULL);
1049 void Project::warn(QString text) {
1050 foreach (QString line, text.split(
"\n")) {
1057 void Project::storeWarning(QString text) {
1058 m_warnings->append(text);
1062 void Project::imagesReady(ImageList images) {
1063 m_numImagesCurrentlyReading -= images.count();
1065 foreach (Image *image, images) {
1066 connect(image, SIGNAL(destroyed(
QObject *)),
1068 connect(
this, SIGNAL(projectRelocated(
Project *)),
1069 image, SLOT(updateFileName(
Project *)));
1071 (*m_idToImageMap)[image->id()] = image;
1072 createOrRetrieveImageList(FileName(images[0]->fileName()).dir().dirName())->
append(image);
1079 QMutexLocker lock(m_mutex);
1080 emit imagesAdded(m_images->last());
1083 foreach (openImage, images) {
1084 openImage->closeCube();
1093 if (m_numImagesCurrentlyReading == 0) {
1094 m_imageReadingMutex->unlock();
1103 QMutableListIterator<ImageList *> it(*m_images);
1104 while (it.hasNext()) {
1107 int foundElement = list->indexOf((
Image *)imageObj);
1109 if (foundElement != -1) {
1114 m_idToImageMap->remove(m_idToImageMap->key((
Image *)imageObj));
1122 QMutableListIterator<ControlList *> it(*m_controls);
1123 while (it.hasNext()) {
1126 int foundElement = list->indexOf((
Control *)controlObj);
1128 if (foundElement != -1) {
1133 m_idToControlMap->remove(m_idToControlMap->key((
Control *)controlObj));
1141 int indexToRemove = m_controls->indexOf(static_cast<ControlList *>(controlListObj));
1142 if (indexToRemove != -1) {
1143 m_controls->removeAt(indexToRemove);
1152 int indexToRemove = m_images->indexOf(static_cast<ImageList *>(imageListObj));
1153 if (indexToRemove != -1) {
1154 m_images->removeAt(indexToRemove);
1159 Project::XmlHandler::XmlHandler(
Project *project) {
1160 m_project = project;
1165 bool Project::XmlHandler::startElement(
const QString &namespaceURI,
const QString &localName,
1166 const QString &qName,
const QXmlAttributes &atts) {
1167 if (XmlStackedHandler::startElement(namespaceURI, localName, qName, atts)) {
1169 if (localName ==
"project") {
1170 QString name = atts.value(
"name");
1172 if (!name.isEmpty()) {
1173 m_project->setName(name);
1177 else if (localName ==
"controlNets") {
1178 m_controls.append(
new ControlList(m_project, reader()));
1181 else if (localName ==
"imageList") {
1182 m_imageLists.append(
new ImageList(m_project, reader()));
1185 else if (localName ==
"workOrder") {
1186 QString type = atts.value(
"type");
1189 ASSERT(m_workOrder->metaObject()->className() == type);
1191 m_workOrder->read(reader());
1193 else if (localName ==
"warning") {
1194 QString warningText = atts.value(
"text");
1196 if (!warningText.isEmpty()) {
1197 m_project->warn(warningText);
1201 else if (localName ==
"directory") {
1202 m_project->directory()->load(reader());
1210 bool Project::XmlHandler::endElement(
const QString &namespaceURI,
const QString &localName,
1211 const QString &qName) {
1212 if (localName ==
"project") {
1213 foreach (ImageList *imageList, m_imageLists) {
1214 m_project->imagesReady(*imageList);
1217 else if (localName ==
"workOrder") {
1218 m_project->m_workOrderHistory->append(m_workOrder);
1221 else if (localName ==
"controlNets") {
1222 foreach (ControlList *list, m_controls) {
1223 foreach (Control *control, *list) {
1224 m_project->addControl(control);
1231 return XmlStackedHandler::endElement(namespaceURI, localName, qName);