USGS

Isis 3.0 Object Programmers' Reference

Home

Project.cpp
Go to the documentation of this file.
1 
24 #include "IsisDebug.h"
25 #include "Project.h"
26 
27 #include <unistd.h>
28 
29 #include <QApplication>
30 #include <QDir>
31 #include <QFileDialog>
32 #include <QFuture>
33 #include <QFutureWatcher>
34 #include <QMessageBox>
35 #include <QMutex>
36 #include <QMutexLocker>
37 #include <QProgressBar>
38 #include <QStringList>
39 #include <QtDebug>
40 #include <QXmlStreamWriter>
41 
42 #include "Control.h"
43 #include "ControlList.h"
44 #include "Cube.h"
45 #include "Directory.h"
46 #include "Environment.h"
47 #include "FileName.h"
48 #include "IException.h"
49 #include "ImageList.h"
50 #include "ImageReader.h"
51 #include "IException.h"
52 #include "ProgressBar.h"
53 #include "WorkOrder.h"
54 #include "WorkOrderFactory.h"
55 #include "XmlStackedHandlerReader.h"
56 
57 namespace Isis {
58 
62  Project::Project(Directory &directory, QObject *parent) :
63  QObject(parent) {
64 
65  m_directory = &directory;
66  m_projectRoot = NULL;
67  m_cnetRoot = NULL;
68  m_idToControlMap = NULL;
69  m_idToImageMap = NULL;
70  m_images = NULL;
71  m_imageReader = NULL;
72  m_warnings = NULL;
73  m_workOrderHistory = NULL;
74  m_isTemporaryProject = true;
75 
76  m_numImagesCurrentlyReading = 0;
77 
78  m_mutex = NULL;
79  m_imageReadingMutex = NULL;
80 
81  m_idToControlMap = new QMap<QString, Control *>;
82  m_idToImageMap = new QMap<QString, Image *>;
83 
84  m_name = "Project";
85 
86  // Look for old projects
87  QDir tempDir = QDir::temp();
88  QStringList nameFilters;
89  nameFilters.append(Environment::userName() + "_" +
90  QApplication::applicationName() + "_*");
91  tempDir.setNameFilters(nameFilters);
92 
93  QStringList existingProjects = tempDir.entryList();
94  bool crashedPreviously = false;
95 
96  foreach (QString existingProject, existingProjects) {
97  FileName existingProjectFileName(tempDir.absolutePath() + "/" + existingProject);
98  QString pidString = QString(existingProject).replace(QRegExp(".*_"), "");
99  int otherPid = pidString.toInt();
100 
101  if (otherPid != 0) {
102  if (!QFile::exists("/proc/" + pidString)) {
103  crashedPreviously = true;
104  int status = system( ("rm -rf '" + existingProjectFileName.expanded() + "' &").toAscii().data() );
105  if (status != 0) {
106  QString msg = "Executing command [rm -rf" + existingProjectFileName.expanded() +
107  "' &] failed with return status [" + toString(status) + "]";
109  }
110  }
111  }
112  }
113 
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()));
118  }
119 
120 
121  try {
122  QString tmpFolder = QDir::temp().absolutePath() + "/"
123  + Environment::userName() + "_"
124  + QApplication::applicationName() + "_" + QString::number(getpid());
125  QDir temp(tmpFolder + "/tmpProject");
126  m_projectRoot = new QDir(temp);
127 
128  createFolders();
129  }
130  catch (IException &e) {
131  throw IException(e, IException::Programmer, "Error creating project folders.", _FILEINFO_);
132  }
133  catch (std::exception &e) {
134  // e.what()
136  tr("Error creating project folders [%1]").arg(e.what()), _FILEINFO_);
137  }
138 
139  // image reader
140  m_mutex = new QMutex;
141 
142  m_imageReader = new ImageReader(m_mutex, false);
143 
144  connect(m_imageReader, SIGNAL(imagesReady(ImageList)), this, SLOT(imagesReady(ImageList)));
145 
146  m_images = new QList<ImageList *>;
147 
148  m_controls = new QList<ControlList *>;
149  m_warnings = new QStringList;
150  m_workOrderHistory = new QList< QPointer<WorkOrder> >;
151 
152  m_imageReadingMutex = new QMutex;
153  }
154 
155 
160  if (m_isTemporaryProject) {
162  }
163  else {
164  int undoStackIndex = m_undoStack.index();
165  int undoStackCleanIndex = m_undoStack.cleanIndex();
166 
167  int undoNeededToRestoreDiskState = undoStackIndex;
168 
169  for (int i = undoStackIndex - 1; i >= undoStackCleanIndex; i--) {
170  const WorkOrder *currentWorkOrder =
171  dynamic_cast<const WorkOrder *>(m_undoStack.command(i));
172  if (currentWorkOrder && currentWorkOrder->modifiesDiskState()) {
173  undoNeededToRestoreDiskState = i;
174  }
175  }
176 
177  if (undoNeededToRestoreDiskState != undoStackIndex) {
178  m_undoStack.setIndex(undoNeededToRestoreDiskState);
179  }
180  }
181 
182  if (m_images) {
183  foreach (ImageList *imageList, *m_images) {
184  foreach (Image *image, *imageList) {
185  delete image;
186  }
187 
188  delete imageList;
189  }
190 
191  delete m_images;
192  m_images = NULL;
193  }
194 
195  if (m_controls) {
196  foreach (ControlList *controlList, *m_controls) {
197  foreach (Control *control, *controlList) {
198  delete control;
199  }
200 
201  delete controlList;
202  }
203  delete m_controls;
204  m_controls = NULL;
205  }
206 
207  delete m_idToControlMap;
208  m_idToControlMap = NULL;
209 
210  delete m_idToImageMap;
211  m_idToImageMap = NULL;
212 
213  m_directory = NULL;
214 
215  delete m_projectRoot;
216  m_projectRoot = NULL;
217 
218  delete m_cnetRoot;
219  m_cnetRoot = NULL;
220 
221  delete m_imageReader;
222 
223  delete m_warnings;
224  m_warnings = NULL;
225 
226  delete m_workOrderHistory;
227  m_workOrderHistory = NULL;
228  }
229 
230 
235  QDir dir;
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()),
241  _FILEINFO_);
242  }
243 
244  try {
245  if (!dir.mkdir(cnetRoot())) {
246  QString msg = QString("Unable to create folder [%1] when trying to initialize project")
247  .arg(cnetRoot());
248  warn(msg);
249  throw IException(IException::Io, msg, _FILEINFO_);
250  }
251 
252  if (!dir.mkdir(imageDataRoot())) {
253  QString msg = QString("Unable to create folder [%1] when trying to initialize project")
254  .arg(imageDataRoot());
255  warn(msg);
256  throw IException(IException::Io, msg, _FILEINFO_);
257  }
258  }
259  catch (...) {
260  warn("Failed to create project directory structure");
261  throw;
262  }
263  }
264 
265 
266  ImageList *Project::createOrRetrieveImageList(QString name) {
267  ImageList *result = imageList(name);
268 
269  if (!result) {
270  result = new ImageList;
271 
272  result->setName(name);
273  result->setPath(name);
274 
275  connect(result, SIGNAL(destroyed(QObject *)),
276  this, SLOT(imageListDeleted(QObject *)));
277 
278  m_images->append(result);
279  }
280 
281  return result;
282  }
283 
284 
298  void Project::save(QXmlStreamWriter &stream, FileName newProjectRoot) const {
299  stream.writeStartElement("project");
300 
301  stream.writeAttribute("name", m_name);
302 
303  if (!m_controls->isEmpty()) {
304  stream.writeStartElement("controlNets");
305 
306  for (int i = 0; i < m_controls->count(); i++) {
307  m_controls->at(i)->save(stream, this, newProjectRoot);
308  }
309 
310  stream.writeEndElement();
311  }
312 
313  if (!m_images->isEmpty()) {
314  stream.writeStartElement("imageLists");
315 
316  for (int i = 0; i < m_images->count(); i++) {
317  m_images->at(i)->save(stream, this, newProjectRoot);
318  }
319 
320  stream.writeEndElement();
321  }
322 
323  stream.writeEndElement();
324  }
325 
326 
342  void Project::saveHistory(QXmlStreamWriter &stream) const {
343  stream.writeStartElement("history");
344 
345  foreach (WorkOrder *workOrder, *m_workOrderHistory) {
346  if (workOrder) {
347  workOrder->save(stream);
348  }
349  }
350 
351  stream.writeEndElement();
352  }
353 
365  void Project::saveWarnings(QXmlStreamWriter &stream) const {
366  stream.writeStartElement("warnings");
367 
368  foreach (QString warning, *m_warnings) {
369  stream.writeStartElement("warning");
370  stream.writeAttribute("text", warning);
371  stream.writeEndElement();
372  }
373 
374  stream.writeEndElement();
375  }
376 
377 
384  // TODO: thread via ImageReader
385  QStringList Project::images(QStringList fileNames) {
386 
387  QStringList result;
388 
389  foreach (QString fileName, fileNames) {
390  try {
391  Cube tmp(fileName);
392  result.append(fileName);
393  }
394  catch (IException &) {
395  }
396  }
397 
398  return result;
399  }
400 
401 
408  return m_imageReader->actions(ImageDisplayProperties::FootprintViewProperties);
409  }
410 
411 
417  QDir Project::addCnetFolder(QString prefix) {
418  QDir cnetFolder = cnetRoot();
419  prefix += "%1";
420  int prefixCounter = 0;
421 
422  QString numberedPrefix;
423  do {
424  prefixCounter++;
425  numberedPrefix = prefix.arg(QString::number(prefixCounter));
426  }
427  while (cnetFolder.exists(numberedPrefix));
428 
429  if (!cnetFolder.mkpath(numberedPrefix)) {
431  tr("Could not create control network directory [%1] in [%2].")
432  .arg(numberedPrefix).arg(cnetFolder.absolutePath()),
433  _FILEINFO_);
434  }
435 
436  cnetFolder.cd(numberedPrefix);
437 
438  m_currentCnetFolder = cnetFolder;
439 
440  return cnetFolder;
441  }
442 
443 
449  void Project::addControl(Control *control) {
450 
451  connect(control, SIGNAL(destroyed(QObject *)), this, SLOT(controlClosed(QObject *)));
452  connect(this, SIGNAL(projectRelocated(Project *)), control, SLOT(updateFileName(Project *)));
453 
454  createOrRetrieveControlList(FileName(control->fileName()).dir().dirName())->append(control);
455 
456  (*m_idToControlMap)[control->id()] = control;
457 
458  emit controlAdded(control);
459  }
460 
461 
462  ControlList *Project::createOrRetrieveControlList(QString name) {
463  ControlList *result = controlList(name);
464 
465  if (!result) {
466  result = new ControlList;
467 
468  result->setName(name);
469  result->setPath(name);
470 
471  connect(result, SIGNAL(destroyed(QObject *)),
472  this, SLOT(controlListDeleted(QObject *)));
473 
474  emit controlListAdded(result);
475  m_controls->append(result);
476  }
477 
478  return result;
479  }
480 
481 
487  QDir Project::addImageFolder(QString prefix) {
488  QDir imageFolder = imageDataRoot();
489  prefix += "%1";
490  int prefixCounter = 0;
491 
492  QString numberedPrefix;
493  do {
494  prefixCounter++;
495  numberedPrefix = prefix.arg(QString::number(prefixCounter));
496  }
497  while (imageFolder.exists(numberedPrefix));
498 
499  if (!imageFolder.mkpath(numberedPrefix)) {
501  tr("Could not create image directory [%1] in [%2].")
502  .arg(numberedPrefix).arg(imageFolder.absolutePath()),
503  _FILEINFO_);
504  }
505 
506  imageFolder.cd(numberedPrefix);
507 
508  return imageFolder;
509  }
510 
511 
515  void Project::addImages(QStringList imageFiles) {
516  if (m_numImagesCurrentlyReading == 0) {
517  m_imageReadingMutex->lock();
518  }
519 
520  m_numImagesCurrentlyReading += imageFiles.count();
521  m_imageReader->read(imageFiles);
522  }
523 
524 
525  void Project::addImages(ImageList newImages) {
526  imagesReady(newImages);
527  }
528 
529 
530 
531  Control *Project::control(QString id) {
532  return (*m_idToControlMap)[id];
533  }
534 
535 
543  return m_directory;
544  }
545 
546 
550  void Project::open(QString projectPathStr) {
551  m_isTemporaryProject = false;
552 
553  FileName projectPath(projectPathStr);
554  XmlHandler handler(this);
555 
557  reader.pushContentHandler(&handler);
558  reader.setErrorHandler(&handler);
559 
560  QString projectXmlPath = projectPath.toString() + "/project.xml";
561  QFile file(projectXmlPath);
562 
563  if (!file.open(QFile::ReadOnly)) {
565  QString("Unable to open [%1] with read access")
566  .arg(projectXmlPath),
567  _FILEINFO_);
568  }
569 
570  QDir oldProjectRoot(*m_projectRoot);
571  *m_projectRoot = projectPath.expanded();
572 
573  QXmlInputSource xmlInputSource(&file);
574  if (!reader.parse(xmlInputSource)) {
575  warn(tr("Failed to open project [%1]").arg(projectPath.original()));
576  }
577 
578  QString projectXmlHistoryPath = projectPath.toString() + "/history.xml";
579  QFile historyFile(projectXmlHistoryPath);
580 
581  if (!historyFile.open(QFile::ReadOnly)) {
583  QString("Unable to open [%1] with read access")
584  .arg(projectXmlHistoryPath),
585  _FILEINFO_);
586  }
587 
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()));
592  }
593 
594  QString projectXmlWarningsPath =
595  projectPath.toString() + "/warnings.xml";
596  QFile warningsFile(projectXmlWarningsPath);
597 
598  if (!warningsFile.open(QFile::ReadOnly)) {
600  QString("Unable to open [%1] with read access")
601  .arg(projectXmlWarningsPath),
602  _FILEINFO_);
603  }
604 
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()));
609  }
610 
611  QString directoryXmlPath =
612  projectPath.toString() + "/directory.xml";
613  QFile directoryFile(directoryXmlPath);
614 
615  if (!directoryFile.open(QFile::ReadOnly)) {
617  QString("Unable to open [%1] with read access")
618  .arg(directoryXmlPath),
619  _FILEINFO_);
620  }
621 
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()));
626  }
627 
628  emit projectLoaded(this);
629  }
630 
631 
632  QProgressBar *Project::progress() {
633  return m_imageReader->progress();
634  }
635 
636 
637  Image *Project::image(QString id) {
638  return (*m_idToImageMap)[id];
639  }
640 
641 
642  ImageList *Project::imageList(QString name) {
643  QListIterator<ImageList *> it(*m_images);
644 
645  ImageList *result = NULL;
646  while (it.hasNext() && !result) {
647  ImageList *list = it.next();
648 
649  if (list->name() == name)
650  result = list;
651  }
652 
653  return result;
654  }
655 
656 
657  bool Project::isTemporaryProject() const {
658  return m_isTemporaryProject;
659  }
660 
661 
662  WorkOrder *Project::lastNotUndoneWorkOrder() {
663  WorkOrder *result = NULL;
664  QListIterator< QPointer<WorkOrder> > it(*m_workOrderHistory);
665  it.toBack();
666 
667  while (!result && it.hasPrevious()) {
668  WorkOrder *workOrder = it.previous();
669 
670  if (!workOrder->isUndone() && !workOrder->isUndoing()) {
671  result = workOrder;
672  }
673  }
674 
675  return result;
676  }
677 
678 
682  QString Project::name() const {
683  return m_name;
684  }
685 
686 
687  const WorkOrder *Project::lastNotUndoneWorkOrder() const {
688  const WorkOrder *result = NULL;
689  QListIterator< QPointer<WorkOrder> > it(*m_workOrderHistory);
690  it.toBack();
691 
692  while (!result && it.hasPrevious()) {
693  WorkOrder *workOrder = it.previous();
694 
695  if (!workOrder->isUndone() && !workOrder->isUndoing()) {
696  result = workOrder;
697  }
698  }
699 
700  return result;
701  }
702 
703 
711  QMutex *Project::mutex() {
712  return m_mutex;
713  }
714 
715 
719  QString Project::projectRoot() const {
720  return m_projectRoot->path();
721  }
722 
723 
727  void Project::setName(QString newName) {
728  m_name = newName;
729  emit nameChanged(m_name);
730  }
731 
732 
733  QUndoStack *Project::undoStack() {
734  return &m_undoStack;
735  }
736 
737 
738  QString Project::nextImageListGroupName() {
739  int numLists = m_images->size();
740  QString maxName = "";
741  QString newGroupName = "Group";
742 
743  foreach (ImageList *imageList, *m_images) {
744  QString name = imageList->name();
745  if (!name.contains("Group")) continue;
746  if (maxName.isEmpty()) {
747  maxName = name;
748  }
749  else if (name > maxName) {
750  maxName = name;
751  }
752  }
753 
754  if (maxName.isEmpty()) {
755  newGroupName += QString::number(numLists+1);
756  }
757  else {
758  int maxNum = maxName.remove("Group").toInt();
759  maxNum++;
760 
761  newGroupName += QString::number(maxNum);
762  }
763  return newGroupName;
764 
765  }
766 
767 
768 // void Project::removeImages(ImageList &imageList) {
769 // foreach (Image *image, imageList) {
770 // removeImage(image);
771 // }
772 // }
773 
774 
775  void Project::waitForImageReaderFinished() {
776  QMutexLocker locker(m_imageReadingMutex);
777  }
778 
779 
784  QList<WorkOrder *> result;
785  foreach (WorkOrder *workOrder, *m_workOrderHistory) {
786  result.append(workOrder);
787  }
788 
789  return result;
790  }
791 
792 
798  QString Project::cnetRoot(QString projectRoot) {
799  return projectRoot + "/cnets";
800  }
801 
802 
808  QString Project::cnetRoot() const {
809  return cnetRoot(m_projectRoot->path());
810  }
811 
812 
813  QList<ControlList *> Project::controls() {
814 
815  return *m_controls;
816  }
817 
818 
819  ControlList *Project::controlList(QString name) {
820  QListIterator<ControlList *> it(*m_controls);
821 
822  ControlList *result = NULL;
823  while (it.hasNext() && !result) {
824  ControlList *list = it.next();
825 
826  if (list->name() == name)
827  result = list;
828  }
829 
830  return result;
831  }
832 
833 
839  QString Project::imageDataRoot(QString projectRoot) {
840  return projectRoot + "/images";
841  }
842 
843 
849  QString Project::imageDataRoot() const {
850  return imageDataRoot(m_projectRoot->path());
851  }
852 
853 
854  QList<ImageList *> Project::images() {
855  return *m_images;
856  }
857 
858 
863  foreach (ImageList *imagesInAFolder, *m_images) {
864  imagesInAFolder->deleteFromDisk(this);
865  }
866 
867  if (!m_projectRoot->rmdir(imageDataRoot())) {
868  warn(tr("Did not properly clean up images folder [%1] in project").arg(imageDataRoot()));
869  }
870 
871  if (!m_projectRoot->rmdir(cnetRoot())) {
872  warn(tr("Did not properly clean up control network folder [%1] in project")
873  .arg(cnetRoot()));
874  }
875 
876  if (!m_projectRoot->rmpath(m_projectRoot->path())) {
877  warn(tr("Did not properly clean up project in [%1]").arg(m_projectRoot->path()));
878  }
879  }
880 
881 
887  void Project::relocateProjectRoot(QString newProjectRoot) {
888  *m_projectRoot = newProjectRoot;
889  emit projectRelocated(this);
890  }
891 
892 
893  void Project::save() {
894  if (m_isTemporaryProject) {
895  QString newDestination =
896  QFileDialog::getSaveFileName(NULL, QString("Project Location"), QString("."));
897 
898  if (!newDestination.isEmpty()) {
899  save(QFileInfo(newDestination + "/").absolutePath());
900 
901  // delete the temporary project
903  relocateProjectRoot(newDestination);
904  m_isTemporaryProject = false;
905  }
906  }
907  else {
908  save(m_projectRoot->absolutePath(), false);
909  }
910  }
911 
912 
913  void Project::save(FileName newPath, bool verifyPathDoesntExist) {
914  if (verifyPathDoesntExist && QFile::exists(newPath.toString())) {
915  throw IException(IException::Io,
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()),
919  _FILEINFO_);
920  }
921 
922  QDir dir;
923  if (!dir.mkpath(newPath.toString())) {
924  throw IException(IException::Io,
925  QString("Unable to save project at [%1] because we could not create the folder")
926  .arg(newPath.original()),
927  _FILEINFO_);
928  }
929 
930  QFile projectSettingsFile(newPath.toString() + "/project.xml");
931  if (!projectSettingsFile.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
932  throw IException(IException::Io,
933  QString("Unable to save project at [%1] because the file [%2] could not be opened for "
934  "writing")
935  .arg(newPath.original()).arg(projectSettingsFile.fileName()),
936  _FILEINFO_);
937  }
938 
939 
940  QXmlStreamWriter writer(&projectSettingsFile);
941  writer.setAutoFormatting(true);
942 
943  writer.writeStartDocument();
944 
945  // Do amazing, fantastical stuff here!!!
946  save(writer, newPath);
947 
948  writer.writeEndDocument();
949 
950  QFile projectHistoryFile(newPath.toString() + "/history.xml");
951  if (!projectHistoryFile.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
952  throw IException(IException::Io,
953  QString("Unable to save project at [%1] because the file [%2] could not be opened for "
954  "writing")
955  .arg(newPath.original()).arg(projectHistoryFile.fileName()),
956  _FILEINFO_);
957  }
958 
959 
960  QXmlStreamWriter historyWriter(&projectHistoryFile);
961  historyWriter.setAutoFormatting(true);
962 
963  historyWriter.writeStartDocument();
964  saveHistory(historyWriter);
965  historyWriter.writeEndDocument();
966 
967  QFile projectWarningsFile(newPath.toString() + "/warnings.xml");
968  if (!projectWarningsFile.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
969  throw IException(IException::Io,
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()),
973  _FILEINFO_);
974  }
975 
976  QXmlStreamWriter warningsWriter(&projectWarningsFile);
977  warningsWriter.setAutoFormatting(true);
978 
979  warningsWriter.writeStartDocument();
980  saveWarnings(warningsWriter);
981  warningsWriter.writeEndDocument();
982 
983  QFile directoryStateFile(newPath.toString() + "/directory.xml");
984  if (!directoryStateFile.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
985  throw IException(IException::Io,
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()),
989  _FILEINFO_);
990  }
991 
992  QXmlStreamWriter directoryStateWriter(&directoryStateFile);
993  directoryStateWriter.setAutoFormatting(true);
994 
995  directoryStateWriter.writeStartDocument();
996  m_directory->save(directoryStateWriter, newPath);
997  directoryStateWriter.writeEndDocument();
998  }
999 
1000 
1014  if (workOrder) {
1015  connect(workOrder, SIGNAL(finished(WorkOrder *)),
1016  this, SIGNAL(workOrderFinished(WorkOrder *)));
1017 
1018  workOrder->setPrevious(lastNotUndoneWorkOrder());
1019 
1020  if (workOrder->execute()) {
1021  if (workOrder->previous())
1022  workOrder->previous()->setNext(workOrder);
1023 
1024  m_workOrderHistory->append(workOrder);
1025 
1026  emit workOrderStarting(workOrder);
1027 
1028  // Work orders that create clean states (save, save as) don't belong on the undo stack.
1029  // Instead, we tell the undo stack that we're now clean.
1030  if (workOrder->createsCleanState()) {
1031  m_undoStack.setClean();
1032  }
1033  else {
1034  // All other work orders go onto the undo stack
1035  m_undoStack.push(workOrder); // This calls redo for us
1036  }
1037 
1038  // Clean up deleted work orders (the m_undoStack.push() can delete work orders)
1039  m_workOrderHistory->removeAll(NULL);
1040  }
1041  else {
1042  delete workOrder;
1043  workOrder = NULL;
1044  }
1045  }
1046  }
1047 
1048 
1049  void Project::warn(QString text) {
1050  foreach (QString line, text.split("\n")) {
1051  storeWarning(line);
1052  directory()->showWarning(line);
1053  }
1054  }
1055 
1056 
1057  void Project::storeWarning(QString text) {
1058  m_warnings->append(text);
1059  }
1060 
1061 
1062  void Project::imagesReady(ImageList images) {
1063  m_numImagesCurrentlyReading -= images.count();
1064 
1065  foreach (Image *image, images) {
1066  connect(image, SIGNAL(destroyed(QObject *)),
1067  this, SLOT(imageClosed(QObject *)));
1068  connect(this, SIGNAL(projectRelocated(Project *)),
1069  image, SLOT(updateFileName(Project *)));
1070 
1071  (*m_idToImageMap)[image->id()] = image;
1072  createOrRetrieveImageList(FileName(images[0]->fileName()).dir().dirName())->append(image);
1073  }
1074 
1075  // We really can't have all of the cubes in memory before
1076  // the OS stops letting us open more files.
1077  // Assume cameras are being used in other parts of code since it's
1078  // unknown
1079  QMutexLocker lock(m_mutex);
1080  emit imagesAdded(m_images->last());
1081 
1082  Image *openImage;
1083  foreach (openImage, images) {
1084  openImage->closeCube();
1085  }
1086 
1087 // if(m_projectPvl && m_projectPvl->HasObject("MosaicFileList"))
1088 // m_fileList->fromPvl(m_projectPvl->FindObject("MosaicFileList"));
1089 
1090 // if(m_projectPvl && m_projectPvl->HasObject("MosaicScene"))
1091 // m_scene->fromPvl(m_projectPvl->FindObject("MosaicScene"));
1092 
1093  if (m_numImagesCurrentlyReading == 0) {
1094  m_imageReadingMutex->unlock();
1095  }
1096  }
1097 
1098 
1102  void Project::imageClosed(QObject * imageObj) {
1103  QMutableListIterator<ImageList *> it(*m_images);
1104  while (it.hasNext()) {
1105  ImageList *list = it.next();
1106 
1107  int foundElement = list->indexOf((Image *)imageObj);
1108 
1109  if (foundElement != -1) {
1110  list->removeAt(foundElement);
1111  }
1112  }
1113 
1114  m_idToImageMap->remove(m_idToImageMap->key((Image *)imageObj));
1115  }
1116 
1117 
1121  void Project::controlClosed(QObject *controlObj) {
1122  QMutableListIterator<ControlList *> it(*m_controls);
1123  while (it.hasNext()) {
1124  ControlList *list = it.next();
1125 
1126  int foundElement = list->indexOf((Control *)controlObj);
1127 
1128  if (foundElement != -1) {
1129  list->removeAt(foundElement);
1130  }
1131  }
1132 
1133  m_idToControlMap->remove(m_idToControlMap->key((Control *)controlObj));
1134  }
1135 
1136 
1140  void Project::controlListDeleted(QObject *controlListObj) {
1141  int indexToRemove = m_controls->indexOf(static_cast<ControlList *>(controlListObj));
1142  if (indexToRemove != -1) {
1143  m_controls->removeAt(indexToRemove);
1144  }
1145  }
1146 
1147 
1151  void Project::imageListDeleted(QObject *imageListObj) {
1152  int indexToRemove = m_images->indexOf(static_cast<ImageList *>(imageListObj));
1153  if (indexToRemove != -1) {
1154  m_images->removeAt(indexToRemove);
1155  }
1156  }
1157 
1158 
1159  Project::XmlHandler::XmlHandler(Project *project) {
1160  m_project = project;
1161  m_workOrder = NULL;
1162  }
1163 
1164 
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)) {
1168 
1169  if (localName == "project") {
1170  QString name = atts.value("name");
1171 
1172  if (!name.isEmpty()) {
1173  m_project->setName(name);
1174  }
1175  }
1176 
1177  else if (localName == "controlNets") {
1178  m_controls.append(new ControlList(m_project, reader()));
1179  }
1180 
1181  else if (localName == "imageList") {
1182  m_imageLists.append(new ImageList(m_project, reader()));
1183  }
1184 
1185  else if (localName == "workOrder") {
1186  QString type = atts.value("type");
1187 
1188  m_workOrder = WorkOrderFactory::create(m_project, type);
1189  ASSERT(m_workOrder->metaObject()->className() == type);
1190 
1191  m_workOrder->read(reader());
1192  }
1193  else if (localName == "warning") {
1194  QString warningText = atts.value("text");
1195 
1196  if (!warningText.isEmpty()) {
1197  m_project->warn(warningText);
1198  }
1199  }
1200 
1201  else if (localName == "directory") {
1202  m_project->directory()->load(reader());
1203  }
1204  }
1205 
1206  return true;
1207  }
1208 
1209 
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);
1215  }
1216  }
1217  else if (localName == "workOrder") {
1218  m_project->m_workOrderHistory->append(m_workOrder);
1219  m_workOrder = NULL;
1220  }
1221  else if (localName == "controlNets") {
1222  foreach (ControlList *list, m_controls) {
1223  foreach (Control *control, *list) {
1224  m_project->addControl(control);
1225  }
1226  delete list;
1227  }
1228  m_controls.clear();
1229  }
1230 
1231  return XmlStackedHandler::endElement(namespaceURI, localName, qName);
1232  }
1233 }