Engauge Digitizer  2
 All Classes Functions Variables Typedefs Enumerations Friends Pages
MainWindow.cpp
1 /******************************************************************************************************
2  * (C) 2014 markummitchell@github.com. This file is part of Engauge Digitizer, which is released *
3  * under GNU General Public License version 2 (GPLv2) or (at your option) any later version. See file *
4  * LICENSE or go to gnu.org/licenses for details. Distribution requires prior written permission. *
5  ******************************************************************************************************/
6 
7 #include "BackgroundImage.h"
8 #include "BackgroundStateContext.h"
9 #include "ChecklistGuide.h"
10 #include "ChecklistGuideWizard.h"
11 #include "CmdAddPointsGraph.h"
12 #include "CmdCopy.h"
13 #include "CmdCut.h"
14 #include "CmdDelete.h"
15 #include "CmdMediator.h"
16 #include "CmdSelectCoordSystem.h"
17 #include "CmdStackShadow.h"
18 #include "ColorFilter.h"
19 #include "CreateFacade.h"
20 #include "Curve.h"
21 #include "DataKey.h"
22 #include "DigitizeStateContext.h"
23 #include "DlgAbout.h"
24 #include "DlgErrorReportLocal.h"
25 #include "DlgImportAdvanced.h"
26 #include "DlgRequiresTransform.h"
27 #include "DlgSettingsAxesChecker.h"
28 #include "DlgSettingsColorFilter.h"
29 #include "DlgSettingsCoords.h"
30 #include "DlgSettingsCurveList.h"
31 #include "DlgSettingsCurveProperties.h"
32 #include "DlgSettingsDigitizeCurve.h"
33 #include "DlgSettingsExportFormat.h"
34 #include "DlgSettingsGeneral.h"
35 #include "DlgSettingsGridDisplay.h"
36 #include "DlgSettingsGridRemoval.h"
37 #include "DlgSettingsMainWindow.h"
38 #include "DlgSettingsPointMatch.h"
39 #include "DlgSettingsSegments.h"
40 #include "DocumentScrub.h"
41 #include "DocumentSerialize.h"
42 #include "EngaugeAssert.h"
43 #include "EnumsToQt.h"
44 #include "ExportImageForRegression.h"
45 #include "ExportToFile.h"
46 #include "FileCmdScript.h"
47 #include "FittingCurve.h"
48 #include "FittingWindow.h"
49 #include "GeometryWindow.h"
50 #include "Ghosts.h"
51 #include "GraphicsItemsExtractor.h"
52 #include "GraphicsItemType.h"
53 #include "GraphicsScene.h"
54 #include "GraphicsView.h"
55 #include "GridLineFactory.h"
56 #include "GridLineLimiter.h"
57 #if !defined(OSX_DEBUG) && !defined(OSX_RELEASE)
58 #include "HelpWindow.h"
59 #endif
60 #include "ImportImageExtensions.h"
61 #ifdef ENGAUGE_JPEG2000
62 #include "Jpeg2000.h"
63 #endif // ENGAUGE_JPEG2000
64 #include "LoadFileInfo.h"
65 #include "LoadImageFromUrl.h"
66 #include "Logger.h"
67 #include "MainDirectoryPersist.h"
68 #include "MainTitleBarFormat.h"
69 #include "MainWindow.h"
70 #include "MimePointsImport.h"
71 #ifdef NETWORKING
72 #include "NetworkClient.h"
73 #endif
74 #include "NonPdf.h"
75 #ifdef ENGAUGE_PDF
76 #include "Pdf.h"
77 #endif // ENGAUGE_PDF
78 #include "PdfResolution.h"
79 #include <QAction>
80 #include <QApplication>
81 #include <QClipboard>
82 #include <QCloseEvent>
83 #include <QComboBox>
84 #include <QDebug>
85 #include <QDesktopServices>
86 #include <QDockWidget>
87 #include <QDomDocument>
88 #include <QFileDialog>
89 #include <QFileInfo>
90 #include <QImageReader>
91 #include <QKeyEvent>
92 #include <QKeySequence>
93 #include <qmath.h>
94 #include <QMessageBox>
95 #include <QMouseEvent>
96 #include <QPrintDialog>
97 #include <QPrinter>
98 #include <QProcess>
99 #include <QPushButton>
100 #include <QSettings>
101 #include <QSignalMapper>
102 #include <QTextStream>
103 #if !defined(OSX_DEBUG) && !defined(OSX_RELEASE)
104 #include <QtHelp>
105 #endif
106 #include <QTimer>
107 #include <QToolBar>
108 #include <QToolButton>
109 #include "QtToString.h"
110 #include <QVBoxLayout>
111 #include <QWhatsThis>
112 #include <QXmlStreamReader>
113 #include <QXmlStreamWriter>
114 #include "ScaleBarAxisPointsUnite.h"
115 #include "Settings.h"
116 #include "StatusBar.h"
117 #include "TransformationStateContext.h"
118 #include "TutorialDlg.h"
119 #include "Version.h"
120 #include "ViewPointStyle.h"
121 #include "ViewSegmentFilter.h"
122 #include "ZoomFactor.h"
123 #include "ZoomFactorInitial.h"
124 #include "ZoomTransition.h"
125 
126 const QString EMPTY_FILENAME ("");
127 static const char *ENGAUGE_FILENAME_DESCRIPTION = "Engauge Document";
128 const QString ENGAUGE_FILENAME_EXTENSION ("dig");
129 const int REGRESSION_INTERVAL = 400; // Milliseconds
130 const unsigned int MAX_RECENT_FILE_LIST_SIZE = 8;
131 
132 MainWindow::MainWindow(const QString &errorReportFile,
133  const QString &fileCmdScriptFile,
134  bool isDropRegression,
135  bool isRegressionTest,
136  bool isGnuplot,
137  bool isReset,
138  bool isExportOnly,
139  bool isExtractImageOnly,
140  const QString &extractImageOnlyExtension,
141  const QStringList &loadStartupFiles,
142  const QStringList &commandLineWithoutLoadStartupFiles,
143  QWidget *parent) :
144  QMainWindow(parent),
145  m_originalFileWasImported (false),
146  m_isDocumentExported (false),
147  m_engaugeFile (EMPTY_FILENAME),
148  m_currentFile (EMPTY_FILENAME),
149  m_layout (nullptr),
150  m_scene (nullptr),
151  m_view (nullptr),
152  m_loadImageFromUrl (nullptr),
153  m_cmdMediator (nullptr),
154  m_digitizeStateContext (nullptr),
155  m_transformationStateContext (nullptr),
156  m_backgroundStateContext (nullptr),
157  m_networkClient (nullptr),
158  m_isGnuplot (isGnuplot),
159  m_commandLineWithoutLoadStartupFiles (commandLineWithoutLoadStartupFiles),
160  m_ghosts (nullptr),
161  m_timerRegressionErrorReport(nullptr),
162  m_fileCmdScript (nullptr),
163  m_isErrorReportRegressionTest (isRegressionTest),
164  m_timerRegressionFileCmdScript(nullptr),
165  m_fittingCurve (nullptr),
166  m_isExportOnly (isExportOnly),
167  m_isExtractImageOnly (isExtractImageOnly),
168  m_extractImageOnlyExtension (extractImageOnlyExtension)
169 {
170  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::MainWindow"
171  << " curDir=" << QDir::currentPath().toLatin1().data();
172 
173 #if defined(OSX_DEBUG) || defined(OSX_RELEASE)
174  qApp->setApplicationName ("Engauge Digitizer");
175  qApp->setOrganizationDomain ("Mark Mitchell");
176 #endif
177 
179 
180  m_startupDirectory = QDir::currentPath();
181 
182  setCurrentFile ("");
183 
184  CreateFacade createFacade;
185  createFacade.create (*this);
186 
187  updateControls ();
188 
189  settingsRead (isReset); // This changes the current directory when not regression testing
190  setCurrentFile ("");
191  setUnifiedTitleAndToolBarOnMac(true);
192 
193  installEventFilter(this);
194 
195  // Start regression scripting if appropriate. Regression scripts assume current directory is the original
196  // current directory, so we temporarily reset the current directory
197  QString originalPath = QDir::currentPath();
198  QDir::setCurrent (m_startupDirectory);
199  if (isExportOnly) {
200  m_loadStartupFiles = loadStartupFiles;
201  m_regressionFile = exportRegressionFilenameFromInputFilename (loadStartupFiles.first ()); // For regression test
202  slotLoadStartupFiles ();
203  slotFileExport (); // Export one file. QProcess::startDetached will be called for each remaining file
204  exit (0);
205  } else if (isExtractImageOnly) {
206  m_loadStartupFiles = loadStartupFiles;
207  m_regressionFile = exportRegressionFilenameFromInputFilename (loadStartupFiles.first ()); // For regression test
208  slotLoadStartupFiles ();
209  handlerFileExtractImage (); // Extract one file. QProcess::startDetached will be called for each remaining file
210  exit (0);
211  } else if (!errorReportFile.isEmpty()) {
212  loadErrorReportFile(errorReportFile);
213  if (m_isErrorReportRegressionTest) {
214  startRegressionTestErrorReport(errorReportFile);
215  }
216  } else if (!fileCmdScriptFile.isEmpty()) {
217  m_fileCmdScript = new FileCmdScript (fileCmdScriptFile);
218  startRegressionTestFileCmdScript();
219  } else if (isDropRegression) {
220  m_fileCmdScript = new FileCmdScript (""); // Hack to keep dialogs from popping up
221  startRegressionDropTest (loadStartupFiles);
222  } else {
223 
224  // Save file names for later, after gui becomes available. The file names are dropped if error report file is specified
225  // since only one of the two modes is available at any time, for simplicity
226  m_loadStartupFiles = loadStartupFiles;
227  }
228  QDir::setCurrent (originalPath);
229 }
230 
231 MainWindow::~MainWindow()
232 {
233 #if !defined(OSX_DEBUG) && !defined(OSX_RELEASE)
234  delete m_helpWindow;
235 #endif
236  delete m_tutorialDlg;
237  delete m_cmdMediator;
238  delete m_cmdStackShadow;
239  delete m_digitizeStateContext;
240  delete m_transformationStateContext;
241  delete m_backgroundStateContext;
242  delete m_dlgSettingsAxesChecker;
243  delete m_dlgSettingsColorFilter;
244  delete m_dlgSettingsCoords;
245  delete m_dlgSettingsCurveList;
246  delete m_dlgSettingsCurveProperties;
247  delete m_dlgSettingsDigitizeCurve;
248  delete m_dlgSettingsExportFormat;
249  delete m_dlgSettingsGeneral;
250  delete m_dlgSettingsGridDisplay;
251  delete m_dlgSettingsGridRemoval;
252  delete m_dlgSettingsMainWindow;
253  delete m_dlgSettingsPointMatch;
254  delete m_dlgSettingsSegments;
255  delete m_fileCmdScript;
256  m_gridLines.clear ();
257 }
258 
259 void MainWindow::addDockWindow (QDockWidget *dockWidget,
260  QSettings &settings,
261  const QString &settingsTokenArea,
262  const QString &settingsTokenGeometry,
263  Qt::DockWidgetArea dockWidgetArea)
264 {
265  // Checklist guide is docked or undocked. Default is docked so it does not get overlooked by the user (which
266  // can happen if it opens elsewhere). The user may not know it can be undocked, but at least can resize or
267  // hide it if he/she needs more room for the main window.
268  const bool DOCKED_EQUALS_NOT_FLOATING = false;
269  Qt::DockWidgetArea area = static_cast<Qt::DockWidgetArea> (settings.value (settingsTokenArea,
270  Qt::NoDockWidgetArea).toInt());
271 
272  if (area == Qt::NoDockWidgetArea) {
273 
274  addDockWidget (dockWidgetArea,
275  dockWidget); // Add on the right to prevent error message, then immediately make undocked
276  dockWidget->setFloating(DOCKED_EQUALS_NOT_FLOATING);
277  if (settings.contains (settingsTokenGeometry)) {
278  dockWidget->restoreGeometry (settings.value (settingsTokenGeometry).toByteArray());
279  }
280 
281  } else {
282 
283  addDockWidget (area,
284  dockWidget);
285 
286  }
287 }
288 
289 void MainWindow::applyZoomFactorAfterLoad()
290 {
291  ZoomFactor zoomFactor;
292  ZoomFactorInitial zoomFactorInitial = m_modelMainWindow.zoomFactorInitial();
293 
294  if (m_zoomMapFromInitial.contains (zoomFactorInitial)) {
295  zoomFactor = m_zoomMapFromInitial [zoomFactorInitial];
296  } else if (zoomFactorInitial == ZOOM_INITIAL_PREVIOUS) {
297  zoomFactor = currentZoomFactor ();
298  } else {
299  ENGAUGE_ASSERT (false);
300  zoomFactor = currentZoomFactor();
301  }
302 
303  slotViewZoom (zoomFactor);
304 }
305 
306 void MainWindow::closeEvent(QCloseEvent *event)
307 {
308  if (maybeSave()) {
309  settingsWrite ();
310  event->accept ();
311  } else {
312  event->ignore ();
313  }
314 }
315 
317 {
318  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::cmdFileClose";
319 
320  setWindowModified (false); // Prevent popup query asking if changes should be saved
321  slotFileClose();
322 }
323 
324 void MainWindow::cmdFileExport(const QString &fileName)
325 {
326  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::cmdFileExport";
327 
328  ExportToFile exportStrategy;
329  fileExport(fileName,
330  exportStrategy);
331 }
332 
333 void MainWindow::cmdFileImport(const QString &fileName)
334 {
335  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::cmdFileImport";
336 
337  m_regressionFile = exportRegressionFilenameFromInputFilename (fileName);
338  fileImport (fileName,
339  IMPORT_TYPE_SIMPLE);
340 }
341 
342 void MainWindow::cmdFileOpen(const QString &fileName)
343 {
344  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::cmdFileOpen";
345 
346  m_regressionFile = exportRegressionFilenameFromInputFilename (fileName);
347  loadDocumentFile(fileName);
348 }
349 
351 {
352  // We do not check m_cmdMediator with ENGAUGE_CHECK_PTR since calling code is expected to deal with null pointer at startup
353  return m_cmdMediator;
354 }
355 
356 ZoomFactor MainWindow::currentZoomFactor () const
357 {
358  // Find the zoom control that is checked
359  for (int z = 0; z < NUMBER_ZOOM_FACTORS; z++) {
360  ZoomFactor zoomFactor = static_cast<ZoomFactor> (z);
361  if (m_zoomMapToAction [zoomFactor]->isChecked ()) {
362  // This zoom control is checked
363  return zoomFactor;
364  }
365  }
366 
367  ENGAUGE_ASSERT (false);
368  return ZOOM_1_TO_1;
369 }
370 
371 bool MainWindow::eventFilter(QObject *target, QEvent *event)
372 {
373  if (event->type () == QEvent::KeyPress) {
374 
375  QKeyEvent *eventKeyPress = static_cast<QKeyEvent *> (event);
376 
377  // Special shortcuts. All of these are probably only useful for debugging and/or regression testing
378  if ((eventKeyPress->key() == Qt::Key_E) &&
379  ((eventKeyPress->modifiers() & Qt::ShiftModifier) != 0) &&
380  ((eventKeyPress->modifiers() & Qt::ControlModifier) != 0)) {
381 
382  saveErrorReportFileAndExit ("Shift+Control+E",
383  __FILE__,
384  __LINE__,
385  "userTriggered");
386 
387  }
388  }
389 
390  return QObject::eventFilter (target, event);
391 }
392 
393 #if !defined(OSX_DEBUG) && !defined(OSX_RELEASE)
394 void MainWindow::exportAllCoordinateSystemsAfterRegressionTests()
395 {
396  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::exportAllCoordinateSystemsAfterRegressionTests curDir=" << QDir::currentPath().toLatin1().data();
397 
398  // Output the regression test results. One file is output for every coordinate system
399  for (CoordSystemIndex index = 0; index < m_cmdMediator->document().coordSystemCount(); index++) {
400 
401  updateCoordSystem (index); // Switch to the specified coordinate system
402 
403  QString regressionFile = QString ("%1_%2")
404  .arg (m_regressionFile)
405  .arg (index + 1); // Append the coordinate system index
406 
407  // Normally we just export to a file, but when regression testing the export will fail since coordinates are not defined. To
408  // get an export file when regression testing, we just output the image size
409  if (m_isErrorReportRegressionTest && !m_transformation.transformIsDefined()) {
410 
411  ExportImageForRegression exportStrategy (m_cmdMediator->pixmap ());
412  exportStrategy.fileExport (regressionFile);
413 
414  } else {
415 
416  ExportToFile exportStrategy;
417 
418  fileExport (regressionFile,
419  exportStrategy);
420  }
421  }
422 }
423 #endif
424 
425 QString MainWindow::exportRegressionFilenameFromInputFilename (const QString &fileName) const
426 {
427  // Include file extensions used in loading, importing or drag and drop. Note html is before htm
428  // so the "l" gets replaced
429  QStringList befores;
430  befores << ".dig" << ".gif" << ".html" << ".htm" << ".jp2" << ".jpg" << ".pbm"
431  << ".pdf" << ".pgm" << ".png" << ".ppm" << ".xbm" << ".xpm" << ".xml";
432 
433  QString outFileName = fileName;
434 
435  QStringList::iterator itr;
436  for (itr = befores.begin(); itr != befores.end(); itr++) {
437  QString suffix = *itr;
438 
439  outFileName = outFileName.replace (suffix, ".csv_actual", Qt::CaseInsensitive);
440  }
441 
442  return outFileName;
443 }
444 
445 void MainWindow::fileExport(const QString &fileName,
446  ExportToFile exportStrategy)
447 {
448  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::fileExport"
449  << " curDir=" << QDir::currentPath().toLatin1().data()
450  << " fileName=" << fileName.toLatin1().data();
451 
452  QFile file (fileName);
453  if (file.open(QIODevice::WriteOnly)) {
454 
455  QTextStream str (&file);
456 
457  DocumentModelExportFormat modelExportFormat = modelExportOverride (m_cmdMediator->document().modelExport(),
458  exportStrategy,
459  fileName);
460  exportStrategy.exportToFile (modelExportFormat,
461  m_cmdMediator->document(),
462  m_modelMainWindow,
463  transformation (),
464  str);
465 
466  m_isDocumentExported = true; // Remember that export was performed
467 
468  updateChecklistGuide ();
469  m_statusBar->showTemporaryMessage("File saved");
470 
471  } else {
472 
473  LOG4CPP_ERROR_S ((*mainCat)) << "MainWindow::fileExport"
474  << " file=" << fileName.toLatin1().data()
475  << " curDir=" << QDir::currentPath().toLatin1().data();
476  QMessageBox::critical (nullptr,
477  engaugeWindowTitle(),
478  tr ("Unable to export to file") + " " + fileName);
479  }
480 }
481 
482 void MainWindow::fileExtractImage (const QString &fileName)
483 {
484  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::fileExtractImage"
485  << " curDir=" << QDir::currentPath().toLatin1().data()
486  << " fileName=" << fileName.toLatin1().data();
487 
488  QFile file (fileName);
489  if (file.open(QIODevice::WriteOnly)) {
490 
491  QPixmap pixmap = m_cmdMediator->pixmap();
492  pixmap.save (&file);
493 
494  // Generate a checksum file if performing a regression test
495  if (m_isErrorReportRegressionTest) {
496  QString csvFile = QString ("%1_1")
497  .arg (exportRegressionFilenameFromInputFilename (m_regressionFile));
498 
499  // Generate csv file with only checksum. Since QProcess cannot handle pipes, we let shell execute it
500  QProcess process;
501  process.start ("bash -c \"cksum " + fileName + " | awk '{print $1}' > " + csvFile + "\"");
502  process.waitForFinished (-1);
503  }
504 
505  } else {
506 
507  LOG4CPP_ERROR_S ((*mainCat)) << "MainWindow::fileExtractImage"
508  << " file=" << fileName.toLatin1().data()
509  << " curDir=" << QDir::currentPath().toLatin1().data();
510  QMessageBox::critical (nullptr,
511  engaugeWindowTitle(),
512  tr ("Unable to extract image to file") + " " + fileName);
513  }
514 }
515 
516 void MainWindow::fileImport (const QString &fileName,
517  ImportType importType)
518 {
519  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::fileImport"
520  << " fileName=" << fileName.toLatin1 ().data ()
521  << " curDir=" << QDir::currentPath().toLatin1().data()
522  << " importType=" << importType;
523 
524  QString originalFileOld = m_originalFile;
525  bool originalFileWasImported = m_originalFileWasImported;
526 
527  m_originalFile = fileName; // Make this available for logging in case an error occurs during the load
528  m_originalFileWasImported = true;
529 
530  if (importType == IMPORT_TYPE_ADVANCED) {
531 
532  // Remove any existing points, axes checker(s) and such from the previous Document so they do not appear in setupAfterLoadNewDocument
533  // when previewing for IMAGE_TYPE_ADVANCED
534  slotFileClose();
535 
536  // Restore the background just closed by slotFileClose. This is required so when the image is loaded for preview, it will appear
537  m_backgroundStateContext->setBackgroundImage(BACKGROUND_IMAGE_ORIGINAL);
538  }
539 
540  QImage image;
541  bool loaded = false;
542 
543 #ifdef ENGAUGE_JPEG2000
544  Jpeg2000 jpeg2000;
545  loaded = jpeg2000.load (fileName,
546  image);
547 #endif // ENGAUGE_JPEG2000
548 
549 #ifdef ENGAUGE_PDF
550  if (!loaded) {
551 
552  Pdf pdf;
553  PdfReturn pdfReturn = pdf.load (fileName,
554  image,
555  m_modelMainWindow.pdfResolution(),
556  m_modelMainWindow.importCropping(),
557  m_isErrorReportRegressionTest);
558  if (pdfReturn == PDF_RETURN_CANCELED) {
559 
560  // User canceled so exit immediately
561  return;
562 
563  }
564 
565  loaded = (pdfReturn == PDF_RETURN_SUCCESS);
566  }
567 #endif // ENGAUGE_PDF
568 
569  if (!loaded) {
570  NonPdf nonPdf;
571  NonPdfReturn nonPdfReturn = nonPdf.load (fileName,
572  image,
573  m_modelMainWindow.importCropping(),
574  m_isErrorReportRegressionTest);
575  if (nonPdfReturn == NON_PDF_RETURN_CANCELED) {
576 
577  // User canceled so exit immediately
578  return;
579 
580  }
581 
582  loaded = (nonPdfReturn == NON_PDF_RETURN_SUCCESS);
583  }
584 
585  if (!loaded) {
586  QString msg = QString("%1 %2 %3 %4.")
587  .arg (tr ("Cannot read file"))
588  .arg (fileName)
589  .arg (tr ("from directory"))
590  .arg (QDir::currentPath());
591 #ifdef WIN32
592  if (fileName.contains ("???")) {
593 
594  // At this point the file name is filled with question marks in Windows if it had letter from
595  // more than one alphabet (e.g. latin '.dig' suffix and cyrillic basename)
596  // in which case we cannot recover the original file without user intervention
597  msg += QObject::tr ("The file appears to have characters from multiple language "
598  "alphabets, which does not work in the Windows command line");
599  }
600 #endif
601  QMessageBox::warning (this,
602  engaugeWindowTitle(),
603  msg);
604 
605  // Reset
606  m_originalFile = originalFileOld;
607  m_originalFileWasImported = originalFileWasImported;
608 
609  } else {
610 
611  loaded = loadImage (fileName,
612  image,
613  importType);
614 
615  if (!loaded) {
616 
617  // Failed
618  if (importType == IMPORT_TYPE_ADVANCED) {
619 
620  // User cancelled after another file was imported so it could be previewed. In anticipation of the loading-for-preview,
621  // we closed the current Document at the top of this method so we cannot reload. So, the only option is to close again
622  // so the half-imported current Document is removed
623  slotFileClose();
624 
625  } else {
626 
627  // Reset
628  m_originalFile = originalFileOld;
629  m_originalFileWasImported = originalFileWasImported;
630  }
631  }
632  }
633 }
634 
635 void MainWindow::fileImportWithPrompts (ImportType importType)
636 {
637  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::fileImportWithPrompts"
638  << " importType=" << importType;
639 
640  // Skip maybeSave method for IMPORT_TYPE_REPLACE_IMAGE since open file dialog is enough to allow user to cancel the operation, and
641  // since no information is lost in that case
642  bool okToContinue = true;
643  if (importType != IMPORT_TYPE_IMAGE_REPLACE) {
644  okToContinue = maybeSave ();
645  }
646 
647  if (okToContinue) {
648 
649  QString filter;
650  QTextStream str (&filter);
651 
652  ImportImageExtensions importImageExtensions;
653  QStringList supportedImageFormatStrings = importImageExtensions.fileExtensionsWithAsterisks ();
654 
655  str << "Image Files (" << supportedImageFormatStrings.join (" ") << ")";
656 
657  // Allow selection of files with strange suffixes in case the file extension was changed. Since
658  // the default is the first filter, we add this afterwards (it is the off-nominal case)
659  str << ";; All Files (*.*)";
660 
661  MainDirectoryPersist directoryPersist;
662  QString fileName = QFileDialog::getOpenFileName (this,
663  tr("Import Image"),
664  directoryPersist.getDirectoryImportOpen ().path (),
665  filter);
666  if (!fileName.isEmpty ()) {
667 
668  directoryPersist.setDirectoryImportOpenFromFilename (fileName);
669 
670  // We import the file BEFORE asking the number of coordinate systems, so user can see how many there are
671  fileImport (fileName,
672  importType);
673  }
674  }
675 }
676 
677 QString MainWindow::fileNameForExportOnly () const
678 {
679  ExportToFile exportStrategy;
680 
681  QString fileName;
682  if (m_isErrorReportRegressionTest) {
683 
684  // Regression test has a specific file extension
685  fileName = QString ("%1_1")
686  .arg (exportRegressionFilenameFromInputFilename (m_regressionFile));
687 
688  } else {
689 
690  // User requested export-only mode so just change file extension
691  QString dir = QFileInfo (m_currentFileWithPathAndFileExtension).absolutePath();
692  fileName = QString ("%1/%2.%3")
693  .arg (dir)
694  .arg (m_currentFile)
695  .arg (exportStrategy.fileExtensionCsv ());
696  }
697 
698  return fileName;
699 }
700 
701 QString MainWindow::fileNameForExtractImageOnly () const
702 {
703  // User requested export-only mode so just change file extension
704  QString dir = QFileInfo (m_currentFileWithPathAndFileExtension).absolutePath();
705  QString fileName = QString ("%1/%2.%3")
706  .arg (dir)
707  .arg (m_currentFile)
708  .arg (m_extractImageOnlyExtension);
709 
710  return fileName;
711 }
712 
713 void MainWindow::filePaste (ImportType importType)
714 {
715  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::filePaste"
716  << " importType=" << importType;
717 
718  QString originalFileOld = m_originalFile;
719  bool originalFileWasImported = m_originalFileWasImported;
720 
721  QString fileName ("clipboard");
722  m_originalFile = fileName; // Make this available for logging in case an error occurs during the load
723  m_originalFileWasImported = true;
724 
725  if (importType == IMPORT_TYPE_ADVANCED) {
726 
727  // Remove any existing points, axes checker(s) and such from the previous Document so they do not appear in setupAfterLoadNewDocument
728  // when previewing for IMAGE_TYPE_ADVANCED
729  slotFileClose();
730 
731  // Restore the background just closed by slotFileClose. This is required so when the image is loaded for preview, it will appear
732  m_backgroundStateContext->setBackgroundImage(BACKGROUND_IMAGE_ORIGINAL);
733  }
734 
735  // An image was in the clipboard when this method was called but it may have disappeared
736  QImage image = QApplication::clipboard()->image();
737 
738  bool loaded = false;
739  if (!loaded) {
740  loaded = !image.isNull();
741  }
742 
743  if (!loaded) {
744  QMessageBox::warning (this,
745  engaugeWindowTitle(),
746  QString("%1 %2 %3 %4.")
747  .arg (tr ("Cannot read file"))
748  .arg (fileName)
749  .arg (tr ("from directory"))
750  .arg (QDir::currentPath ()));
751 
752  // Reset
753  m_originalFile = originalFileOld;
754  m_originalFileWasImported = originalFileWasImported;
755 
756  } else {
757 
758  loaded = loadImage (fileName,
759  image,
760  importType);
761 
762  if (!loaded) {
763 
764  // Failed
765  if (importType == IMPORT_TYPE_ADVANCED) {
766 
767  // User cancelled after another file was imported so it could be previewed. In anticipation of the loading-for-preview,
768  // we closed the current Document at the top of this method so we cannot reload. So, the only option is to close again
769  // so the half-imported current Document is removed
770  slotFileClose();
771 
772  } else {
773 
774  // Reset
775  m_originalFile = originalFileOld;
776  m_originalFileWasImported = originalFileWasImported;
777  }
778  }
779  }
780 }
781 
782 void MainWindow::ghostsCreate ()
783 {
784  LOG4CPP_DEBUG_S ((*mainCat)) << "MainWindow::ghostsCreate";
785 
786  ENGAUGE_ASSERT (m_ghosts == nullptr);
787  m_ghosts = new Ghosts (m_cmdMediator->document().coordSystemIndex());
788 
789  for (unsigned int index = 0; index < m_cmdMediator->document().coordSystemCount(); index++) {
790 
791  // Skip this coordinate system if it is the selected coordinate system since it will be displayed anyway, so no ghosts are required
792  if (index != m_ghosts->coordSystemIndexToBeRestored ()) {
793 
794  updateCoordSystem (index);
795 
796  // Take a snapshot of the graphics items
797  m_ghosts->captureGraphicsItems (*m_scene);
798  }
799  }
800 
801  // Restore the coordinate system that was originally selected, so its points/lines are visible
803 
804  // Make visible ghosts
805  m_ghosts->createGhosts (*m_scene);
806 }
807 
808 void MainWindow::ghostsDestroy ()
809 {
810  LOG4CPP_DEBUG_S ((*mainCat)) << "MainWindow::ghostsDestroy";
811 
812  ENGAUGE_CHECK_PTR (m_ghosts);
813 
814  m_ghosts->destroyGhosts(*m_scene);
815 
816  delete m_ghosts;
817  m_ghosts = nullptr;
818 }
819 
820 void MainWindow::handlerFileExtractImage ()
821 {
822  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::handlerFileExtractImage";
823 
824  if (m_isExtractImageOnly) {
825  QString fileName = fileNameForExtractImageOnly ();
826 
827  MainDirectoryPersist directoryPersist;
828 
829  directoryPersist.setDirectoryExportSaveFromFilename(fileName);
830  fileExtractImage(fileName);
831  }
832 }
833 
835 {
836  return m_backgroundStateContext->imageForCurveState();
837 }
838 
840 {
841  return m_isGnuplot;
842 }
843 
844 void MainWindow::loadCoordSystemListFromCmdMediator ()
845 {
846  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::loadCoordSystemListFromCmdMediator";
847 
848  m_cmbCoordSystem->clear();
849 
850  unsigned int numberCoordSystem = m_cmdMediator->document().coordSystemCount();
851 
852  for (unsigned int i = 0; i < numberCoordSystem; i++) {
853  int index1Based = signed (i + 1);
854  m_cmbCoordSystem->addItem (QString::number (index1Based),
855  QVariant (i));
856  }
857 
858  // Always start with the first entry selected
859  m_cmbCoordSystem->setCurrentIndex (0);
860 
861  // Disable the controls if there is only one entry. Hopefully the user will not even notice it, thus simplifying the interface
862  bool enable = (m_cmbCoordSystem->count() > 1);
863  m_cmbCoordSystem->setEnabled (enable);
864  m_btnShowAll->setEnabled (enable);
865  m_btnPrintAll->setEnabled (enable);
866 }
867 
868 void MainWindow::loadCurveListFromCmdMediator ()
869 {
870  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::loadCurveListFromCmdMediator";
871 
872  m_cmbCurve->clear ();
873  QStringList curvesGraphsNames = m_cmdMediator->curvesGraphsNames ();
874  QStringList::iterator itr;
875  for (itr = curvesGraphsNames.begin (); itr != curvesGraphsNames.end (); itr++) {
876 
877  QString curvesGraphName = *itr;
878  m_cmbCurve->addItem (curvesGraphName);
879  }
880 
881  // Select the curve that is associated with the current coordinate system
882  m_cmbCurve->setCurrentText (m_cmdMediator->selectedCurveName ());
883 }
884 
885 void MainWindow::loadDocumentFile (const QString &fileName)
886 {
887  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::loadDocumentFile fileName=" << fileName.toLatin1 ().data ();
888 
889  QApplication::setOverrideCursor(Qt::WaitCursor);
890  CmdMediator *cmdMediator = new CmdMediator (*this,
891  fileName);
892 
893  if (cmdMediator->successfulRead ()) {
894 
895  setCurrentPathFromFile (fileName);
896  rebuildRecentFileListForCurrentFile(fileName);
897  m_currentFile = fileName; // This enables the FileSaveAs menu option
898 
899  delete m_cmdMediator;
900 
901  m_cmdMediator = cmdMediator;
902  setupAfterLoadNewDocument (fileName,
903  tr ("File opened"),
904  IMPORT_TYPE_SIMPLE);
905 
906  // Start select mode
907  m_actionDigitizeSelect->setChecked (true); // We assume user wants to first select existing stuff
908  slotDigitizeSelect(); // Trigger transition so cursor gets updated immediately
909 
910  m_engaugeFile = fileName;
911  m_originalFile = fileName; // This is needed by updateAfterCommand below if an error report is generated
912  m_originalFileWasImported = false;
913 
914  updateGridLines ();
915  updateAfterCommand (); // Enable Save button now that m_engaugeFile is set
916 
917  QApplication::restoreOverrideCursor();
918 
919  } else {
920 
921  QApplication::restoreOverrideCursor();
922 
923  QMessageBox::warning (this,
924  engaugeWindowTitle(),
925  QString("%1 %2 %3 %4:\n%5.")
926  .arg (tr ("Cannot read file"))
927  .arg (fileName)
928  .arg (tr ("from directory"))
929  .arg (QDir::currentPath ())
930  .arg(cmdMediator->reasonForUnsuccessfulRead ()));
931  delete cmdMediator;
932 
933  }
934 }
935 
936 void MainWindow::loadErrorReportFile(const QString &errorReportFile)
937 {
938  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::loadErrorReportFile"
939  << " file=" << errorReportFile.toLatin1().data();
940 
941  QFile file (errorReportFile);
942  if (!file.exists()) {
943  // Convert path from relative to absolute so file-not-found errors are easier to fix
944  QFileInfo fileInfo (errorReportFile);
945 
946  QMessageBox::critical (this,
947  engaugeWindowTitle(),
948  tr ("File not found") + ": " + fileInfo.absoluteFilePath());
949  exit (-1);
950  }
951 
952  // Open the error report file as if it was a regular Document file
953  QXmlStreamReader reader (&file);
954  file.open(QIODevice::ReadOnly | QIODevice::Text);
955  m_cmdMediator = new CmdMediator(*this,
956  errorReportFile);
957 
958  // Load the commands into the shadow command stack
959  m_cmdStackShadow->loadCommands (*this,
960  m_cmdMediator->document(),
961  reader);
962  file.close();
963 
964  setupAfterLoadNewDocument (errorReportFile,
965  tr ("Error report opened"),
966  IMPORT_TYPE_SIMPLE);
967 
968  // Start select mode
969  m_actionDigitizeSelect->setChecked (true); // We assume user wants to first select existing stuff
970  slotDigitizeSelect(); // Trigger transition so cursor gets updated immediately
971 
973 }
974 
975 bool MainWindow::loadImage (const QString &fileName,
976  const QImage &image,
977  ImportType importType)
978 {
979  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::loadImage"
980  << " fileName=" << fileName.toLatin1 ().data ()
981  << " importType=" << importType;
982 
983  bool success;
984  if (importType == IMPORT_TYPE_IMAGE_REPLACE) {
985  success = loadImageReplacingImage (fileName,
986  image,
987  importType);
988  } else {
989  success = loadImageNewDocument (fileName,
990  image,
991  importType);
992  }
993 
994  return success;
995 }
996 
997 bool MainWindow::loadImageNewDocument (const QString &fileName,
998  const QImage &image,
999  ImportType importType)
1000 {
1001  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::loadImageNewDocument"
1002  << " fileName=" << fileName.toLatin1 ().data ()
1003  << " importType=" << importType;
1004 
1005  ENGAUGE_ASSERT (importType != IMPORT_TYPE_IMAGE_REPLACE);
1006 
1007  QApplication::setOverrideCursor(Qt::WaitCursor);
1008  CmdMediator *cmdMediator = new CmdMediator (*this,
1009  image);
1010  QApplication::restoreOverrideCursor();
1011 
1012  setCurrentPathFromFile (fileName);
1013  // We do not call rebuildRecentFileListForCurrentFile for an image file, so only proper Engauge document files appear in the recent file list
1014  m_engaugeFile = EMPTY_FILENAME; // Forces first Save to be treated as Save As
1015 
1016  delete m_cmdMediator;
1017 
1018  m_cmdMediator = cmdMediator;
1019  bool accepted = setupAfterLoadNewDocument (fileName,
1020  tr ("File imported"),
1021  importType);
1022 
1023  if (accepted) {
1024 
1025  // Show the wizard if user selected it and we are not running a script
1026  if (m_actionHelpChecklistGuideWizard->isChecked () &&
1027  (m_fileCmdScript == nullptr)) {
1028 
1029  // Show wizard
1030  ChecklistGuideWizard *wizard = new ChecklistGuideWizard (*this,
1031  m_cmdMediator->document().coordSystemCount());
1032  if (wizard->exec() == QDialog::Accepted) {
1033 
1034  for (CoordSystemIndex coordSystemIndex = 0; coordSystemIndex < m_cmdMediator->document().coordSystemCount(); coordSystemIndex++) {
1035 
1036  // Populate the checklist guide
1037  m_dockChecklistGuide->setTemplateHtml (wizard->templateHtml(coordSystemIndex),
1038  wizard->curveNames(coordSystemIndex));
1039 
1040  // Update Document
1041  CurvesGraphs curvesGraphs;
1042  wizard->populateCurvesGraphs (coordSystemIndex,
1043  curvesGraphs);
1044  m_cmdMediator->document().setCurvesGraphs(curvesGraphs);
1045  }
1046 
1047  // Unhide the checklist guide
1048  m_actionViewChecklistGuide->setChecked (true);
1049 
1050  // Update the curve dropdown
1051  loadCurveListFromCmdMediator();
1052 
1053  // Update the CoordSystem dropdown
1054  loadCoordSystemListFromCmdMediator();
1055  }
1056  delete wizard;
1057  }
1058 
1059  // Start axis mode
1060  m_actionDigitizeAxis->setChecked (true); // We assume user first wants to digitize axis points
1061 
1062  // Trigger transition so cursor gets updated immediately
1063  if (modeMap ()) {
1064  slotDigitizeScale ();
1065  } else if (modeGraph ()) {
1066  slotDigitizeAxis ();
1067  }
1068 
1069  updateControls ();
1070  }
1071 
1072  return accepted;
1073 }
1074 
1075 bool MainWindow::loadImageReplacingImage (const QString &fileName,
1076  const QImage &image,
1077  ImportType importType)
1078 {
1079  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::loadImageReplacingImage"
1080  << " fileName=" << fileName.toLatin1 ().data ()
1081  << " importType=" << importType;
1082 
1083  ENGAUGE_ASSERT (importType == IMPORT_TYPE_IMAGE_REPLACE);
1084 
1085  setCurrentPathFromFile (fileName);
1086  // We do not call rebuildRecentFileListForCurrentFile for an image file, so only proper Engauge document files appear in the recent file list
1087  m_engaugeFile = EMPTY_FILENAME; // Forces first Save to be treated as Save As
1088 
1089  ENGAUGE_ASSERT (m_cmdMediator != nullptr); // Menu option should only be available when a document is currently open
1090 
1091  m_cmdMediator->document().setPixmap (image);
1092 
1093  bool accepted = setupAfterLoadReplacingImage (fileName,
1094  tr ("File imported"),
1095  importType);
1096 
1097  // No checklist guide wizard is displayed when just replacing the image
1098 
1099  return accepted;
1100 }
1101 
1102 void MainWindow::loadInputFileForErrorReport(QDomDocument &domInputFile) const
1103 {
1104  QFile file (m_originalFile);
1105 
1106  // File should be available for opening, if not then the dom will be left empty. We assume it has not been
1107  // modified since opened
1108  if (!file.open (QIODevice::ReadOnly)) {
1109  return;
1110  }
1111 
1112  domInputFile.setContent (&file);
1113  file.close();
1114 }
1115 
1116 void MainWindow::loadToolTips()
1117 {
1118  if (m_actionViewToolTips->isChecked ()) {
1119 
1120  // Show tool tips
1121  m_actionDigitizeSelect->setToolTip (m_actionDigitizeSelect->text());
1122  m_actionDigitizeAxis->setToolTip (m_actionDigitizeAxis->text());
1123  m_actionDigitizeScale->setToolTip (m_actionDigitizeScale->text());
1124  m_actionDigitizeCurve->setToolTip (m_actionDigitizeCurve->text());
1125  m_actionDigitizePointMatch->setToolTip (m_actionDigitizePointMatch->text());
1126  m_actionDigitizeColorPicker->setToolTip (m_actionDigitizeColorPicker->text());
1127  m_actionDigitizeSegment->setToolTip (m_actionDigitizeSegment->text());
1128  m_cmbBackground->setToolTip (tr ("Background image."));
1129  m_cmbCurve->setToolTip (tr ("Currently selected curve."));
1130  m_viewPointStyle->setToolTip (tr ("Point style for currently selected curve."));
1131  m_viewSegmentFilter->setToolTip (tr ("Segment Fill filter for currently selected curve."));
1132 
1133  } else {
1134 
1135  // Remove any previous tool tips
1136  m_actionDigitizeSelect->setToolTip ("");
1137  m_actionDigitizeAxis->setToolTip ("");
1138  m_actionDigitizeScale->setToolTip ("");
1139  m_actionDigitizeCurve->setToolTip ("");
1140  m_actionDigitizePointMatch->setToolTip ("");
1141  m_actionDigitizeColorPicker->setToolTip ("");
1142  m_actionDigitizeSegment->setToolTip ("");
1143  m_cmbBackground->setToolTip ("");
1144  m_cmbCurve->setToolTip ("");
1145  m_viewPointStyle->setToolTip ("");
1146  m_viewSegmentFilter->setToolTip ("");
1147 
1148  }
1149 }
1150 
1151 bool MainWindow::modeGraph () const
1152 {
1153  bool success = false;
1154 
1155  if (m_cmdMediator != nullptr) {
1156  success = (m_cmdMediator->document().documentAxesPointsRequired() != DOCUMENT_AXES_POINTS_REQUIRED_2);
1157  }
1158 
1159  return success;
1160 }
1161 
1162 bool MainWindow::modeMap () const
1163 {
1164  bool success = false;
1165 
1166  if (m_cmdMediator != nullptr) {
1167  success = (m_cmdMediator->document().documentAxesPointsRequired() == DOCUMENT_AXES_POINTS_REQUIRED_2);
1168  }
1169 
1170  return success;
1171 }
1172 
1173 bool MainWindow::maybeSave()
1174 {
1175  if (m_cmdMediator != nullptr) {
1176  if (m_cmdMediator->isModified()) {
1177  QMessageBox::StandardButton ret = QMessageBox::warning (this,
1178  engaugeWindowTitle(),
1179  tr("The document has been modified.\n"
1180  "Do you want to save your changes?"),
1181  QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
1182  if (ret == QMessageBox::Save) {
1183  return slotFileSave();
1184  } else if (ret == QMessageBox::Cancel) {
1185  return false;
1186  }
1187  }
1188  }
1189 
1190  return true;
1191 }
1192 
1193 DocumentModelExportFormat MainWindow::modelExportOverride (const DocumentModelExportFormat &modelExportFormatBefore,
1194  const ExportToFile &exportStrategy,
1195  const QString &fileName) const
1196 {
1197  DocumentModelExportFormat modelExportFormatAfter = modelExportFormatBefore;
1198 
1199  // See if delimiter setting overrides commas/tabs for files with csv/tsv file extensions respectively
1200  if (!modelExportFormatAfter.overrideCsvTsv()) {
1201 
1202  // Extract file extensions
1203  QString csvExtension = QString (".%1")
1204  .arg (exportStrategy.fileExtensionCsv());
1205  QString tsvExtension = QString (".%1")
1206  .arg (exportStrategy.fileExtensionTsv());
1207  QString fileExtensionVersusCsv = fileName.right (csvExtension.size());
1208  QString fileExtensionVersusTsv = fileName.right (tsvExtension.size());
1209 
1210  // Override if CSV or TSV was selected. We cannot use QFileDialog::selectedNameFilter() since that is
1211  // broken in Linux, so we use the file extension
1212  if (csvExtension.compare (fileExtensionVersusCsv, Qt::CaseInsensitive) == 0) {
1213  modelExportFormatAfter.setDelimiter (EXPORT_DELIMITER_COMMA);
1214  } else if (tsvExtension.compare (fileExtensionVersusTsv, Qt::CaseInsensitive) == 0) {
1215  modelExportFormatAfter.setDelimiter (EXPORT_DELIMITER_TAB);
1216  }
1217  }
1218 
1219  return modelExportFormatAfter;
1220 }
1221 
1223 {
1224  return m_modelMainWindow;
1225 }
1226 
1227 void MainWindow::rebuildRecentFileListForCurrentFile(const QString &filePath)
1228 {
1229  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::rebuildRecentFileListForCurrentFile";
1230 
1231  setWindowFilePath (filePath);
1232 
1233  QSettings settings (SETTINGS_ENGAUGE, SETTINGS_DIGITIZER);
1234  QStringList recentFilePaths = settings.value (SETTINGS_RECENT_FILE_LIST).toStringList();
1235  recentFilePaths.removeAll (filePath); // Remove previous instance of the current filePath
1236  recentFilePaths.prepend (filePath); // Insert current filePath at start
1237  while (recentFilePaths.count () > qFloor (MAX_RECENT_FILE_LIST_SIZE)) {
1238  recentFilePaths.removeLast (); // Remove entry since the number of entries exceeds the limit
1239  }
1240  settings.setValue (SETTINGS_RECENT_FILE_LIST, recentFilePaths);
1241 
1242  updateRecentFileList();
1243 }
1244 
1245 void MainWindow::resizeEvent(QResizeEvent * /* event */)
1246 {
1247  LOG4CPP_DEBUG_S ((*mainCat)) << "MainWindow::resizeEvent";
1248 
1249  if (m_actionZoomFill->isChecked ()) {
1250  slotViewZoomFactor (ZOOM_FILL);
1251  }
1252 }
1253 
1254 bool MainWindow::saveDocumentFile (const QString &fileName)
1255 {
1256  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::saveDocumentFile fileName=" << fileName.toLatin1 ().data ();
1257 
1258  QFile file(fileName);
1259  if (!file.open(QFile::WriteOnly)) {
1260  QMessageBox::warning (this,
1261  engaugeWindowTitle(),
1262  QString ("%1 %2: \n%3.")
1263  .arg(tr ("Cannot write file"))
1264  .arg(fileName)
1265  .arg(file.errorString()));
1266  return false;
1267  }
1268 
1269  rebuildRecentFileListForCurrentFile (fileName);
1270 
1271  QApplication::setOverrideCursor (Qt::WaitCursor);
1272  QXmlStreamWriter writer(&file);
1273  writer.setAutoFormatting(true);
1274  writer.writeStartDocument();
1275  writer.writeDTD("<!DOCTYPE engauge>");
1276  m_cmdMediator->document().saveXml(writer);
1277  writer.writeEndDocument();
1278  QApplication::restoreOverrideCursor ();
1279 
1280  // Notify the undo stack that the current state is now considered "clean". This will automatically trigger a
1281  // signal back to this class that will update the modified marker in the title bar
1282  m_cmdMediator->setClean ();
1283 
1284  setCurrentFile(fileName);
1285  m_engaugeFile = fileName;
1286  updateAfterCommand (); // Enable Save button now that m_engaugeFile is set
1287  m_statusBar->showTemporaryMessage("File saved");
1288 
1289  return true;
1290 }
1291 
1292 void MainWindow::saveErrorReportFileAndExit (const char *context,
1293  const char *file,
1294  int line,
1295  const char *comment)
1296 {
1297  // Skip if currently performing a regression test - in which case the preferred behavior is to let the current test fail and
1298  // continue on to execute the remaining tests
1299  if ((m_cmdMediator != nullptr) && !m_isErrorReportRegressionTest) {
1300 
1301  QString report = saveErrorReportFileAndExitXml (context,
1302  file,
1303  line,
1304  comment);
1305 
1306  DlgErrorReportLocal dlg (report);
1307  if (dlg.exec() == QDialog::Accepted) {
1308  QFileDialog dlg;
1309 
1310  QString fileName = dlg.getSaveFileName (this,
1311  tr("Save"),
1312  "error_report.xml");
1313  if (!fileName.isEmpty ()) {
1314  // Save the error report
1315  QFile fileError (fileName);
1316  QTextStream str (&fileError);
1317  fileError.open (QIODevice::WriteOnly | QIODevice::Text);
1318  str << report;
1319  fileError.close ();
1320  }
1321  }
1322 
1323  exit (-1);
1324  }
1325 }
1326 
1327 QString MainWindow::saveErrorReportFileAndExitXml (const char *context,
1328  const char *file,
1329  int line,
1330  const char *comment) const
1331 {
1332  const bool DEEP_COPY = true;
1333 
1334  QString xmlErrorReport;
1335  QXmlStreamWriter writer (&xmlErrorReport);
1336  writer.setAutoFormatting(true);
1337 
1338  // Entire error report contains metadata, commands and other details
1339  writer.writeStartElement(DOCUMENT_SERIALIZE_ERROR_REPORT);
1340 
1341  // Version
1342  writer.writeStartElement(DOCUMENT_SERIALIZE_APPLICATION);
1343  writer.writeAttribute(DOCUMENT_SERIALIZE_APPLICATION_VERSION_NUMBER, VERSION_NUMBER);
1344  writer.writeEndElement();
1345 
1346  // Document
1347  // Insert snapshot xml into writer stream, by reading from reader stream. Highest level of snapshot is DOCUMENT_SERIALIZE_APPLICATION
1348  QXmlStreamReader reader (m_startingDocumentSnapshot);
1349  while (!reader.atEnd ()) {
1350  reader.readNext ();
1351  if (reader.tokenType() != QXmlStreamReader::StartDocument &&
1352  reader.tokenType() != QXmlStreamReader::EndDocument &&
1353  reader.tokenType() != QXmlStreamReader::Invalid) {
1354  writer.writeCurrentToken (reader);
1355  }
1356  }
1357 
1358  // Operating system
1359  writer.writeStartElement(DOCUMENT_SERIALIZE_OPERATING_SYSTEM);
1360  writer.writeAttribute(DOCUMENT_SERIALIZE_OPERATING_SYSTEM_ENDIAN, EndianToString (QSysInfo::ByteOrder));
1361  writer.writeAttribute(DOCUMENT_SERIALIZE_OPERATING_SYSTEM_WORD_SIZE, QString::number (QSysInfo::WordSize));
1362  writer.writeEndElement();
1363 
1364  // Placeholder for original file, before the commands in the command stack were applied
1365  writer.writeStartElement(DOCUMENT_SERIALIZE_FILE);
1366  writer.writeAttribute(DOCUMENT_SERIALIZE_FILE_IMPORTED,
1367  m_originalFileWasImported ? DOCUMENT_SERIALIZE_BOOL_TRUE : DOCUMENT_SERIALIZE_BOOL_FALSE);
1368  writer.writeEndElement();
1369 
1370  // Commands
1371  m_cmdMediator->saveXml(writer);
1372 
1373  // Error
1374  writer.writeStartElement(DOCUMENT_SERIALIZE_ERROR);
1375  writer.writeAttribute(DOCUMENT_SERIALIZE_ERROR_CONTEXT, context);
1376  writer.writeAttribute(DOCUMENT_SERIALIZE_ERROR_FILE, file);
1377  writer.writeAttribute(DOCUMENT_SERIALIZE_ERROR_LINE, QString::number (line));
1378  writer.writeAttribute(DOCUMENT_SERIALIZE_ERROR_COMMENT, comment);
1379  writer.writeEndElement();
1380 
1381  writer.writeEndElement();
1382 
1383  // Put string into DOM
1384  QDomDocument domErrorReport ("ErrorReport");
1385  domErrorReport.setContent (xmlErrorReport);
1386 
1387  // Postprocessing
1388  if (!m_originalFileWasImported) {
1389 
1390  // Insert the original file into its placeholder, by manipulating the source and target xml as DOM documents. Very early
1391  // in the loading process, the original file may not be specified yet (m_originalFile is empty)
1392  QDomDocument domInputFile;
1393  loadInputFileForErrorReport (domInputFile);
1394  QDomDocumentFragment fragmentFileFrom = domErrorReport.createDocumentFragment();
1395  if (!domInputFile.isNull()) {
1396  fragmentFileFrom.appendChild (domErrorReport.importNode (domInputFile.documentElement(), DEEP_COPY));
1397  }
1398  QDomNodeList nodesFileTo = domErrorReport.elementsByTagName (DOCUMENT_SERIALIZE_FILE);
1399  if (nodesFileTo.count () > 0) {
1400  QDomNode nodeFileTo = nodesFileTo.at (0);
1401  nodeFileTo.appendChild (fragmentFileFrom);
1402  }
1403 
1404  // Replace DOCUMENT_SERIALIZE_IMAGE by same node with CDATA removed, since:
1405  // 1) it is very big and working with smaller files, especially in emails, is easier
1406  // 2) removing the image better preserves user's privacy
1407  // 3) having the actual image does not help that much when debugging
1408  QDomNodeList nodesDocument = domErrorReport.elementsByTagName (DOCUMENT_SERIALIZE_DOCUMENT);
1409  for (int i = 0 ; i < nodesDocument.count(); i++) {
1410  QDomNode nodeDocument = nodesDocument.at (i);
1411  QDomElement elemImage = nodeDocument.firstChildElement(DOCUMENT_SERIALIZE_IMAGE);
1412  if (!elemImage.isNull()) {
1413 
1414  // Get old image attributes so we can create an empty document with the same size
1415  if (elemImage.hasAttribute (DOCUMENT_SERIALIZE_IMAGE_WIDTH) &&
1416  elemImage.hasAttribute (DOCUMENT_SERIALIZE_IMAGE_HEIGHT)) {
1417 
1418  int width = elemImage.attribute(DOCUMENT_SERIALIZE_IMAGE_WIDTH).toInt();
1419  int height = elemImage.attribute(DOCUMENT_SERIALIZE_IMAGE_HEIGHT).toInt();
1420 
1421  QDomNode nodeReplacement;
1422  QDomElement elemReplacement = nodeReplacement.toElement();
1423  elemReplacement.setAttribute (DOCUMENT_SERIALIZE_IMAGE_WIDTH, width);
1424  elemReplacement.setAttribute (DOCUMENT_SERIALIZE_IMAGE_HEIGHT, height);
1425 
1426  // Replace with the new and then remove the old
1427  nodeDocument.insertBefore (nodeReplacement,
1428  elemImage);
1429  nodeDocument.removeChild(elemImage);
1430  }
1431  }
1432  }
1433  }
1434 
1435  return domErrorReport.toString();
1436 }
1437 
1438 void MainWindow::saveStartingDocumentSnapshot()
1439 {
1440  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::saveStartingDocumentSnapshot";
1441 
1442  QXmlStreamWriter writer (&m_startingDocumentSnapshot);
1443  writer.setAutoFormatting (true);
1444  m_cmdMediator->document().saveXml (writer);
1445 }
1446 
1448 {
1449  ENGAUGE_CHECK_PTR (m_scene);
1450  return *m_scene;
1451 }
1452 
1453 BackgroundImage MainWindow::selectOriginal(BackgroundImage backgroundImage)
1454 {
1455  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::selectBackgroundOriginal";
1456 
1457  BackgroundImage previousBackground = static_cast<BackgroundImage> (m_cmbBackground->currentData().toInt());
1458 
1459  int index = m_cmbBackground->findData (backgroundImage);
1460  ENGAUGE_ASSERT (index >= 0);
1461 
1462  m_cmbBackground->setCurrentIndex(index);
1463 
1464  return previousBackground;
1465 }
1466 
1468 {
1469  return m_cmbCurve->currentText ();
1470 }
1471 
1472 void MainWindow::setCurrentFile (const QString &fileName)
1473 {
1474  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::setCurrentFile";
1475 
1476  QString fileNameStripped;
1477  if (!fileName.isEmpty()) {
1478 
1479  // Strip out path and file extension. We use completeBaseName rather than baseName so
1480  // files with multiple periods are handled correctly - all but last suffix gets kept
1481  QFileInfo fileInfo (fileName);
1482  fileNameStripped = fileInfo.completeBaseName();
1483  }
1484 
1485  m_currentFile = fileNameStripped;
1486  m_currentFileWithPathAndFileExtension = fileName;
1487 
1488  updateWindowTitle ();
1489 }
1490 
1491 void MainWindow::setCurrentPathFromFile (const QString &fileName)
1492 {
1493  QDir dir = QFileInfo (fileName).absoluteDir();
1494 
1495  if (dir.exists ()) {
1496 
1497  bool success = QDir::setCurrent (dir.absolutePath ()); // Return to chosen directory the next time
1498  ENGAUGE_ASSERT (success);
1499 
1500  } else {
1501 
1502  // File was a url so it is irrelevant to the current directory
1503  }
1504 }
1505 
1506 void MainWindow::setNonFillZoomFactor (ZoomFactor newZoomFactor)
1507 {
1508  ENGAUGE_ASSERT (newZoomFactor != ZOOM_FILL);
1509 
1510  // Update controls and apply zoom factor
1511  m_zoomMapToAction [newZoomFactor]->setChecked (true);
1512  slotViewZoomFactor (newZoomFactor);
1513 }
1514 
1515 void MainWindow::setPixmap (const QString &curveSelected,
1516  const QPixmap &pixmap)
1517 {
1518  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::setPixmap";
1519 
1520  m_digitizeStateContext->setImageIsLoaded (m_cmdMediator,
1521  true);
1522 
1523  // We cannot reliably use m_cmbCurve->currentText below for the selected curve since that control
1524  // can be pointing to a curve that no longer exists so this method requires curveSelected as an argument
1525  m_backgroundStateContext->setPixmap (m_isGnuplot,
1526  m_transformation,
1527  m_cmdMediator->document().modelGridRemoval(),
1528  m_cmdMediator->document().modelColorFilter(),
1529  pixmap,
1530  curveSelected);
1531 }
1532 
1533 void MainWindow::settingsRead (bool isReset)
1534 {
1535  QSettings settings (SETTINGS_ENGAUGE, SETTINGS_DIGITIZER);
1536 
1537  if (isReset) {
1538  // Delete all settings. Default values are specified, later, for each settings as it is loaded
1539  settings.clear ();
1540  }
1541 
1542  settingsReadEnvironment (settings);
1543  settingsReadMainWindow (settings);
1544 }
1545 
1546 void MainWindow::settingsReadEnvironment (QSettings &settings)
1547 {
1548  settings.beginGroup (SETTINGS_GROUP_ENVIRONMENT);
1549  QDir::setCurrent (settings.value (SETTINGS_CURRENT_DIRECTORY,
1550  QDir::currentPath ()).toString ());
1551  settings.endGroup ();
1552 }
1553 
1554 void MainWindow::settingsReadMainWindow (QSettings &settings)
1555 {
1556  settings.beginGroup(SETTINGS_GROUP_MAIN_WINDOW);
1557 
1558  // Main window geometry
1559  resize (settings.value (SETTINGS_SIZE,
1560  QSize (600, 600)).toSize ());
1561  move (settings.value (SETTINGS_POS,
1562  QPoint (200, 200)).toPoint ());
1563 
1564  // Help window geometry
1565 #if !defined(OSX_DEBUG) && !defined(OSX_RELEASE)
1566  QSize helpSize = settings.value (SETTINGS_HELP_SIZE,
1567  QSize (900, 600)).toSize();
1568  m_helpWindow->resize (helpSize);
1569  if (settings.contains (SETTINGS_HELP_POS)) {
1570  QPoint helpPos = settings.value (SETTINGS_HELP_POS).toPoint();
1571  m_helpWindow->move (helpPos);
1572  }
1573 #endif
1574 
1575  // Checklist guide wizard
1576  m_actionHelpChecklistGuideWizard->setChecked (settings.value (SETTINGS_CHECKLIST_GUIDE_WIZARD,
1577  true).toBool ());
1578 
1579  // Background toolbar visibility
1580  bool viewBackgroundToolBar = settings.value (SETTINGS_VIEW_BACKGROUND_TOOLBAR,
1581  true).toBool ();
1582  m_actionViewBackground->setChecked (viewBackgroundToolBar);
1583  m_toolBackground->setVisible (viewBackgroundToolBar);
1584  BackgroundImage backgroundImage = static_cast<BackgroundImage> (settings.value (SETTINGS_BACKGROUND_IMAGE,
1585  BACKGROUND_IMAGE_FILTERED).toInt ());
1586  int indexBackground = m_cmbBackground->findData (QVariant (backgroundImage));
1587  m_cmbBackground->setCurrentIndex (indexBackground);
1588 
1589  // Digitize toolbar visibility
1590  bool viewDigitizeToolBar = settings.value (SETTINGS_VIEW_DIGITIZE_TOOLBAR,
1591  true).toBool ();
1592  m_actionViewDigitize->setChecked (viewDigitizeToolBar);
1593  m_toolDigitize->setVisible (viewDigitizeToolBar);
1594 
1595  // Views toolbar visibility
1596  bool viewSettingsViewsToolBar = settings.value (SETTINGS_VIEW_SETTINGS_VIEWS_TOOLBAR,
1597  true).toBool ();
1598  m_actionViewSettingsViews->setChecked (viewSettingsViewsToolBar);
1599  m_toolSettingsViews->setVisible (viewSettingsViewsToolBar);
1600 
1601  // Coordinate system toolbar visibility
1602  bool viewCoordSystemToolbar = settings.value (SETTINGS_VIEW_COORD_SYSTEM_TOOLBAR,
1603  false).toBool ();
1604  m_actionViewCoordSystem->setChecked (viewCoordSystemToolbar);
1605  m_toolCoordSystem->setVisible (viewCoordSystemToolbar);
1606 
1607  // Tooltips visibility
1608  bool viewToolTips = settings.value (SETTINGS_VIEW_TOOL_TIPS,
1609  true).toBool ();
1610  m_actionViewToolTips->setChecked (viewToolTips);
1611  loadToolTips ();
1612 
1613  // Statusbar visibility
1614  StatusBarMode statusBarMode = static_cast<StatusBarMode> (settings.value (SETTINGS_VIEW_STATUS_BAR,
1615  false).toInt ());
1616  m_statusBar->setStatusBarMode (statusBarMode);
1617  m_actionStatusNever->setChecked (statusBarMode == STATUS_BAR_MODE_NEVER);
1618  m_actionStatusTemporary->setChecked (statusBarMode == STATUS_BAR_MODE_TEMPORARY);
1619  m_actionStatusAlways->setChecked (statusBarMode == STATUS_BAR_MODE_ALWAYS);
1620 
1621  addDockWindow (m_dockChecklistGuide,
1622  settings,
1623  SETTINGS_CHECKLIST_GUIDE_DOCK_AREA,
1624  SETTINGS_CHECKLIST_GUIDE_DOCK_GEOMETRY,
1625  Qt::RightDockWidgetArea);
1626  addDockWindow (m_dockFittingWindow,
1627  settings,
1628  SETTINGS_FITTING_WINDOW_DOCK_AREA,
1629  SETTINGS_FITTING_WINDOW_DOCK_GEOMETRY,
1630  Qt::RightDockWidgetArea);
1631  addDockWindow (m_dockGeometryWindow,
1632  settings,
1633  SETTINGS_GEOMETRY_WINDOW_DOCK_AREA,
1634  SETTINGS_GEOMETRY_WINDOW_DOCK_GEOMETRY,
1635  Qt::RightDockWidgetArea);
1636 
1637  // Main window settings. Preference for initial zoom factor is 100%, rather than fill mode, for issue #25. Some or all
1638  // settings are saved to the application AND saved to m_modelMainWindow for use in DlgSettingsMainWindow. Note that
1639  // TranslatorContainer has previously extracted the locale from the settings
1640  QLocale localeDefault;
1641  QLocale::Language language = static_cast<QLocale::Language> (settings.value (SETTINGS_LOCALE_LANGUAGE,
1642  QVariant (localeDefault.language())).toInt());
1643  QLocale::Country country = static_cast<QLocale::Country> (settings.value (SETTINGS_LOCALE_COUNTRY,
1644  QVariant (localeDefault.country())).toInt());
1645  QLocale locale (language,
1646  country);
1647  slotViewZoom (static_cast<ZoomFactor> (settings.value (SETTINGS_ZOOM_FACTOR,
1648  QVariant (ZOOM_1_TO_1)).toInt()));
1649  m_modelMainWindow.setLocale (locale);
1650  m_modelMainWindow.setZoomFactorInitial(static_cast<ZoomFactorInitial> (settings.value (SETTINGS_ZOOM_FACTOR_INITIAL,
1651  QVariant (DEFAULT_ZOOM_FACTOR_INITIAL)).toInt()));
1652  m_modelMainWindow.setZoomControl (static_cast<ZoomControl> (settings.value (SETTINGS_ZOOM_CONTROL,
1653  QVariant (ZOOM_CONTROL_MENU_WHEEL_PLUSMINUS)).toInt()));
1654  m_modelMainWindow.setMainTitleBarFormat (static_cast<MainTitleBarFormat> (settings.value (SETTINGS_MAIN_TITLE_BAR_FORMAT,
1655  QVariant (MAIN_TITLE_BAR_FORMAT_PATH)).toInt()));
1656  m_modelMainWindow.setPdfResolution (settings.value (SETTINGS_IMPORT_PDF_RESOLUTION,
1657  QVariant (DEFAULT_IMPORT_PDF_RESOLUTION)).toInt ());
1658  m_modelMainWindow.setImportCropping (static_cast<ImportCropping> (settings.value (SETTINGS_IMPORT_CROPPING,
1659  QVariant (DEFAULT_IMPORT_CROPPING)).toInt ()));
1660  m_modelMainWindow.setMaximumGridLines (settings.value (SETTINGS_MAXIMUM_GRID_LINES,
1661  QVariant (DEFAULT_MAXIMUM_GRID_LINES)).toInt ());
1662  m_modelMainWindow.setHighlightOpacity (settings.value (SETTINGS_HIGHLIGHT_OPACITY,
1663  QVariant (DEFAULT_HIGHLIGHT_OPACITY)).toDouble ());
1664  m_modelMainWindow.setSmallDialogs (settings.value (SETTINGS_SMALL_DIALOGS,
1665  QVariant (DEFAULT_SMALL_DIALOGS)).toBool ());
1666  m_modelMainWindow.setDragDropExport (settings.value (SETTINGS_DRAG_DROP_EXPORT,
1667  QVariant (DEFAULT_DRAG_DROP_EXPORT)).toBool ());
1668  m_modelMainWindow.setSignificantDigits (settings.value (SETTINGS_SIGNIFICANT_DIGITS,
1669  QVariant (DEFAULT_SIGNIFICANT_DIGITS)).toInt ());
1670  m_modelMainWindow.setImageReplaceRenamesDocument (settings.value (SETTINGS_IMAGE_REPLACE_RENAMES_DOCUMENT,
1671  QVariant (DEFAULT_IMAGE_REPLACE_RENAMES_DOCUMENT)).toBool ());
1672 
1674  updateSmallDialogs();
1675 
1676  settings.endGroup();
1677 }
1678 
1679 void MainWindow::settingsWrite ()
1680 {
1681  QSettings settings (SETTINGS_ENGAUGE, SETTINGS_DIGITIZER);
1682 
1683  settings.beginGroup (SETTINGS_GROUP_ENVIRONMENT);
1684  settings.setValue (SETTINGS_CURRENT_DIRECTORY, QDir::currentPath ());
1685  settings.endGroup ();
1686 
1687  settings.beginGroup (SETTINGS_GROUP_MAIN_WINDOW);
1688  settings.setValue (SETTINGS_SIZE, size ());
1689  settings.setValue (SETTINGS_POS, pos ());
1690 #if !defined(OSX_DEBUG) && !defined(OSX_RELEASE)
1691  settings.setValue (SETTINGS_HELP_SIZE, m_helpWindow->size());
1692  settings.setValue (SETTINGS_HELP_POS, m_helpWindow->pos ());
1693 #endif
1694  if (m_dockChecklistGuide->isFloating()) {
1695 
1696  settings.setValue (SETTINGS_CHECKLIST_GUIDE_DOCK_AREA, Qt::NoDockWidgetArea);
1697  settings.setValue (SETTINGS_CHECKLIST_GUIDE_DOCK_GEOMETRY, m_dockChecklistGuide->saveGeometry ());
1698 
1699  } else {
1700 
1701  settings.setValue (SETTINGS_CHECKLIST_GUIDE_DOCK_AREA, dockWidgetArea (m_dockChecklistGuide));
1702 
1703  }
1704  if (m_dockFittingWindow->isFloating()) {
1705 
1706  settings.setValue (SETTINGS_FITTING_WINDOW_DOCK_AREA, Qt::NoDockWidgetArea);
1707  settings.setValue (SETTINGS_FITTING_WINDOW_DOCK_GEOMETRY, m_dockFittingWindow->saveGeometry());
1708  } else {
1709 
1710  settings.setValue (SETTINGS_FITTING_WINDOW_DOCK_AREA, dockWidgetArea (m_dockFittingWindow));
1711  }
1712  if (m_dockGeometryWindow->isFloating()) {
1713 
1714  settings.setValue (SETTINGS_GEOMETRY_WINDOW_DOCK_AREA, Qt::NoDockWidgetArea);
1715  settings.setValue (SETTINGS_GEOMETRY_WINDOW_DOCK_GEOMETRY, m_dockGeometryWindow->saveGeometry ());
1716 
1717  } else {
1718 
1719  settings.setValue (SETTINGS_GEOMETRY_WINDOW_DOCK_AREA, dockWidgetArea (m_dockGeometryWindow));
1720 
1721  }
1722  settings.setValue (SETTINGS_BACKGROUND_IMAGE, m_cmbBackground->currentData().toInt());
1723  settings.setValue (SETTINGS_CHECKLIST_GUIDE_WIZARD, m_actionHelpChecklistGuideWizard->isChecked ());
1724  settings.setValue (SETTINGS_DRAG_DROP_EXPORT, m_modelMainWindow.dragDropExport ());
1725  settings.setValue (SETTINGS_HIGHLIGHT_OPACITY, m_modelMainWindow.highlightOpacity());
1726  settings.setValue (SETTINGS_IMAGE_REPLACE_RENAMES_DOCUMENT, m_modelMainWindow.imageReplaceRenamesDocument());
1727  settings.setValue (SETTINGS_IMPORT_CROPPING, m_modelMainWindow.importCropping());
1728  settings.setValue (SETTINGS_IMPORT_PDF_RESOLUTION, m_modelMainWindow.pdfResolution ());
1729  settings.setValue (SETTINGS_LOCALE_LANGUAGE, m_modelMainWindow.locale().language());
1730  settings.setValue (SETTINGS_LOCALE_COUNTRY, m_modelMainWindow.locale().country());
1731  settings.setValue (SETTINGS_MAIN_TITLE_BAR_FORMAT, m_modelMainWindow.mainTitleBarFormat());
1732  settings.setValue (SETTINGS_MAXIMUM_GRID_LINES, m_modelMainWindow.maximumGridLines());
1733  settings.setValue (SETTINGS_SMALL_DIALOGS, m_modelMainWindow.smallDialogs());
1734  settings.setValue (SETTINGS_VIEW_BACKGROUND_TOOLBAR, m_actionViewBackground->isChecked());
1735  settings.setValue (SETTINGS_VIEW_DIGITIZE_TOOLBAR, m_actionViewDigitize->isChecked ());
1736  settings.setValue (SETTINGS_VIEW_STATUS_BAR, m_statusBar->statusBarMode ());
1737  settings.setValue (SETTINGS_VIEW_SETTINGS_VIEWS_TOOLBAR, m_actionViewSettingsViews->isChecked ());
1738  settings.setValue (SETTINGS_VIEW_COORD_SYSTEM_TOOLBAR, m_actionViewCoordSystem->isChecked ());
1739  settings.setValue (SETTINGS_VIEW_TOOL_TIPS, m_actionViewToolTips->isChecked ());
1740  settings.setValue (SETTINGS_ZOOM_CONTROL, m_modelMainWindow.zoomControl());
1741  settings.setValue (SETTINGS_ZOOM_FACTOR, currentZoomFactor ());
1742  settings.setValue (SETTINGS_ZOOM_FACTOR_INITIAL, m_modelMainWindow.zoomFactorInitial());
1743  settings.endGroup ();
1744 }
1745 
1746 bool MainWindow::setupAfterLoadNewDocument (const QString &fileName,
1747  const QString &temporaryMessage ,
1748  ImportType importType)
1749 {
1750  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::setupAfterLoadNewDocument"
1751  << " file=" << fileName.toLatin1().data()
1752  << " message=" << temporaryMessage.toLatin1().data()
1753  << " importType=" << importType;
1754 
1755  // The steps in this method should theoretically be a superset of the steps in setupAfterLoadNewDocument. Therefore, any
1756  // changes to this method should be considered for application to the other method also
1757 
1758  const QString EMPTY_CURVE_NAME_TO_SKIP_BACKGROUND_PROCESSING; // For bootstrapping the preview
1759 
1760  // At this point the code assumes CmdMediator for the NEW Document is already stored in m_cmdMediator
1761 
1762  m_digitizeStateContext->resetOnLoad (m_cmdMediator); // Before setPixmap
1763  m_backgroundStateContext->setCurveSelected (m_isGnuplot,
1764  m_transformation,
1765  m_cmdMediator->document().modelGridRemoval(),
1766  m_cmdMediator->document().modelColorFilter(),
1767  EMPTY_CURVE_NAME_TO_SKIP_BACKGROUND_PROCESSING); // Before setPixmap
1768  setPixmap (m_cmdMediator->document().curvesGraphsNames().first(),
1769  m_cmdMediator->pixmap ()); // Set background immediately so it is visible as a preview when any dialogs are displayed
1770 
1771  // Image is visible now so the user can refer to it when we ask for the number of coordinate systems. Note that the Document
1772  // may already have multiple CoordSystem if user loaded a file that had multiple CoordSystem entries
1773  if (importType == IMPORT_TYPE_ADVANCED) {
1774 
1775  applyZoomFactorAfterLoad(); // Apply the currently selected zoom factor
1776 
1777  DlgImportAdvanced dlgImportAdvanced (*this);
1778  dlgImportAdvanced.exec();
1779 
1780  if (dlgImportAdvanced.result() == QDialog::Rejected) {
1781  return false;
1782  }
1783 
1784  int numberCoordSystem = signed (dlgImportAdvanced.numberCoordSystem());
1785  m_cmdMediator->document().addCoordSystems (unsigned (numberCoordSystem - 1));
1786  m_cmdMediator->setDocumentAxesPointsRequired (dlgImportAdvanced.documentAxesPointsRequired());
1787  }
1788 
1789  m_transformation.resetOnLoad();
1790  m_transformationStateContext->resetOnLoad();
1791  m_scene->resetOnLoad();
1792 
1793  connect (m_actionEditUndo, SIGNAL (triggered ()), m_cmdMediator, SLOT (undo ()));
1794  connect (m_actionEditUndo, SIGNAL (triggered ()), m_cmdStackShadow, SLOT (slotUndo ()));
1795  connect (m_actionEditRedo, SIGNAL (triggered ()), m_cmdMediator, SLOT (redo ())); // No effect until CmdMediator::undo and CmdStackShadow::slotUndo get called
1796  connect (m_actionEditRedo, SIGNAL (triggered ()), m_cmdStackShadow, SLOT (slotRedo ())); // No effect after CmdMediator::undo and CmdStackShadow::slotUndo get called
1797  connect (m_cmdMediator, SIGNAL (canRedoChanged(bool)), this, SLOT (slotCanRedoChanged (bool)));
1798  connect (m_cmdMediator, SIGNAL (canUndoChanged(bool)), this, SLOT (slotCanUndoChanged (bool)));
1799  connect (m_cmdMediator, SIGNAL (redoTextChanged (const QString &)), this, SLOT (slotRedoTextChanged (const QString &)));
1800  connect (m_cmdMediator, SIGNAL (undoTextChanged (const QString &)), this, SLOT (slotUndoTextChanged (const QString &)));
1801  loadCurveListFromCmdMediator ();
1802  loadCoordSystemListFromCmdMediator ();
1804 
1805  m_isDocumentExported = false;
1806 
1807  // Background must be set (by setPixmap) before slotViewZoomFactor which relies on the background. At this point
1808  // the transformation is undefined (unless the code is changed) so grid removal will not work
1809  // but updateTransformationAndItsDependencies will call this again to fix that issue. Note that the selected
1810  // curve name was set (by setCurveSelected) earlier before the call to setPixmap
1811  m_backgroundStateContext->setCurveSelected (m_isGnuplot,
1812  m_transformation,
1813  m_cmdMediator->document().modelGridRemoval(),
1814  m_cmdMediator->document().modelColorFilter(),
1815  m_cmbCurve->currentText ());
1816  m_backgroundStateContext->setBackgroundImage (static_cast<BackgroundImage> (m_cmbBackground->currentIndex ()));
1817 
1818  applyZoomFactorAfterLoad(); // Zoom factor must be reapplied after background image is set, to have any effect
1819 
1820  setCurrentFile(fileName);
1821  m_statusBar->showTemporaryMessage (temporaryMessage);
1822  m_statusBar->wakeUp ();
1823 
1824  saveStartingDocumentSnapshot();
1825 
1826  updateAfterCommand(); // Replace stale points by points in new Document
1827 
1828  return true;
1829 }
1830 
1831 bool MainWindow::setupAfterLoadReplacingImage (const QString &fileName,
1832  const QString &temporaryMessage ,
1833  ImportType importType)
1834 {
1835  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::setupAfterLoadReplacingImage"
1836  << " file=" << fileName.toLatin1().data()
1837  << " message=" << temporaryMessage.toLatin1().data()
1838  << " importType=" << importType;
1839 
1840  // The steps in this method should theoretically be just a subset of the steps in setupAfterLoadNewDocument
1841 
1842  // After this point there should be no commands in CmdMediator, since we effectively have a new document
1843  m_cmdMediator->clear();
1844 
1845  setPixmap (m_cmdMediator->document().curvesGraphsNames().first(),
1846  m_cmdMediator->pixmap ()); // Set background immediately so it is visible as a preview when any dialogs are displayed
1847 
1848  m_isDocumentExported = false;
1849 
1850  m_backgroundStateContext->setBackgroundImage (static_cast<BackgroundImage> (m_cmbBackground->currentIndex ()));
1851 
1852  applyZoomFactorAfterLoad(); // Zoom factor must be reapplied after background image is set, to have any effect
1853 
1854  // Some people prefer
1855  if (m_modelMainWindow.imageReplaceRenamesDocument()) {
1856  setCurrentFile(fileName);
1857  }
1858 
1859  m_statusBar->showTemporaryMessage (temporaryMessage);
1860  m_statusBar->wakeUp ();
1861 
1862  saveStartingDocumentSnapshot();
1863 
1864  updateAfterCommand(); // Replace stale points by points in new Document
1865 
1866  return true;
1867 }
1868 
1869 void MainWindow::showEvent (QShowEvent *event)
1870 {
1871  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::showEvent"
1872  << " files=" << m_loadStartupFiles.join (",").toLatin1().data();
1873 
1874  QMainWindow::showEvent (event);
1875 
1876  if (m_loadStartupFiles.count() > 0) {
1877 
1878  m_timerLoadStartupFiles = new QTimer;
1879  m_timerLoadStartupFiles->setSingleShot (true);
1880  connect (m_timerLoadStartupFiles, SIGNAL (timeout ()), this, SLOT (slotLoadStartupFiles ()));
1881  m_timerLoadStartupFiles->start (0); // Zero delay still waits until execution finishes and gui is available
1882 
1883  }
1884 }
1885 
1886 void MainWindow::showTemporaryMessage (const QString &temporaryMessage)
1887 {
1888  m_statusBar->showTemporaryMessage (temporaryMessage);
1889 }
1890 
1891 void MainWindow::slotBtnPrintAll ()
1892 {
1893  LOG4CPP_DEBUG_S ((*mainCat)) << "MainWindow::slotBtnPrintAll";
1894 
1895  ghostsCreate ();
1896 
1897  QPrinter printer (QPrinter::HighResolution);
1898  QPrintDialog dlg (&printer, this);
1899  if (dlg.exec() == QDialog::Accepted) {
1900  QPainter painter (&printer);
1901  m_view->render (&painter);
1902  painter.end();
1903  }
1904 
1905  ghostsDestroy ();
1906 }
1907 
1908 void MainWindow::slotBtnShowAllPressed ()
1909 {
1910  LOG4CPP_DEBUG_S ((*mainCat)) << "MainWindow::slotBtnShowAllPressed";
1911 
1912  // Start of press-release sequence
1913  ghostsCreate ();
1914 }
1915 
1916 void MainWindow::slotBtnShowAllReleased ()
1917 {
1918  LOG4CPP_DEBUG_S ((*mainCat)) << "MainWindow::slotBtnShowAllReleased";
1919 
1920  // End of press-release sequence
1921  ghostsDestroy ();
1922 }
1923 
1924 void MainWindow::slotCanRedoChanged (bool canRedo)
1925 {
1926  LOG4CPP_DEBUG_S ((*mainCat)) << "MainWindow::slotCanRedoChanged";
1927 
1928  m_actionEditRedo->setEnabled (canRedo || m_cmdStackShadow->canRedo());
1929 }
1930 
1931 void MainWindow::slotCanUndoChanged (bool canUndo)
1932 {
1933  LOG4CPP_DEBUG_S ((*mainCat)) << "MainWindow::slotCanUndoChanged";
1934 
1935  m_actionEditUndo->setEnabled (canUndo);
1936 }
1937 
1938 void MainWindow::slotChecklistClosed()
1939 {
1940  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotChecklistClosed";
1941 
1942  m_actionViewChecklistGuide->setChecked (false);
1943 }
1944 
1945 void MainWindow::slotCleanChanged(bool clean)
1946 {
1947  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotCleanChanged";
1948 
1949  setWindowModified (!clean);
1950 }
1951 
1952 void MainWindow::slotCmbBackground(int currentIndex)
1953 {
1954  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotCmbBackground";
1955 
1956  switch (currentIndex) {
1957  case BACKGROUND_IMAGE_NONE:
1958  if (!m_actionViewBackgroundNone->isChecked()) {
1959  m_actionViewBackgroundNone->toggle();
1960  }
1961  break;
1962 
1963  case BACKGROUND_IMAGE_ORIGINAL:
1964  if (!m_actionViewBackgroundOriginal->isChecked ()) {
1965  m_actionViewBackgroundOriginal->toggle();
1966  }
1967  break;
1968 
1969  case BACKGROUND_IMAGE_FILTERED:
1970  if (!m_actionViewBackgroundFiltered->isChecked ()) {
1971  m_actionViewBackgroundFiltered->toggle();
1972  }
1973  break;
1974  }
1975 
1976  m_backgroundStateContext->setBackgroundImage (static_cast<BackgroundImage> (currentIndex));
1977 }
1978 
1979 void MainWindow::slotCmbCoordSystem(int index)
1980 {
1981  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotCmbCoordSystem";
1982 
1983  CmdSelectCoordSystem *cmd = new CmdSelectCoordSystem (*this,
1984  m_cmdMediator->document(),
1985  static_cast<CoordSystemIndex> (index));
1986 
1987  m_cmdMediator->push (cmd);
1988 }
1989 
1990 void MainWindow::slotCmbCurve(int /* index */)
1991 {
1992  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotCmbCurve";
1993 
1994  m_backgroundStateContext->setCurveSelected (m_isGnuplot,
1995  m_transformation,
1996  m_cmdMediator->document().modelGridRemoval(),
1997  m_cmdMediator->document().modelColorFilter(),
1998  m_cmbCurve->currentText ());
1999  m_digitizeStateContext->handleCurveChange (m_cmdMediator);
2000  m_cmdMediator->setSelectedCurveName (m_cmbCurve->currentText ()); // Save for next time current coordinate system returns
2001 
2002  updateViewedCurves();
2004  updateFittingWindow();
2005  updateGeometryWindow();
2006 }
2007 
2008 void MainWindow::slotContextMenuEventAxis (QString pointIdentifier)
2009 {
2010  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotContextMenuEventAxis point=" << pointIdentifier.toLatin1 ().data ();
2011 
2012  m_digitizeStateContext->handleContextMenuEventAxis (m_cmdMediator,
2013  pointIdentifier);
2014 }
2015 
2016 void MainWindow::slotContextMenuEventGraph (QStringList pointIdentifiers)
2017 {
2018  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotContextMenuEventGraph point=" << pointIdentifiers.join(",").toLatin1 ().data ();
2019 
2020  m_digitizeStateContext->handleContextMenuEventGraph (m_cmdMediator,
2021  pointIdentifiers);
2022 }
2023 
2024 void MainWindow::slotDigitizeAxis ()
2025 {
2026  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotDigitizeAxis";
2027 
2028  m_digitizeStateContext->requestImmediateStateTransition (m_cmdMediator,
2029  DIGITIZE_STATE_AXIS);
2030  m_cmbCurve->setEnabled (false); // Graph curve is irrelevant in this mode
2031  m_viewPointStyle->setEnabled (true); // Point style is important in this mode
2032  m_viewSegmentFilter->setEnabled (true); // Filtering is important in this mode
2033  updateControls (); // For Paste which is state dependent
2034 }
2035 
2036 void MainWindow::slotDigitizeColorPicker ()
2037 {
2038  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotDigitizeColorPicker";
2039 
2040  m_digitizeStateContext->requestImmediateStateTransition (m_cmdMediator,
2041  DIGITIZE_STATE_COLOR_PICKER);
2042  m_cmbCurve->setEnabled (true);
2043  m_viewPointStyle->setEnabled (true);
2044  m_viewSegmentFilter->setEnabled (true);
2045  updateControls (); // For Paste which is state dependent
2046 }
2047 
2048 void MainWindow::slotDigitizeCurve ()
2049 {
2050  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotDigitizeCurve";
2051 
2052  m_digitizeStateContext->requestImmediateStateTransition (m_cmdMediator,
2053  DIGITIZE_STATE_CURVE);
2054  m_cmbCurve->setEnabled (true);
2055  m_viewPointStyle->setEnabled (true);
2056  m_viewSegmentFilter->setEnabled (true);
2057  updateControls (); // For Paste which is state dependent
2058 }
2059 
2060 void MainWindow::slotDigitizePointMatch ()
2061 {
2062  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotDigitizePointMatch";
2063 
2064  m_digitizeStateContext->requestImmediateStateTransition (m_cmdMediator,
2065  DIGITIZE_STATE_POINT_MATCH);
2066  m_cmbCurve->setEnabled (true);
2067  m_viewPointStyle->setEnabled (true);
2068  m_viewSegmentFilter->setEnabled (true);
2069  updateControls (); // For Paste which is state dependent
2070 }
2071 
2072 void MainWindow::slotDigitizeScale ()
2073 {
2074  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotDigitizeScale";
2075 
2076  m_digitizeStateContext->requestImmediateStateTransition (m_cmdMediator,
2077  DIGITIZE_STATE_SCALE);
2078  m_cmbCurve->setEnabled (false);
2079  m_viewPointStyle->setEnabled (false);
2080  m_viewSegmentFilter->setEnabled (false);
2081  updateControls (); // For Paste which is state dependent
2082 }
2083 
2084 void MainWindow::slotDigitizeSegment ()
2085 {
2086  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotDigitizeSegment";
2087 
2088  m_digitizeStateContext->requestImmediateStateTransition (m_cmdMediator,
2089  DIGITIZE_STATE_SEGMENT);
2090  m_cmbCurve->setEnabled (true);
2091  m_viewPointStyle->setEnabled (true);
2092  m_viewSegmentFilter->setEnabled (true);
2093  updateControls (); // For Paste which is state dependent
2094 }
2095 
2096 void MainWindow::slotDigitizeSelect ()
2097 {
2098  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotDigitizeSelect";
2099 
2100  m_digitizeStateContext->requestImmediateStateTransition (m_cmdMediator,
2101  DIGITIZE_STATE_SELECT);
2102  m_cmbCurve->setEnabled (false);
2103  m_viewPointStyle->setEnabled (false);
2104  m_viewSegmentFilter->setEnabled (false);
2105  updateControls (); // For Paste which is state dependent
2106 }
2107 
2108 void MainWindow::slotEditCopy ()
2109 {
2110  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotEditCopy";
2111 
2112  // Copy command is sent to FittingWindow or GeometryWindow, or processed locally
2113  bool tableFittingIsActive, tableFittingIsCopyable;
2114  bool tableGeometryIsActive, tableGeometryIsCopyable;
2115  m_dockFittingWindow->getTableStatus (tableFittingIsActive, tableFittingIsCopyable); // Fitting window status
2116  m_dockGeometryWindow->getTableStatus (tableGeometryIsActive, tableGeometryIsCopyable); // Geometry window status
2117 
2118  if (tableFittingIsActive) {
2119 
2120  // Send to FittingWindow
2121  m_dockFittingWindow->doCopy ();
2122 
2123  } else if (tableGeometryIsActive) {
2124 
2125  // Send to GeometryWindow
2126  m_dockGeometryWindow->doCopy ();
2127 
2128  } else {
2129 
2130  // Process curve points in main window
2131  GraphicsItemsExtractor graphicsItemsExtractor;
2132  const QList<QGraphicsItem*> &items = m_scene->selectedItems();
2133  QStringList pointIdentifiers = graphicsItemsExtractor.selectedPointIdentifiers (items);
2134 
2135  CmdCopy *cmd = new CmdCopy (*this,
2136  m_cmdMediator->document(),
2137  pointIdentifiers);
2138  m_digitizeStateContext->appendNewCmd (m_cmdMediator,
2139  cmd);
2140  }
2141 }
2142 
2143 void MainWindow::slotEditCut ()
2144 {
2145  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotEditCut";
2146 
2147  // Copy command is sent to FittingWindow or GeometryWindow, or processed locally
2148  bool tableFittingIsActive, tableFittingIsCopyable;
2149  bool tableGeometryIsActive, tableGeometryIsCopyable;
2150  m_dockFittingWindow->getTableStatus (tableFittingIsActive, tableFittingIsCopyable); // Fitting window status
2151  m_dockGeometryWindow->getTableStatus (tableGeometryIsActive, tableGeometryIsCopyable); // Geometry window status
2152 
2153  if (tableFittingIsActive || tableGeometryIsActive) {
2154 
2155  // Cannot delete from fitting or geometry windows
2156 
2157  } else {
2158 
2159  // Process curve points in main window
2160  GraphicsItemsExtractor graphicsItemsExtractor;
2161  const QList<QGraphicsItem*> &items = m_scene->selectedItems();
2162  QStringList pointIdentifiers = graphicsItemsExtractor.selectedPointIdentifiers (items);
2163 
2164  CmdCut *cmd = new CmdCut (*this,
2165  m_cmdMediator->document(),
2166  pointIdentifiers);
2167  m_digitizeStateContext->appendNewCmd (m_cmdMediator,
2168  cmd);
2169  }
2170 }
2171 
2172 void MainWindow::slotEditDelete ()
2173 {
2174  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotEditDelete";
2175 
2176  // Copy command is sent to FittingWindow or GeometryWindow, or processed locally
2177  bool tableFittingIsActive, tableFittingIsCopyable;
2178  bool tableGeometryIsActive, tableGeometryIsCopyable;
2179  m_dockFittingWindow->getTableStatus (tableFittingIsActive, tableFittingIsCopyable); // Fitting window status
2180  m_dockGeometryWindow->getTableStatus (tableGeometryIsActive, tableGeometryIsCopyable); // Geometry window status
2181 
2182  if (tableFittingIsActive || tableGeometryIsActive) {
2183 
2184  // Cannot delete from fitting or geometry windows
2185 
2186  } else {
2187 
2188  // If this is a map, which has a scale bar with two axis points, then selection of just one axis point
2189  // for deletion should result in deletion of the other point also so this object will enforce that. Otherwise
2190  // this class has no effect below
2191  ScaleBarAxisPointsUnite scaleBarAxisPoints;
2192 
2193  // Process curve points in main window
2194  GraphicsItemsExtractor graphicsItemsExtractor;
2195  const QList<QGraphicsItem*> &items = m_scene->selectedItems();
2196  QStringList pointIdentifiers = scaleBarAxisPoints.unite (m_cmdMediator,
2197  graphicsItemsExtractor.selectedPointIdentifiers (items));
2198 
2199  CmdDelete *cmd = new CmdDelete (*this,
2200  m_cmdMediator->document(),
2201  pointIdentifiers);
2202  m_digitizeStateContext->appendNewCmd (m_cmdMediator,
2203  cmd);
2204  }
2205 }
2206 
2207 void MainWindow::slotEditMenu ()
2208 {
2209  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotEditMenu";
2210 
2211  m_actionEditPasteAsNew->setEnabled (!QApplication::clipboard()->image().isNull());
2212  m_actionEditPasteAsNewAdvanced->setEnabled (!QApplication::clipboard()->image().isNull());
2213 }
2214 
2215 void MainWindow::slotEditPaste ()
2216 {
2217  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotEditPaste";
2218 
2219  QList<QPoint> points;
2220  QList<double> ordinals;
2221 
2222  MimePointsImport mimePointsImport;
2223  mimePointsImport.retrievePoints (m_transformation,
2224  points,
2225  ordinals);
2226 
2227  CmdAddPointsGraph *cmd = new CmdAddPointsGraph (*this,
2228  m_cmdMediator->document(),
2229  m_cmbCurve->currentText (),
2230  points,
2231  ordinals);
2232  m_digitizeStateContext->appendNewCmd (m_cmdMediator,
2233  cmd);
2234 }
2235 
2236 void MainWindow::slotEditPasteAsNew ()
2237 {
2238  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotEditPasteAsNew";
2239 
2240  filePaste (IMPORT_TYPE_SIMPLE);
2241 }
2242 
2243 void MainWindow::slotEditPasteAsNewAdvanced ()
2244 {
2245  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotEditPasteAsNewAdvanced";
2246 
2247  filePaste (IMPORT_TYPE_ADVANCED);
2248 }
2249 
2250 void MainWindow::slotFileClose()
2251 {
2252  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFileClose";
2253 
2254  if (maybeSave ()) {
2255 
2256  // Transition from defined to undefined. This must be after the clearing of the screen
2257  // since the axes checker screen item (and maybe others) must still exist
2258  m_transformationStateContext->triggerStateTransition(m_isGnuplot,
2259  TRANSFORMATION_STATE_UNDEFINED,
2260  *m_cmdMediator,
2261  m_transformation,
2262  selectedGraphCurve());
2263 
2264  // Transition to empty state so an inadvertent mouse press does not trigger, for example,
2265  // the creation of an axis point on a non-existent GraphicsScene (=crash)
2266  m_digitizeStateContext->requestImmediateStateTransition (m_cmdMediator,
2267  DIGITIZE_STATE_EMPTY);
2268 
2269  // Deallocate fitted curve
2270  if (m_fittingCurve != nullptr) {
2271  m_scene->removeItem (m_fittingCurve);
2272  m_fittingCurve = nullptr;
2273  }
2274 
2275  // Remove screen objects
2276  m_scene->resetOnLoad ();
2277 
2278  // Remove background
2279  m_backgroundStateContext->close ();
2280 
2281  // Remove scroll bars if they exist
2282  m_scene->setSceneRect (QRectF (0, 0, 1, 1));
2283 
2284  // Remove stale data from fitting window
2285  m_dockFittingWindow->clear ();
2286 
2287  // Remove stale data from geometry window
2288  m_dockGeometryWindow->clear ();
2289 
2290  // Deallocate Document
2291  delete m_cmdMediator;
2292 
2293  // Remove file information
2294  m_cmdMediator = nullptr;
2295  m_currentFile = "";
2296  m_engaugeFile = "";
2297  setWindowTitle (engaugeWindowTitle ());
2298 
2299  m_gridLines.clear();
2300  updateControls();
2301  }
2302 }
2303 
2304 void MainWindow::slotFileExport ()
2305 {
2306  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFileExport";
2307 
2308  if (m_transformation.transformIsDefined()) {
2309 
2310  MainDirectoryPersist directoryPersist;
2311  ExportToFile exportStrategy;
2312 
2313  QString fileName;
2314  if (m_isExportOnly) {
2315  fileName = fileNameForExportOnly ();
2316  } else {
2317 
2318  QString filter = QString ("%1;;%2;;All files (*.*)")
2319  .arg (exportStrategy.filterCsv ())
2320  .arg (exportStrategy.filterTsv ());
2321 
2322  // OSX sandbox requires, for the default, a non-empty filename
2323  QString defaultFileName = QString ("%1/%2.%3")
2324  .arg (directoryPersist.getDirectoryExportSave().path ())
2325  .arg (m_currentFile)
2326  .arg (exportStrategy.fileExtensionCsv ());
2327  QFileDialog dlg;
2328  QString filterCsv = exportStrategy.filterCsv ();
2329 
2330  fileName = dlg.getSaveFileName (this,
2331  tr("Export"),
2332  defaultFileName,
2333  filter,
2334  &filterCsv);
2335  }
2336 
2337  if (!fileName.isEmpty ()) {
2338 
2339  directoryPersist.setDirectoryExportSaveFromFilename(fileName);
2340  fileExport(fileName,
2341  exportStrategy);
2342  }
2343  } else {
2344  DlgRequiresTransform dlg ("Export");
2345  dlg.exec ();
2346  }
2347 }
2348 
2349 void MainWindow::slotFileImport ()
2350 {
2351  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFileImport";
2352 
2353  fileImportWithPrompts (IMPORT_TYPE_SIMPLE);
2354 }
2355 
2356 void MainWindow::slotFileImportAdvanced ()
2357 {
2358  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFileImportAdvanced";
2359 
2360  fileImportWithPrompts (IMPORT_TYPE_ADVANCED);
2361 }
2362 
2363 void MainWindow::slotFileImportDraggedImage(QImage image)
2364 {
2365  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFileImportDraggedImage";
2366 
2367  // No need to check return value from loadImage since there are no prompts that give the user a chance to cancel
2368  loadImage ("",
2369  image,
2370  IMPORT_TYPE_SIMPLE);
2371 }
2372 
2373 void MainWindow::slotFileImportDraggedImageUrl(QUrl url)
2374 {
2375  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFileImportDraggedImageUrl url=" << url.toString ().toLatin1 ().data ();
2376 
2377  // This is required for drag and drop from GraphicsView. This had an #ifdef
2378  // around it for NETWORKING but restored for drag and drop
2379  m_loadImageFromUrl->startLoadImage (url);
2380 }
2381 
2382 void MainWindow::slotFileImportImage(QString fileName, QImage image)
2383 {
2384  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFileImportImage fileName=" << fileName.toLatin1 ().data ();
2385 
2386  // No need to check return value from loadImage since there are no prompts that give the user a chance to cancel
2387  loadImage (fileName,
2388  image,
2389  IMPORT_TYPE_SIMPLE);
2390 }
2391 
2392 void MainWindow::slotFileImportImageReplace ()
2393 {
2394  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFileImportImageReplace";
2395 
2396  fileImportWithPrompts (IMPORT_TYPE_IMAGE_REPLACE);
2397 }
2398 
2399 void MainWindow::slotFileOpen()
2400 {
2401  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFileOpen";
2402 
2403  if (maybeSave ()) {
2404 
2405  // Allow selection of files with strange suffixes in case the file extension was changed. Since
2406  // the default is the first filter, the wildcard filter is added afterwards (it is the off-nominal case)
2407  QString filter = QString ("%1 (*.%2);; All Files (*.*)")
2408  .arg (ENGAUGE_FILENAME_DESCRIPTION)
2409  .arg (ENGAUGE_FILENAME_EXTENSION);
2410 
2411  MainDirectoryPersist directoryPersist;
2412  QString fileName = QFileDialog::getOpenFileName (this,
2413  tr("Open Document"),
2414  directoryPersist.getDirectoryImportOpen ().path (),
2415  filter);
2416  if (!fileName.isEmpty ()) {
2417 
2418  directoryPersist.setDirectoryImportOpenFromFilename (fileName);
2419  loadDocumentFile (fileName);
2420 
2421  }
2422  }
2423 }
2424 
2425 void MainWindow::slotFileOpenDraggedDigFile (QString fileName)
2426 {
2427  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFileOpenDraggedDigFile";
2428 
2429  loadDocumentFile (fileName);
2430 }
2431 
2432 void MainWindow::slotFilePrint()
2433 {
2434  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFilePrint";
2435 
2436  QPrinter printer (QPrinter::HighResolution);
2437  QPrintDialog dlg (&printer, this);
2438  if (dlg.exec() == QDialog::Accepted) {
2439  QPainter painter (&printer);
2440  m_view->render (&painter);
2441  painter.end();
2442  }
2443 }
2444 
2445 bool MainWindow::slotFileSave()
2446 {
2447  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFileSave";
2448 
2449  if (m_engaugeFile.isEmpty()) {
2450  return slotFileSaveAs();
2451  } else {
2452  return saveDocumentFile (m_engaugeFile);
2453  }
2454 }
2455 
2456 bool MainWindow::slotFileSaveAs()
2457 {
2458  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFileSaveAs";
2459 
2460  // Append engauge file extension if it is not already there
2461  QString filenameDefault = m_currentFile;
2462  if (!m_currentFile.endsWith (ENGAUGE_FILENAME_EXTENSION)) {
2463  filenameDefault = QString ("%1.%2")
2464  .arg (m_currentFile)
2465  .arg (ENGAUGE_FILENAME_EXTENSION);
2466  }
2467 
2468  if (!m_engaugeFile.isEmpty()) {
2469  filenameDefault = m_engaugeFile;
2470  }
2471 
2472  QString filterDigitizer = QString ("%1 (*.%2)")
2473  .arg (ENGAUGE_FILENAME_DESCRIPTION)
2474  .arg (ENGAUGE_FILENAME_EXTENSION);
2475  QString filterAll ("All files (*. *)");
2476 
2477  QStringList filters;
2478  filters << filterDigitizer;
2479  filters << filterAll;
2480 
2481  MainDirectoryPersist directoryPersist;
2482 
2483  QFileDialog dlg(this);
2484  dlg.setFileMode (QFileDialog::AnyFile);
2485  dlg.selectNameFilter (filterDigitizer);
2486  dlg.setNameFilters (filters);
2487 #if !defined(OSX_DEBUG) && !defined(OSX_RELEASE)
2488  // Prevent hang in OSX
2489  dlg.setWindowModality(Qt::WindowModal);
2490 #endif
2491  dlg.setAcceptMode(QFileDialog::AcceptSave);
2492  dlg.selectFile(filenameDefault);
2493  dlg.setDirectory (directoryPersist.getDirectoryExportSave ());
2494  if (dlg.exec()) {
2495 
2496  QStringList files = dlg.selectedFiles();
2497  directoryPersist.setDirectoryExportSaveFromFilename (files.at(0));
2498  return saveDocumentFile(files.at(0));
2499  }
2500 
2501  return false;
2502 }
2503 
2504 void MainWindow::slotFittingWindowClosed()
2505 {
2506  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFittingWindowClosed";
2507 
2508  m_actionViewFittingWindow->setChecked (false);
2509 }
2510 
2511 void MainWindow::slotFittingWindowCurveFit(FittingCurveCoefficients fittingCurveCoef,
2512  double xMin,
2513  double xMax,
2514  bool isLogXTheta,
2515  bool isLogYRadius)
2516 {
2517  // Do not output elements in fittingCurveCoef here since that list may be empty
2518  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotFittingWindowCurveFit"
2519  << " order=" << fittingCurveCoef.size() - 1;
2520 
2521  if (m_fittingCurve != nullptr) {
2522  m_scene->removeItem (m_fittingCurve);
2523  delete m_fittingCurve;
2524  }
2525 
2526  m_fittingCurve = new FittingCurve (fittingCurveCoef,
2527  xMin,
2528  xMax,
2529  isLogXTheta,
2530  isLogYRadius,
2531  m_transformation);
2532  m_fittingCurve->setVisible (m_actionViewFittingWindow->isChecked ());
2533  m_scene->addItem (m_fittingCurve);
2534 }
2535 
2536 void MainWindow::slotGeometryWindowClosed()
2537 {
2538  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotGeometryWindowClosed";
2539 
2540  m_actionViewGeometryWindow->setChecked (false);
2541 }
2542 
2543 void MainWindow::slotHelpAbout()
2544 {
2545  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotHelpAbout";
2546 
2547  DlgAbout dlg (*this);
2548  dlg.exec ();
2549 }
2550 
2551 void MainWindow::slotHelpTutorial()
2552 {
2553  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotHelpTutorial";
2554 
2555  m_tutorialDlg->show ();
2556  m_tutorialDlg->exec ();
2557 }
2558 
2559 void MainWindow::slotKeyPress (Qt::Key key,
2560  bool atLeastOneSelectedItem)
2561 {
2562  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotKeyPress"
2563  << " key=" << QKeySequence (key).toString().toLatin1 ().data ()
2564  << " atLeastOneSelectedItem=" << (atLeastOneSelectedItem ? "true" : "false");
2565 
2566  m_digitizeStateContext->handleKeyPress (m_cmdMediator,
2567  key,
2568  atLeastOneSelectedItem);
2569 }
2570 
2571 void MainWindow::slotLoadStartupFiles ()
2572 {
2573  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotLoadStartupFiles";
2574 
2575  ENGAUGE_ASSERT (m_loadStartupFiles.count() > 0);
2576 
2577  QString fileName = m_loadStartupFiles.front(); // Get next file name
2578  m_loadStartupFiles.pop_front(); // Remove next file name
2579 
2580  // Load next file into this instance of Engauge
2581  LoadFileInfo loadFileInfo;
2582  if (loadFileInfo.loadsAsDigFile(fileName)) {
2583 
2584  loadDocumentFile (fileName);
2585 
2586  } else {
2587 
2588  fileImport (fileName,
2589  IMPORT_TYPE_SIMPLE);
2590 
2591  }
2592 
2593  if (m_loadStartupFiles.count() > 0) {
2594 
2595  // Fork off another instance of this application to handle the remaining files recursively. New process
2596  // is detached so killing/terminating this process does not automatically kill the child process(es) also
2597  QProcess::startDetached (QCoreApplication::applicationFilePath(),
2598  m_commandLineWithoutLoadStartupFiles + m_loadStartupFiles);
2599  }
2600 }
2601 
2602 void MainWindow::slotMouseMove (QPointF pos)
2603 {
2604 // LOG4CPP_DEBUG_S ((*mainCat)) << "MainWindow::slotMouseMove pos=" << QPointFToString (pos).toLatin1 ().data ();
2605 
2606  // Ignore mouse moves before Document is loaded
2607  if (m_cmdMediator != nullptr) {
2608 
2609  // Get status bar coordinates
2610  QString coordsScreen, coordsGraph, resolutionGraph;
2611  m_transformation.coordTextForStatusBar (pos,
2612  coordsScreen,
2613  coordsGraph,
2614  resolutionGraph,
2615  modeMap ());
2616 
2617  // Update status bar coordinates
2618  m_statusBar->setCoordinates (coordsScreen,
2619  coordsGraph,
2620  resolutionGraph);
2621 
2622  // There used to be a call to updateGraphicsLinesToMatchGraphicsPoints here, but that resulted
2623  // in hundreds of gratuitous log messages as the cursor was moved around, and nothing important happened
2624 
2625  m_digitizeStateContext->handleMouseMove (m_cmdMediator,
2626  pos);
2627  }
2628 }
2629 
2630 void MainWindow::slotMousePress (QPointF pos)
2631 {
2632  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotMousePress";
2633 
2634  m_scene->resetPositionHasChangedFlags();
2635 
2636  m_digitizeStateContext->handleMousePress (m_cmdMediator,
2637  pos);
2638 }
2639 
2640 void MainWindow::slotMouseRelease (QPointF pos)
2641 {
2642  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotMouseRelease";
2643 
2644  if (pos.x() < 0 || pos.y() < 0) {
2645 
2646  // Cursor is outside the image so drop this event. However, call updateControls since this may be
2647  // a click-and-drag to select in which case the controls (especially Copy and Cut) reflect the new selection
2648  updateControls ();
2649 
2650  } else {
2651 
2652  // Cursor is within the image so process this as a normal mouse release
2653  m_digitizeStateContext->handleMouseRelease (m_cmdMediator,
2654  pos);
2655  }
2656 }
2657 
2658 void MainWindow::slotRecentFileAction ()
2659 {
2660  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotRecentFileAction";
2661 
2662  QAction *action = qobject_cast<QAction*>(sender ());
2663 
2664  if (action) {
2665  QString fileName = action->data().toString();
2666  loadDocumentFile (fileName);
2667  }
2668 }
2669 
2670 void MainWindow::slotRecentFileClear ()
2671 {
2672  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotRecentFileClear";
2673 
2674  QStringList emptyList;
2675 
2676  QSettings settings (SETTINGS_ENGAUGE, SETTINGS_DIGITIZER);
2677  settings.setValue (SETTINGS_RECENT_FILE_LIST,
2678  emptyList);
2679 
2680  updateRecentFileList();
2681 }
2682 
2683 void MainWindow::slotRedoTextChanged (const QString &text)
2684 {
2685  LOG4CPP_DEBUG_S ((*mainCat)) << "MainWindow::slotRedoTextChanged";
2686 
2687  QString completeText ("Redo");
2688  if (!text.isEmpty ()) {
2689  completeText += QString (" \"%1\"").arg (text);
2690  }
2691  m_actionEditRedo->setText (completeText);
2692 }
2693 
2694 void MainWindow::slotSettingsAxesChecker ()
2695 {
2696  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotSettingsAxesChecker";
2697 
2698  m_dlgSettingsAxesChecker->load (*m_cmdMediator);
2699  m_dlgSettingsAxesChecker->show ();
2700 }
2701 
2702 void MainWindow::slotSettingsColorFilter ()
2703 {
2704  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotSettingsColorFilter";
2705 
2706  m_dlgSettingsColorFilter->load (*m_cmdMediator);
2707  m_dlgSettingsColorFilter->show ();
2708 }
2709 
2710 void MainWindow::slotSettingsCoords ()
2711 {
2712  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotSettingsCoords";
2713 
2714  m_dlgSettingsCoords->load (*m_cmdMediator);
2715  m_dlgSettingsCoords->show ();
2716 }
2717 
2718 void MainWindow::slotSettingsCurveList ()
2719 {
2720  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotSettingsCurveList";
2721 
2722  m_dlgSettingsCurveList->load (*m_cmdMediator);
2723  m_dlgSettingsCurveList->show ();
2724 }
2725 
2726 void MainWindow::slotSettingsCurveProperties ()
2727 {
2728  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotSettingsCurveProperties";
2729 
2730  m_dlgSettingsCurveProperties->load (*m_cmdMediator);
2731  m_dlgSettingsCurveProperties->setCurveName (selectedGraphCurve ());
2732  m_dlgSettingsCurveProperties->show ();
2733 }
2734 
2735 void MainWindow::slotSettingsDigitizeCurve ()
2736 {
2737  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotSettingsDigitizeCurve";
2738 
2739  m_dlgSettingsDigitizeCurve->load (*m_cmdMediator);
2740  m_dlgSettingsDigitizeCurve->show ();
2741 }
2742 
2743 void MainWindow::slotSettingsExportFormat ()
2744 {
2745  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotSettingsExportFormat";
2746 
2747  if (transformIsDefined()) {
2748  m_dlgSettingsExportFormat->load (*m_cmdMediator);
2749  m_dlgSettingsExportFormat->show ();
2750  } else {
2751  DlgRequiresTransform dlg ("Export settings");
2752  dlg.exec();
2753  }
2754 }
2755 
2756 void MainWindow::slotSettingsGeneral ()
2757 {
2758  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotSettingsGeneral";
2759 
2760  m_dlgSettingsGeneral->load (*m_cmdMediator);
2761  m_dlgSettingsGeneral->show ();
2762 }
2763 
2764 void MainWindow::slotSettingsGridDisplay()
2765 {
2766  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotSettingsGridDisplay";
2767 
2768  m_dlgSettingsGridDisplay->load (*m_cmdMediator);
2769  m_dlgSettingsGridDisplay->show ();
2770 }
2771 
2772 void MainWindow::slotSettingsGridRemoval ()
2773 {
2774  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotSettingsGridRemoval";
2775 
2776  m_dlgSettingsGridRemoval->load (*m_cmdMediator);
2777  m_dlgSettingsGridRemoval->show ();
2778 }
2779 
2780 void MainWindow::slotSettingsPointMatch ()
2781 {
2782  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotSettingsPointMatch";
2783 
2784  m_dlgSettingsPointMatch->load (*m_cmdMediator);
2785  m_dlgSettingsPointMatch->show ();
2786 }
2787 
2788 void MainWindow::slotSettingsSegments ()
2789 {
2790  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotSettingsSegments";
2791 
2792  m_dlgSettingsSegments->load (*m_cmdMediator);
2793  m_dlgSettingsSegments->show ();
2794 }
2795 
2796 void MainWindow::slotTableStatusChange ()
2797 {
2798  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotTableStatusChange";
2799 
2800  // This slot is called when either window in FittingWindow or GeometryWindow loses/gains focus. This is
2801  // so the Copy menu item can be updated
2802  updateControls ();
2803 }
2804 
2805 void MainWindow::slotSettingsMainWindow ()
2806 {
2807  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotSettingsMainWindow";
2808 
2809  m_dlgSettingsMainWindow->loadMainWindowModel (*m_cmdMediator,
2810  m_modelMainWindow);
2811  m_dlgSettingsMainWindow->show ();
2812 }
2813 
2814 void MainWindow::slotTimeoutRegressionErrorReport ()
2815 {
2816  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotTimeoutRegressionErrorReport"
2817  << " cmdStackIndex=" << m_cmdMediator->index()
2818  << " cmdStackCount=" << m_cmdMediator->count();
2819 
2820  if (m_cmdStackShadow->canRedo()) {
2821 
2822  // Always reset current directory before the command. This guarantees the upcoming redo step will work
2823  QDir::setCurrent (m_startupDirectory);
2824 
2825  m_cmdStackShadow->slotRedo();
2826 
2827  // Always reset current directory after the command. This guarantees the final export to file will work
2828  QDir::setCurrent (m_startupDirectory);
2829 
2830  } else {
2831 
2832 #if !defined(OSX_DEBUG) && !defined(OSX_RELEASE)
2833  exportAllCoordinateSystemsAfterRegressionTests ();
2834 #endif
2835 
2836  // Regression test has finished so exit. We unset the dirty flag so there is no prompt
2837  m_cmdMediator->setClean();
2838  close();
2839 
2840  }
2841 }
2842 
2843 void MainWindow::slotTimeoutRegressionFileCmdScript ()
2844 {
2845  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotTimeoutRegressionFileCmdScript";
2846 
2847  if (m_fileCmdScript->canRedo()) {
2848 
2849  // Always reset current directory before the command. This guarantees the upcoming redo step will work
2850  QDir::setCurrent (m_startupDirectory);
2851 
2852  m_fileCmdScript->redo(*this);
2853 
2854  // Always reset current directory after the command. This guarantees the final export to file will work
2855  QDir::setCurrent (m_startupDirectory);
2856 
2857  } else {
2858 
2859  // Script file might already have closed the Document so export only if last was not closed
2860  if (m_cmdMediator != nullptr) {
2861 
2862 #if !defined(OSX_DEBUG) && !defined(OSX_RELEASE)
2863  exportAllCoordinateSystemsAfterRegressionTests ();
2864 #endif
2865 
2866  // We unset the dirty flag so there is no "Save changes?" prompt
2867  m_cmdMediator->setClean();
2868 
2869  }
2870 
2871  // Regression test has finished so exit
2872  close();
2873 
2874  }
2875 }
2876 
2877 void MainWindow::slotUndoTextChanged (const QString &text)
2878 {
2879  LOG4CPP_DEBUG_S ((*mainCat)) << "MainWindow::slotUndoTextChanged";
2880 
2881  QString completeText ("Undo");
2882  if (!text.isEmpty ()) {
2883  completeText += QString (" \"%1\"").arg (text);
2884  }
2885  m_actionEditUndo->setText (completeText);
2886 }
2887 
2888 void MainWindow::slotViewGridLines ()
2889 {
2890  LOG4CPP_DEBUG_S ((*mainCat)) << "MainWindow::slotViewGridLines";
2891 
2892  updateGridLines ();
2893 }
2894 
2895 void MainWindow::slotViewGroupBackground(QAction *action)
2896 {
2897  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewGroupBackground";
2898 
2899  // Set the combobox
2900  BackgroundImage backgroundImage;
2901  int indexBackground;
2902  if (action == m_actionViewBackgroundNone) {
2903  indexBackground = m_cmbBackground->findData (QVariant (BACKGROUND_IMAGE_NONE));
2904  backgroundImage = BACKGROUND_IMAGE_NONE;
2905  } else if (action == m_actionViewBackgroundOriginal) {
2906  indexBackground = m_cmbBackground->findData (QVariant (BACKGROUND_IMAGE_ORIGINAL));
2907  backgroundImage = BACKGROUND_IMAGE_ORIGINAL;
2908  } else if (action == m_actionViewBackgroundFiltered) {
2909  indexBackground = m_cmbBackground->findData (QVariant (BACKGROUND_IMAGE_FILTERED));
2910  backgroundImage = BACKGROUND_IMAGE_FILTERED;
2911  } else {
2912  ENGAUGE_ASSERT (false);
2913 
2914  // Defaults if assert is disabled so execution continues
2915  indexBackground = m_cmbBackground->findData (QVariant (BACKGROUND_IMAGE_ORIGINAL));
2916  backgroundImage = BACKGROUND_IMAGE_ORIGINAL;
2917  }
2918 
2919  m_cmbBackground->setCurrentIndex (indexBackground);
2920  m_backgroundStateContext->setBackgroundImage (backgroundImage);
2921 }
2922 
2923 void MainWindow::slotViewGroupCurves(QAction * /* action */)
2924 {
2925  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewGroupCurves";
2926 
2927  updateViewedCurves ();
2928 }
2929 
2930 void MainWindow::slotViewGroupStatus(QAction *action)
2931 {
2932  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewGroupStatus";
2933 
2934  ENGAUGE_CHECK_PTR (m_statusBar); // At startup, make sure status bar is already set up when View menu gets initialized
2935 
2936  if (action == m_actionStatusNever) {
2937  m_statusBar->setStatusBarMode(STATUS_BAR_MODE_NEVER);
2938  } else if (action == m_actionStatusTemporary) {
2939  m_statusBar->setStatusBarMode(STATUS_BAR_MODE_TEMPORARY);
2940  } else {
2941  m_statusBar->setStatusBarMode(STATUS_BAR_MODE_ALWAYS);
2942  }
2943 }
2944 
2945 void MainWindow::slotViewToolBarBackground ()
2946 {
2947  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewToolBarBackground";
2948 
2949  if (m_actionViewBackground->isChecked ()) {
2950  m_toolBackground->show();
2951  } else {
2952  m_toolBackground->hide();
2953  }
2954 }
2955 
2956 void MainWindow::slotViewToolBarChecklistGuide ()
2957 {
2958  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewToolBarChecklistGuide";
2959 
2960  if (m_actionViewChecklistGuide->isChecked ()) {
2961  m_dockChecklistGuide->show();
2962  } else {
2963  m_dockChecklistGuide->hide();
2964  }
2965 }
2966 
2967 void MainWindow::slotViewToolBarCoordSystem ()
2968 {
2969  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewToolBarCoordSystem";
2970 
2971  if (m_actionViewCoordSystem->isChecked ()) {
2972  m_toolCoordSystem->show();
2973  } else {
2974  m_toolCoordSystem->hide();
2975  }
2976 }
2977 
2978 void MainWindow::slotViewToolBarDigitize ()
2979 {
2980  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewToolBarDigitize";
2981 
2982  if (m_actionViewDigitize->isChecked ()) {
2983  m_toolDigitize->show();
2984  } else {
2985  m_toolDigitize->hide();
2986  }
2987 }
2988 
2989 void MainWindow::slotViewToolBarFittingWindow()
2990 {
2991  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewToolBarFittingWindow";
2992 
2993  if (m_actionViewFittingWindow->isChecked()) {
2994  m_dockFittingWindow->show ();
2995  if (m_fittingCurve != nullptr) {
2996  m_fittingCurve->setVisible (true);
2997  }
2998  } else {
2999  m_dockFittingWindow->hide ();
3000  if (m_fittingCurve != nullptr) {
3001  m_fittingCurve->setVisible (false);
3002  }
3003  }
3004 }
3005 
3006 void MainWindow::slotViewToolBarGeometryWindow ()
3007 {
3008  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewToolBarGeometryWindow";
3009 
3010  if (m_actionViewGeometryWindow->isChecked ()) {
3011  m_dockGeometryWindow->show();
3012  } else {
3013  m_dockGeometryWindow->hide();
3014  }
3015 }
3016 
3017 void MainWindow::slotViewToolBarSettingsViews ()
3018 {
3019  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewToolBarSettingsViews";
3020 
3021  if (m_actionViewSettingsViews->isChecked ()) {
3022  m_toolSettingsViews->show();
3023  } else {
3024  m_toolSettingsViews->hide();
3025  }
3026 }
3027 
3028 void MainWindow::slotViewToolTips ()
3029 {
3030  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewToolTips";
3031 
3032  loadToolTips();
3033 }
3034 
3035 void MainWindow::slotViewZoom (int zoom)
3036 {
3037  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewZoom";
3038 
3039  // Update zoom controls and apply the zoom factor
3040  ZoomFactor zoomFactor = static_cast<ZoomFactor> (zoom);
3041  m_zoomMapToAction [zoomFactor]->setChecked (true);
3042  slotViewZoomFactor (static_cast<ZoomFactor> (zoom));
3043 }
3044 
3045 void MainWindow::slotViewZoomFactor (ZoomFactor zoomFactor)
3046 {
3047  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewZoomFactor";
3048 
3049  if (zoomFactor == ZOOM_FILL) {
3050  m_backgroundStateContext->fitInView (*m_view);
3051  } else {
3052 
3053  ZoomTransition zoomTransition;
3054  double factor = zoomTransition.mapToFactor (zoomFactor);
3055 
3056  QTransform transform;
3057  transform.scale (factor, factor);
3058  m_view->setTransform (transform);
3059  }
3060 
3061  emit signalZoom(zoomFactor);
3062 }
3063 
3064 void MainWindow::slotViewZoomFactorInt (int zoom)
3065 {
3066  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewZoomFactorInt";
3067 
3068  slotViewZoomFactor (static_cast<ZoomFactor> (zoom));
3069 }
3070 
3071 void MainWindow::slotViewZoomIn ()
3072 {
3073  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewZoomIn";
3074 
3075  ZoomTransition zoomTransition;
3076  ZoomFactor zoomFactorNew = zoomTransition.zoomIn (currentZoomFactor (),
3077  m_view->transform ().m11 (),
3078  m_view->transform ().m22 (),
3079  m_actionZoomFill->isChecked ());
3080  setNonFillZoomFactor (zoomFactorNew);
3081 }
3082 
3083 
3084 void MainWindow::slotViewZoomInFromWheelEvent ()
3085 {
3086  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewZoomInFromWheelEvent";
3087 
3088  if ((m_modelMainWindow.zoomControl() == ZOOM_CONTROL_MENU_WHEEL) ||
3089  (m_modelMainWindow.zoomControl() == ZOOM_CONTROL_MENU_WHEEL_PLUSMINUS)) {
3090 
3091  // Anchor the zoom to the cursor position
3092  m_view->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
3093 
3094  // Forward this event
3095  slotViewZoomIn ();
3096 
3097  m_view->setTransformationAnchor(QGraphicsView::NoAnchor);
3098  }
3099 }
3100 
3101 void MainWindow::slotViewZoomOut ()
3102 {
3103  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewZoomOut";
3104 
3105  // Try to zoom out
3106  ZoomTransition zoomTransition;
3107  ZoomFactor zoomFactorNew = zoomTransition.zoomOut (currentZoomFactor (),
3108  m_view->transform ().m11 (),
3109  m_view->transform ().m22 (),
3110  m_actionZoomFill->isChecked ());
3111  setNonFillZoomFactor (zoomFactorNew);
3112 }
3113 
3114 void MainWindow::slotViewZoomOutFromWheelEvent ()
3115 {
3116  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::slotViewZoomOutFromWheelEvent";
3117 
3118  if ((m_modelMainWindow.zoomControl() == ZOOM_CONTROL_MENU_WHEEL) ||
3119  (m_modelMainWindow.zoomControl() == ZOOM_CONTROL_MENU_WHEEL_PLUSMINUS)) {
3120 
3121  // Anchor the zoom to the cursor position
3122  m_view->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
3123 
3124  // Forward this event
3125  slotViewZoomOut ();
3126 
3127  m_view->setTransformationAnchor(QGraphicsView::NoAnchor);
3128  }
3129 }
3130 
3131 void MainWindow::startRegressionDropTest (const QStringList &loadStartupFiles)
3132 {
3133  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::startRegressionTestErrorReport";
3134 
3135  // Regression testing of drag and drop has some constraints:
3136  // 1) Need graphics window (GraphicsView) or else its events will not work. This is why
3137  // drag and drop testing is not done as one of the cli tests, which do not show the gui
3138  // 2) Drag and drop by itself does not produce the csv file, so this code will output the
3139  // x,y dimensions of the imported image instead of a normal csv file
3140  connect (this, SIGNAL (signalDropRegression (QString)), m_view, SLOT (slotDropRegression (QString)));
3141 
3142  for (int counter = 0; counter < loadStartupFiles.size (); counter++) {
3143  QString filenameDrop = loadStartupFiles.at (counter);
3144 
3145  // Trigger drop part of drag and drop operation
3146  emit signalDropRegression (filenameDrop);
3147 
3148  QSize siz = m_view->size();
3149 
3150  QString filenameCsv;
3151  if (filenameDrop.startsWith ("http")) {
3152 
3153  // Internet url is not useful for computing local file name. Only regression tests reach this branch
3154  // so filename is hardcoded
3155  filenameCsv = "../test/drag_and_drop_http.csv_actual_1";
3156 
3157  } else {
3158 
3159  // Local file
3160  filenameCsv = QString ("%1_%2")
3161  .arg (exportRegressionFilenameFromInputFilename (filenameDrop))
3162  .arg (counter + 1);
3163  }
3164 
3165  QFile file (filenameCsv);
3166  file.open (QIODevice::WriteOnly);
3167  QTextStream str (&file);
3168  str << siz.width() << "x" << siz.height() << "\n";
3169  file.close ();
3170  }
3171 
3172  exit (0);
3173 }
3174 
3175 void MainWindow::startRegressionTestErrorReport(const QString &regressionInputFile)
3176 {
3177  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::startRegressionTestErrorReport";
3178 
3179  // In order for point-deleting commands to work (CmdCut, CmdDelete) in the regression tests, we need to
3180  // reset the Point identifier index here:
3181  // 1) after loading of the file which has increased the index value to greater than 0
3182  // 2) before running any commands since those commands implicitly assume the index is zero
3184 
3185  // Save output/export file name
3186  m_regressionFile = exportRegressionFilenameFromInputFilename (regressionInputFile);
3187 
3188  m_timerRegressionErrorReport = new QTimer();
3189  m_timerRegressionErrorReport->setSingleShot(false);
3190  connect (m_timerRegressionErrorReport, SIGNAL (timeout()), this, SLOT (slotTimeoutRegressionErrorReport()));
3191 
3192  m_timerRegressionErrorReport->start(REGRESSION_INTERVAL);
3193 }
3194 
3195 void MainWindow::startRegressionTestFileCmdScript()
3196 {
3197  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::startRegressionTestFileCmdScript";
3198 
3199  m_timerRegressionFileCmdScript = new QTimer();
3200  m_timerRegressionFileCmdScript->setSingleShot(false);
3201  connect (m_timerRegressionFileCmdScript, SIGNAL (timeout()), this, SLOT (slotTimeoutRegressionFileCmdScript()));
3202 
3203  m_timerRegressionFileCmdScript->start(REGRESSION_INTERVAL);
3204 }
3205 
3207 {
3208  return m_transformation;
3209 }
3210 
3212 {
3213  return m_transformation.transformIsDefined();
3214 }
3215 
3217 {
3218  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateAfterCommand";
3219 
3220  ENGAUGE_CHECK_PTR (m_cmdMediator);
3221 
3222  // Update transformation stuff, including the graph coordinates of every point in the Document, so coordinates in
3223  // status bar are up to date. Point coordinates in Document are also updated
3224  updateAfterCommandStatusBarCoords ();
3225 
3226  updateHighlightOpacity ();
3227 
3228  // Update graphics. Effectively, these steps do very little (just needed for highlight opacity)
3229  m_digitizeStateContext->updateAfterPointAddition (); // May or may not be needed due to point addition
3230 
3231  updateControls ();
3232  updateChecklistGuide ();
3233  updateFittingWindow ();
3234  updateGeometryWindow();
3235 
3236  // Final actions at the end of a redo/undo are:
3237  // 1) checkpoint the Document and GraphicsScene to log files so proper state can be verified
3238  // 2) run sanity check on state
3239  writeCheckpointToLogFile ();
3240  DocumentScrub docScrub;
3241  docScrub.check (*this,
3242  m_cmdMediator->document ());
3243 
3244  // Since focus may have drifted over to Geometry Window or some other control we se focus on the GraphicsView
3245  // so the cursor is appropriate for the current state (otherwise it often ends up as default arrow)
3246  m_view->setFocus ();
3247 }
3248 
3249 void MainWindow::updateAfterCommandStatusBarCoords ()
3250 {
3251  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateAfterCommandStatusBarCoords";
3252 
3253  // For some reason, mapFromGlobal(QCursor::pos) differs from event->pos by a little bit. We must compensate for
3254  // this so cursor coordinates in status bar match the DlgEditPointAxis inputs initially. After the mouse moves
3255  // the problem disappears since event->pos is available and QCursor::pos is no longer needed
3256  const QPoint HACK_SO_GRAPH_COORDINATE_MATCHES_INPUT (1, 1);
3257 
3258  Transformation m_transformationBefore (m_transformation);
3259 
3260  updateTransformationAndItsDependencies();
3261 
3262  // Trigger state transitions for transformation if appropriate
3263  if (!m_transformationBefore.transformIsDefined() && m_transformation.transformIsDefined()) {
3264 
3265  // Transition from undefined to defined
3266  m_transformationStateContext->triggerStateTransition(m_isGnuplot,
3267  TRANSFORMATION_STATE_DEFINED,
3268  *m_cmdMediator,
3269  m_transformation,
3270  selectedGraphCurve());
3271 
3272  } else if (m_transformationBefore.transformIsDefined() && !m_transformation.transformIsDefined()) {
3273 
3274  // Transition from defined to undefined
3275  m_transformationStateContext->triggerStateTransition(m_isGnuplot,
3276  TRANSFORMATION_STATE_UNDEFINED,
3277  *m_cmdMediator,
3278  m_transformation,
3279  selectedGraphCurve());
3280 
3281  } else if (m_transformation.transformIsDefined() && (m_transformationBefore != m_transformation)) {
3282 
3283  // There was not a define/undefined or undefined/defined transition, but the transformation changed so we
3284  // need to update the Checker
3285  m_transformationStateContext->updateAxesChecker(*m_cmdMediator,
3286  m_transformation);
3287 
3288  }
3289 
3290  QPoint posLocal = m_view->mapFromGlobal (QCursor::pos ()) - HACK_SO_GRAPH_COORDINATE_MATCHES_INPUT;
3291  QPointF posScreen = m_view->mapToScene (posLocal);
3292 
3293  slotMouseMove (posScreen); // Update the status bar coordinates to reflect the newly updated transformation
3294 }
3295 
3297 {
3298  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateAfterMouseRelease";
3299 
3300  updateControls ();
3301 }
3302 
3303 void MainWindow::updateChecklistGuide ()
3304 {
3305  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateChecklistGuide";
3306 
3307  m_dockChecklistGuide->update (*m_cmdMediator,
3308  m_isDocumentExported);
3309 }
3310 
3311 void MainWindow::updateControls ()
3312 {
3313  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateControls"
3314  << " selectedItems=" << m_scene->selectedItems().count();
3315 
3316  m_cmbBackground->setEnabled (!m_currentFile.isEmpty ());
3317 
3318  m_actionImportImageReplace->setEnabled (m_cmdMediator != nullptr);
3319 #if !defined(OSX_DEBUG) && !defined(OSX_RELEASE)
3320  m_menuFileOpenRecent->setEnabled ((m_actionRecentFiles.count () > 0) &&
3321  (m_actionRecentFiles.at(0)->isVisible ())); // Need at least one visible recent file entry
3322 #endif
3323  m_actionClose->setEnabled (!m_currentFile.isEmpty ());
3324  m_actionSave->setEnabled (!m_currentFile.isEmpty ());
3325  m_actionSaveAs->setEnabled (!m_currentFile.isEmpty ());
3326  m_actionExport->setEnabled (!m_currentFile.isEmpty ());
3327  m_actionPrint->setEnabled (!m_currentFile.isEmpty ());
3328 
3329  if (m_cmdMediator == nullptr) {
3330  m_actionEditUndo->setEnabled (false);
3331  m_actionEditRedo->setEnabled (false);
3332  } else {
3333  m_actionEditUndo->setEnabled (m_cmdMediator->canUndo ());
3334  m_actionEditRedo->setEnabled (m_cmdMediator->canRedo () || m_cmdStackShadow->canRedo ());
3335  }
3336  bool tableFittingIsActive, tableFittingIsCopyable;
3337  bool tableGeometryIsActive, tableGeometryIsCopyable;
3338  m_dockFittingWindow->getTableStatus (tableFittingIsActive, tableFittingIsCopyable); // Fitting window status
3339  m_dockGeometryWindow->getTableStatus (tableGeometryIsActive, tableGeometryIsCopyable); // Geometry window status
3340  m_actionEditCut->setEnabled (!tableFittingIsActive &&
3341  !tableGeometryIsActive &&
3342  m_scene->selectedItems().count () > 0);
3343  m_actionEditCopy->setEnabled ((!tableFittingIsActive && !tableGeometryIsActive && m_scene->selectedItems().count () > 0) ||
3344  (tableFittingIsActive && tableFittingIsCopyable) ||
3345  (tableGeometryIsActive && tableGeometryIsCopyable));
3346  m_actionEditPaste->setEnabled (m_digitizeStateContext->canPaste (m_transformation,
3347  m_view->size ()));
3348  m_actionEditDelete->setEnabled (!tableFittingIsActive &&
3349  !tableGeometryIsActive &&
3350  m_scene->selectedItems().count () > 0);
3351  // m_actionEditPasteAsNew and m_actionEditPasteAsNewAdvanced are updated when m_menuEdit is about to be shown
3352 
3353  m_actionDigitizeAxis->setEnabled (modeGraph ());
3354  m_actionDigitizeScale->setEnabled (modeMap ());
3355  m_actionDigitizeCurve ->setEnabled (!m_currentFile.isEmpty ());
3356  m_actionDigitizePointMatch->setEnabled (!m_currentFile.isEmpty ());
3357  m_actionDigitizeColorPicker->setEnabled (!m_currentFile.isEmpty ());
3358  m_actionDigitizeSegment->setEnabled (!m_currentFile.isEmpty ());
3359  m_actionDigitizeSelect->setEnabled (!m_currentFile.isEmpty ());
3360  if (m_transformation.transformIsDefined()) {
3361  m_actionViewGridLines->setEnabled (true);
3362  } else {
3363  m_actionViewGridLines->setEnabled (false);
3364  m_actionViewGridLines->setChecked (false);
3365  }
3366  m_actionViewBackground->setEnabled (!m_currentFile.isEmpty());
3367  m_actionViewChecklistGuide->setEnabled (!m_dockChecklistGuide->browserIsEmpty());
3368  m_actionViewDigitize->setEnabled (!m_currentFile.isEmpty ());
3369  m_actionViewSettingsViews->setEnabled (!m_currentFile.isEmpty ());
3370 
3371  m_actionSettingsCoords->setEnabled (!m_currentFile.isEmpty ());
3372  m_actionSettingsCurveList->setEnabled (!m_currentFile.isEmpty ());
3373  m_actionSettingsCurveProperties->setEnabled (!m_currentFile.isEmpty ());
3374  m_actionSettingsDigitizeCurve->setEnabled (!m_currentFile.isEmpty ());
3375  m_actionSettingsExport->setEnabled (!m_currentFile.isEmpty ());
3376  m_actionSettingsColorFilter->setEnabled (!m_currentFile.isEmpty ());
3377  m_actionSettingsAxesChecker->setEnabled (!m_currentFile.isEmpty ());
3378  m_actionSettingsGridDisplay->setEnabled (!m_currentFile.isEmpty () && m_transformation.transformIsDefined());
3379  m_actionSettingsGridRemoval->setEnabled (!m_currentFile.isEmpty ());
3380  m_actionSettingsPointMatch->setEnabled (!m_currentFile.isEmpty ());
3381  m_actionSettingsSegments->setEnabled (!m_currentFile.isEmpty ());
3382  m_actionSettingsGeneral->setEnabled (!m_currentFile.isEmpty ());
3383 
3384  m_groupBackground->setEnabled (!m_currentFile.isEmpty ());
3385  m_groupCurves->setEnabled (!m_currentFile.isEmpty ());
3386  m_groupZoom->setEnabled (!m_currentFile.isEmpty ());
3387 
3388  m_actionZoomIn->setEnabled (!m_currentFile.isEmpty ()); // Disable at startup so shortcut has no effect
3389  m_actionZoomOut->setEnabled (!m_currentFile.isEmpty ()); // Disable at startup so shortcut has no effect
3390 }
3391 
3392 void MainWindow::updateCoordSystem(CoordSystemIndex coordSystemIndex)
3393 {
3394  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateCoordSystem";
3395 
3396  // Set current curve in the Document and in the MainWindow combobox together so they are in sync. Setting
3397  // the selected curve prevents a crash in updateTransformationAndItsDependencies
3398  m_cmdMediator->document().setCoordSystemIndex (coordSystemIndex);
3399  loadCurveListFromCmdMediator ();
3400 
3401  updateTransformationAndItsDependencies(); // Transformation state may have changed
3402  updateSettingsAxesChecker(m_cmdMediator->document().modelAxesChecker()); // Axes checker dependes on transformation state
3403 
3404  // Nice trick for showing that a new coordinate system is in effect is to show the axes checker
3405  m_transformationStateContext->updateAxesChecker (*m_cmdMediator,
3406  m_transformation);
3407 
3409 }
3410 
3411 void MainWindow::updateDigitizeStateIfSoftwareTriggered (DigitizeState digitizeState)
3412 {
3413  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateDigitizeStateIfSoftwareTriggered";
3414 
3415  switch (digitizeState) {
3416  case DIGITIZE_STATE_AXIS:
3417  m_actionDigitizeAxis->setChecked(true);
3418  slotDigitizeAxis(); // Call the slot that the setChecked call fails to trigger
3419  break;
3420 
3421  case DIGITIZE_STATE_COLOR_PICKER:
3422  m_actionDigitizeColorPicker->setChecked(true);
3423  slotDigitizeColorPicker(); // Call the slot that the setChecked call fails to trigger
3424  break;
3425 
3426  case DIGITIZE_STATE_CURVE:
3427  m_actionDigitizeCurve->setChecked(true);
3428  slotDigitizeCurve(); // Call the slot that the setChecked call fails to trigger
3429  break;
3430 
3431  case DIGITIZE_STATE_EMPTY:
3432  break;
3433 
3434  case DIGITIZE_STATE_POINT_MATCH:
3435  m_actionDigitizePointMatch->setChecked(true);
3436  slotDigitizePointMatch(); // Call the slot that the setChecked call fails to trigger
3437  break;
3438 
3439  case DIGITIZE_STATE_SCALE:
3440  m_actionDigitizeScale->setChecked(true);
3441  slotDigitizeScale(); // Call the slot that the setChecked call fails to trigger
3442  break;
3443 
3444  case DIGITIZE_STATE_SEGMENT:
3445  m_actionDigitizeSegment->setChecked(true);
3446  slotDigitizeSegment(); // Call the slot that the setChecked call fails to trigger
3447  break;
3448 
3449  case DIGITIZE_STATE_SELECT:
3450  m_actionDigitizeSelect->setChecked(true);
3451  slotDigitizeSelect(); // Call the slot that the setChecked call fails to trigger
3452  break;
3453 
3454  default:
3455  LOG4CPP_ERROR_S ((*mainCat)) << "MainWindow::updateDigitizeStateIfSoftwareTriggered";
3456  break;
3457  }
3458 }
3459 
3460 void MainWindow::updateFittingWindow ()
3461 {
3462  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateFittingWindow";
3463 
3464  if (m_cmdMediator != nullptr &&
3465  m_cmbCurve != nullptr) {
3466 
3467  // Update fitting window
3468  m_dockFittingWindow->update (*m_cmdMediator,
3469  m_modelMainWindow,
3470  m_cmbCurve->currentText (),
3471  m_transformation);
3472  }
3473 }
3474 
3475 void MainWindow::updateGeometryWindow ()
3476 {
3477  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateGeometryWindow";
3478 
3479  if (m_cmdMediator != nullptr &&
3480  m_cmbCurve != nullptr) {
3481 
3482  // Update geometry window
3483  m_dockGeometryWindow->update (*m_cmdMediator,
3484  m_modelMainWindow,
3485  m_cmbCurve->currentText (),
3486  m_transformation);
3487  }
3488 }
3489 
3491 {
3492  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateGraphicsLinesToMatchGraphicsPoints";
3493 
3495  m_transformation);
3496 }
3497 
3498 void MainWindow::updateGridLines ()
3499 {
3500  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateGridLines";
3501 
3502  // Remove old grid lines
3503  m_gridLines.clear ();
3504 
3505  // Create new grid lines
3506  GridLineFactory factory (*m_scene,
3507  m_cmdMediator->document().modelCoords());
3508  factory.createGridLinesForEvenlySpacedGrid (m_cmdMediator->document().modelGridDisplay(),
3509  m_cmdMediator->document(),
3510  m_modelMainWindow,
3511  m_transformation,
3512  m_gridLines);
3513 
3514  m_gridLines.setVisible (m_actionViewGridLines->isChecked());
3515 }
3516 
3517 void MainWindow::updateHighlightOpacity ()
3518 {
3519  if (m_cmdMediator != nullptr) {
3520 
3521  // Update the QGraphicsScene with the populated Curves. This requires the points in the Document to be already updated
3522  // by updateAfterCommandStatusBarCoords
3523  m_scene->updateAfterCommand (*m_cmdMediator,
3524  m_modelMainWindow.highlightOpacity(),
3525  m_dockGeometryWindow,
3526  m_transformation);
3527  }
3528 }
3529 
3530 void MainWindow::updateRecentFileList()
3531 {
3532  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateRecentFileList";
3533 
3534 #if !defined(OSX_DEBUG) && !defined(OSX_RELEASE)
3535  QSettings settings (SETTINGS_ENGAUGE, SETTINGS_DIGITIZER);
3536  QStringList recentFilePaths = settings.value(SETTINGS_RECENT_FILE_LIST).toStringList();
3537 
3538  // Determine the desired size of the path list
3539  unsigned int count = unsigned (recentFilePaths.size());
3540  if (count > MAX_RECENT_FILE_LIST_SIZE) {
3541  count = MAX_RECENT_FILE_LIST_SIZE;
3542  }
3543 
3544  // Add visible entries
3545  int i;
3546  for (i = 0; i < signed (count); i++) {
3547  QString strippedName = QFileInfo (recentFilePaths.at(i)).fileName();
3548  m_actionRecentFiles.at (i)->setText (strippedName);
3549  m_actionRecentFiles.at (i)->setData (recentFilePaths.at (i));
3550  m_actionRecentFiles.at (i)->setVisible (true);
3551  }
3552 
3553  // Hide any extra entries
3554  for (i = signed (count); i < signed (MAX_RECENT_FILE_LIST_SIZE); i++) {
3555  m_actionRecentFiles.at (i)->setVisible (false);
3556  }
3557 #endif
3558 }
3559 
3561 {
3562  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateSettingsAxesChecker";
3563 
3564  m_cmdMediator->document().setModelAxesChecker(modelAxesChecker);
3565  if (m_transformation.transformIsDefined()) {
3566  m_transformationStateContext->triggerStateTransition(m_isGnuplot,
3567  TRANSFORMATION_STATE_DEFINED,
3568  *m_cmdMediator,
3569  m_transformation,
3570  m_cmbCurve->currentText());
3571  } else {
3572  m_transformationStateContext->triggerStateTransition(m_isGnuplot,
3573  TRANSFORMATION_STATE_UNDEFINED,
3574  *m_cmdMediator,
3575  m_transformation,
3576  m_cmbCurve->currentText());
3577  }
3578 }
3579 
3581 {
3582  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateSettingsColorFilter";
3583 
3584  m_cmdMediator->document().setModelColorFilter(modelColorFilter);
3585  m_backgroundStateContext->updateColorFilter (m_isGnuplot,
3586  m_transformation,
3587  m_cmdMediator->document().modelGridRemoval(),
3588  modelColorFilter,
3589  m_cmbCurve->currentText());
3590  m_digitizeStateContext->handleCurveChange (m_cmdMediator);
3592 }
3593 
3595 {
3596  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateSettingsCoords";
3597 
3598  m_cmdMediator->document().setModelCoords(modelCoords);
3599 }
3600 
3602 {
3603  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateSettingsCurveList";
3604 
3605  m_cmdMediator->document().setCurvesGraphs (curvesGraphs);
3606  loadCurveListFromCmdMediator();
3608 }
3609 
3611 {
3612  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateSettingsCurveStyles";
3613 
3614  m_scene->updateCurveStyles(modelCurveStyles);
3615  m_cmdMediator->document().setModelCurveStyles(modelCurveStyles);
3617 }
3618 
3620 {
3621  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateSettingsDigitizeCurve";
3622 
3623  m_cmdMediator->document().setModelDigitizeCurve(modelDigitizeCurve);
3624  m_digitizeStateContext->updateModelDigitizeCurve (m_cmdMediator,
3625  modelDigitizeCurve);
3626 }
3627 
3629 {
3630  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateSettingsExportFormat";
3631 
3632  m_cmdMediator->document().setModelExport (modelExport);
3633 }
3634 
3636 {
3637  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateSettingsGeneral";
3638 
3639  m_cmdMediator->document().setModelGeneral(modelGeneral);
3640 }
3641 
3643 {
3644  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateSettingsGridDisplay";
3645 
3646  m_cmdMediator->document().setModelGridDisplay(modelGridDisplay);
3647  updateGridLines ();
3648 }
3649 
3651 {
3652  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateSettingsGridRemoval";
3653 
3654  m_cmdMediator->document().setModelGridRemoval(modelGridRemoval);
3655 }
3656 
3657 void MainWindow::updateSettingsMainWindow()
3658 {
3659  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateSettingsMainWindow";
3660 
3661  if (m_modelMainWindow.zoomControl() == ZOOM_CONTROL_MENU_ONLY ||
3662  m_modelMainWindow.zoomControl() == ZOOM_CONTROL_MENU_WHEEL) {
3663 
3664  m_actionZoomIn->setShortcut (tr (""));
3665  m_actionZoomOut->setShortcut (tr (""));
3666 
3667  } else {
3668 
3669  m_actionZoomIn->setShortcut (tr ("+"));
3670  m_actionZoomOut->setShortcut (tr ("-"));
3671 
3672  }
3673 
3674  if ((m_scene != nullptr) &&
3675  (m_cmdMediator != nullptr)) {
3676  m_scene->updateCurveStyles(m_cmdMediator->document().modelCurveStyles());
3677  }
3678 
3679  updateHighlightOpacity();
3680  updateWindowTitle();
3681  updateFittingWindow(); // Forward the drag and drop choice
3682  updateGeometryWindow(); // Forward the drag and drop choice
3683 }
3684 
3686 {
3687  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateSettingsMainWindow";
3688 
3689  m_modelMainWindow = modelMainWindow;
3691 }
3692 
3694 {
3695  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateSettingsPointMatch";
3696 
3697  m_cmdMediator->document().setModelPointMatch(modelPointMatch);
3698 }
3699 
3701 {
3702  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateSettingsSegments";
3703 
3704  m_cmdMediator->document().setModelSegments(modelSegments);
3705  m_digitizeStateContext->updateModelSegments(modelSegments);
3706 }
3707 
3708 void MainWindow::updateSmallDialogs ()
3709 {
3710  m_dlgSettingsAxesChecker->setSmallDialogs (m_modelMainWindow.smallDialogs ());
3711  m_dlgSettingsColorFilter->setSmallDialogs (m_modelMainWindow.smallDialogs ());
3712  m_dlgSettingsCoords->setSmallDialogs (m_modelMainWindow.smallDialogs ());
3713  m_dlgSettingsCurveList->setSmallDialogs (m_modelMainWindow.smallDialogs ());
3714  m_dlgSettingsCurveProperties->setSmallDialogs (m_modelMainWindow.smallDialogs ());
3715  m_dlgSettingsDigitizeCurve->setSmallDialogs (m_modelMainWindow.smallDialogs ());
3716  m_dlgSettingsExportFormat->setSmallDialogs (m_modelMainWindow.smallDialogs ());
3717  m_dlgSettingsGeneral->setSmallDialogs (m_modelMainWindow.smallDialogs ());
3718  m_dlgSettingsGridDisplay->setSmallDialogs (m_modelMainWindow.smallDialogs ());
3719  m_dlgSettingsGridRemoval->setSmallDialogs (m_modelMainWindow.smallDialogs ());
3720  m_dlgSettingsMainWindow->setSmallDialogs (m_modelMainWindow.smallDialogs ());
3721  m_dlgSettingsPointMatch->setSmallDialogs (m_modelMainWindow.smallDialogs ());
3722  m_dlgSettingsSegments->setSmallDialogs (m_modelMainWindow.smallDialogs ());
3723 }
3724 
3725 void MainWindow::updateTransformationAndItsDependencies()
3726 {
3727  m_transformation.update (!m_currentFile.isEmpty (),
3728  *m_cmdMediator,
3729  m_modelMainWindow);
3730 
3731  // Grid removal is affected by new transformation above
3732  m_backgroundStateContext->setCurveSelected (m_isGnuplot,
3733  m_transformation,
3734  m_cmdMediator->document().modelGridRemoval(),
3735  m_cmdMediator->document().modelColorFilter(),
3736  m_cmbCurve->currentText ());
3737 
3738  // Grid display is also affected by new transformation above, if there was a transition into defined state
3739  // in which case that transition triggered the initialization of the grid display parameters
3740  updateGridLines();
3741 }
3742 
3743 void MainWindow::updateViewedCurves ()
3744 {
3745  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateViewedCurves";
3746 
3747  if (m_actionViewCurvesAll->isChecked ()) {
3748 
3749  m_scene->showCurves (true, true);
3750 
3751  } else if (m_actionViewCurvesSelected->isChecked ()) {
3752 
3753  m_scene->showCurves (true, false, selectedGraphCurve ());
3754 
3755  } else if (m_actionViewCurvesNone->isChecked ()) {
3756 
3757  m_scene->showCurves (false);
3758 
3759  } else {
3760  ENGAUGE_ASSERT (false);
3761  }
3762 }
3763 
3765 {
3766  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateViewsOfSettings";
3767 
3768  QString activeCurve = m_digitizeStateContext->activeCurve ();
3769 
3770  updateViewsOfSettings (activeCurve);
3771 }
3772 
3773 void MainWindow::updateViewsOfSettings (const QString &activeCurve)
3774 {
3775  if (activeCurve.isEmpty ()) {
3776 
3777  m_viewPointStyle->unsetPointStyle ();
3778  m_viewSegmentFilter->unsetColorFilterSettings ();
3779 
3780 
3781  } else {
3782 
3783  PointStyle pointStyle = m_cmdMediator->document().modelCurveStyles().curveStyle(activeCurve).pointStyle();
3784  m_viewPointStyle->setPointStyle (pointStyle);
3785 
3786  ColorFilterSettings colorFilterSettings = m_cmdMediator->document().modelColorFilter().colorFilterSettings(activeCurve);
3787  m_viewSegmentFilter->setColorFilterSettings (colorFilterSettings,
3788  m_cmdMediator->pixmap ());
3789 
3790  }
3791 }
3792 
3793 void MainWindow::updateWindowTitle ()
3794 {
3795  LOG4CPP_INFO_S ((*mainCat)) << "MainWindow::updateWindowTitle";
3796 
3797  const QString PLACEHOLDER ("[*]");
3798 
3799  QString title = QString ("%1 %2")
3800  .arg (tr ("Engauge Digitizer"))
3801  .arg (VERSION_NUMBER);
3802 
3803  QString fileNameMaybeStripped;
3804  if (!m_currentFileWithPathAndFileExtension.isEmpty()) {
3805 
3806  QFileInfo fileInfo (m_currentFileWithPathAndFileExtension);
3807 
3808  switch (m_modelMainWindow.mainTitleBarFormat())
3809  {
3810  case MAIN_TITLE_BAR_FORMAT_NO_PATH:
3811  // Remove file extension and path for "clean look". We use completeBaseName rather than baseName so
3812  // files with multiple periods are handled correctly - all but last suffix gets kept
3813  fileNameMaybeStripped = fileInfo.completeBaseName();
3814  break;
3815 
3816  case MAIN_TITLE_BAR_FORMAT_PATH:
3817  fileNameMaybeStripped = m_currentFileWithPathAndFileExtension;
3818  break;
3819  }
3820 
3821  title += QString (": %1")
3822  .arg (fileNameMaybeStripped);
3823  }
3824 
3825  // To prevent "QWidget::setWindowModified: The window title does not contain a [*] placeholder" warnings,
3826  // we always append a placeholder
3827  title += PLACEHOLDER;
3828 
3829  setWindowTitle (title);
3830 }
3831 
3833 {
3834  ENGAUGE_CHECK_PTR (m_view);
3835  return *m_view;
3836 }
3837 
3839 {
3840  ENGAUGE_CHECK_PTR (m_view);
3841  return *m_view;
3842 }
3843 
3844 void MainWindow::writeCheckpointToLogFile ()
3845 {
3846  // Document
3847  QString checkpointDoc;
3848  QTextStream strDoc (&checkpointDoc);
3849  m_cmdMediator->document().printStream(INDENTATION_PAST_TIMESTAMP,
3850  strDoc);
3851 
3852  // Scene
3853  QString checkpointScene;
3854  QTextStream strScene (&checkpointScene);
3855  m_scene->printStream (INDENTATION_PAST_TIMESTAMP,
3856  strScene);
3857 
3858  // Skip slow string manipulation if BEFORE call to LOG4CPP_DEBUG_S
3859  if (mainCat->getPriority() == log4cpp::Priority::DEBUG) {
3860 
3861  LOG4CPP_DEBUG_S ((*mainCat)) << "MainWindow::writeCheckpointToLogFile\n"
3862  << "--------------DOCUMENT CHECKPOINT START----------" << "\n"
3863  << checkpointDoc.toLatin1().data()
3864  << "---------------DOCUMENT CHECKPOINT END-----------" << "\n"
3865  << "----------------SCENE CHECKPOINT START-----------" << "\n"
3866  << checkpointScene.toLatin1().data()
3867  << "-----------------SCENE CHECKPOINT END------------" ;
3868  }
3869 }
void addCoordSystems(unsigned int numberCoordSystemToAdd)
Add some number (0 or more) of additional coordinate systems.
Definition: Document.cpp:148
void load(CmdMediator &cmdMediator)
Load settings from Document.
Factory class for generating the points, composed of QGraphicsItem objects, along a GridLine...
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
QDir getDirectoryImportOpen() const
Get the current Import/Open directory.
void updateGraphicsLinesToMatchGraphicsPoints(const CurveStyles &modelCurveStyles, const Transformation &transformation)
A mouse move has just occurred so move the selected points, since they were dragged.
void updateCoordSystem(CoordSystemIndex coordSystemIndex)
Select a different CoordSystem.
Model for DlgSettingsGeneral and CmdSettingsGeneral.
void unsetPointStyle()
Apply no PointStyle.
Given a set of point identifiers, if a map is in effect (with its two axis endpoints) then both axis ...
DocumentAxesPointsRequired documentAxesPointsRequired() const
Get method for DocumentAxesPointsRequired.
Definition: Document.cpp:361
void setColorFilterSettings(const ColorFilterSettings &colorFilterSettings, const QPixmap &pixmap)
Apply the color filter of the currently selected curve. The pixmap is included so the background colo...
void printStream(QString indentation, QTextStream &str) const
Debugging method that supports print method of this class and printStream method of some other class(...
Definition: Document.cpp:840
unsigned int coordSystemCount() const
Number of CoordSystem.
Definition: Document.cpp:305
void setCurveName(const QString &curveName)
Load information for the specified curve name. When called externally, the load method must have been...
bool imageReplaceRenamesDocument() const
Get method for image replaces renames document.
void createGhosts(QGraphicsScene &scene)
Create ghosts from the path/rect/polygon lists.
Definition: Ghosts.cpp:78
Model for DlgSettingsPointMatch and CmdSettingsPointMatch.
Returns information about files.
Definition: LoadFileInfo.h:13
void updateAfterPointAddition()
Update the graphics attributes.
Color filter parameters for one curve. For a class, this is handled the same as LineStyle and PointSt...
void resetOnLoad(CmdMediator *cmdMediator)
Resetting makes re-initializes for documents after the first.
void updateSettingsMainWindow(const MainWindowModel &modelMainWindow)
Update with new main window properties.
void setSelectedCurveName(const QString &selectedCurveName)
Save curve name that is selected for the current coordinate system, for the next time the coordinate ...
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
void setStatusBarMode(StatusBarMode statusBarMode)
Set the status bar visibility mode.
Definition: StatusBar.cpp:258
Model for DlgSettingsGridDisplay and CmdSettingsGridDisplay.
void resetOnLoad()
Reset, when loading a document after the first, to same state that first document was at when loaded...
DocumentModelColorFilter modelColorFilter() const
Get method for DocumentModelColorFilter.
Definition: Document.cpp:686
Command for cutting all selected Points.
Definition: CmdCut.h:18
void setModelAxesChecker(const DocumentModelAxesChecker &modelAxesChecker)
Set method for DocumentModelAxesChecker.
Definition: Document.cpp:949
void setModelGridRemoval(const DocumentModelGridRemoval &modelGridRemoval)
Set method for DocumentModelGridRemoval.
Definition: Document.cpp:1026
Dialog for saving error report for later transmission to the developers.
void clear()
Deallocate and remove all grid lines.
Definition: GridLines.cpp:24
void updateDigitizeStateIfSoftwareTriggered(DigitizeState digitizeState)
After software-triggered state transition, this method manually triggers the action as if user had cl...
void setDragDropExport(bool dragDropExport)
Set method for drag and drop export.
unsigned int coordSystemIndexToBeRestored() const
Coordinate system index that was active before the ghosts.
Definition: Ghosts.cpp:73
static void setIdentifierIndex(unsigned int identifierIndex)
Reset the current index while performing a Redo.
Definition: Point.cpp:478
void printStream(QString indentation, QTextStream &str)
Debugging method that supports print method of this class and printStream method of some other class(...
void saveXml(QXmlStreamWriter &writer) const
Serialize to xml.
Wrapper around the Poppler library.
Definition: Pdf.h:28
Wrapper around OpenJPEG library, in C, for opening jpeg2000 files.
Definition: Jpeg2000.h:26
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
void fitInView(GraphicsView &view)
Zoom so background fills the window.
void setModelPointMatch(const DocumentModelPointMatch &modelPointMatch)
Set method for DocumentModelPointMatch.
Definition: Document.cpp:1033
Model for DlgSettingsExportFormat and CmdSettingsExportFormat.
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
Transformation transformation() const
Return read-only copy of transformation.
void updateModelDigitizeCurve(CmdMediator *cmdMediator, const DocumentModelDigitizeCurve &modelDigitizeCurve)
Update the digitize curve settings.
void setModelGeneral(const DocumentModelGeneral &modelGeneral)
Set method for DocumentModelGeneral.
Definition: Document.cpp:1012
void setEnabled(bool enabled)
Show the style with semi-transparency or full-transparency to indicate if associated Curve is active ...
void setSignificantDigits(int significantDigits)
Set method for significant digits.
Model for DlgSettingsCurveProperties and CmdSettingsCurveProperties.
Definition: CurveStyles.h:22
void createGridLinesForEvenlySpacedGrid(const DocumentModelGridDisplay &modelGridDisplay, const Document &document, const MainWindowModel &modelMainWindow, const Transformation &transformation, GridLines &gridLines)
Create a rectangular (cartesian) or annular (polar) grid of evenly spaced grid lines.
Wrapper around the QImage class for read and importing non-PDF files.
Definition: NonPdf.h:26
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
bool canRedo() const
Returns true if there is at least one command on the stack.
QString activeCurve() const
Curve name for active Curve. This can include AXIS_CURVE_NAME, and empty string.
void setModelSegments(const DocumentModelSegments &modelSegments)
Set method for DocumentModelSegments.
Definition: Document.cpp:1040
MainTitleBarFormat mainTitleBarFormat() const
Get method for MainWindow titlebar filename format.
void handleContextMenuEventAxis(CmdMediator *cmdMediator, const QString &pointIdentifier)
See DigitizeStateAbstractBase::handleContextMenuEventAxis.
void updateColorFilter(bool isGnuplot, const Transformation &transformation, const DocumentModelGridRemoval &modelGridRemoval, const DocumentModelColorFilter &colorFilter, const QString &curveSelected)
Apply color filter settings.
void updateAfterMouseRelease()
Call MainWindow::updateControls (which is private) after the very specific case - a mouse press/relea...
void saveErrorReportFileAndExit(const char *comment, const char *file, int line, const char *context)
Save error report and exit.
void handleCurveChange(CmdMediator *cmdMediator)
See DigitizeStateAbstractBase::handleCurveChange.
void setDirectoryExportSaveFromFilename(const QString &fileName)
Save the current Export/Save directory, after user has accepted the Export/Save dialog.
void setCoordinates(const QString &coordsScreen, const QString &coordsGraph, const QString &resolutionGraph)
Populate the coordinates fields. Unavailable values are empty. Html-encoding to highlight with colors...
Definition: StatusBar.cpp:239
void handleContextMenuEventGraph(CmdMediator *cmdMediator, const QStringList &pointIdentifiers)
See DigitizeStateAbstractBase::handleContextMenuEventGraph.
MainWindow(const QString &errorReportFile, const QString &fileCmdScriptFile, bool isDropRegression, bool isRegressionTest, bool isGnuplot, bool isReset, bool isExportOnly, bool isExtractImageOnly, const QString &extractImageOnlyExtension, const QStringList &loadStartupFiles, const QStringList &commandLineWithoutLoadStartupFiles, QWidget *parent=0)
Single constructor.
Definition: MainWindow.cpp:132
void cmdFileClose()
Close file. This is called from a file script command.
Definition: MainWindow.cpp:316
void setModelGridDisplay(const DocumentModelGridDisplay &modelGridDisplay)
Set method for DocumentModelGridDisplay.
Definition: Document.cpp:1019
Provides list of file extensions for import.
bool modeMap() const
True if document scale is set using a scale bar, otherwise using axis points.
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
void updateViewsOfSettings(const QString &activeCurve)
Update curve-specific view of settings. Private version gets active curve name from DigitizeStateCont...
QString selectedGraphCurve() const
Curve name that is currently selected in m_cmbCurve.
void setHighlightOpacity(double highlightOpacity)
Set method for highlight opacity.
Class for showing points and lines for all coordinate systems simultaneously, even though the code no...
Definition: Ghosts.h:26
DocumentModelCoords modelCoords() const
Get method for DocumentModelCoords.
Definition: Document.cpp:693
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
void slotRedo()
Move next command from list to CmdMediator. Noop if there are no more commands.
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
virtual void update(const CmdMediator &cmdMediator, const MainWindowModel &modelMainWindow, const QString &curveSelected, const Transformation &transformation)
Populate the table with the specified Curve.
void getTableStatus(bool &tableIsActive, bool &tableIsCopyable) const
Give table status so MainWindow can determine if table can be copied.
void setModelDigitizeCurve(const DocumentModelDigitizeCurve &modelDigitizeCurve)
Set method for DocumentModelDigitizeCurve.
Definition: Document.cpp:998
PointStyle pointStyle() const
Get method for PointStyle.
Definition: CurveStyle.cpp:75
ZoomFactor zoomOut(ZoomFactor currentZoomFactor, double m11, double m22, bool actionZoomFillIsChecked) const
Zoom out.
void cmdFileOpen(const QString &fileName)
Open file. This is called from a file script command.
Definition: MainWindow.cpp:342
void setDocumentAxesPointsRequired(DocumentAxesPointsRequired documentAxesPointsRequired)
Set the number of axes points required.
virtual void update(const CmdMediator &cmdMediator, const MainWindowModel &modelMainWindow, const QString &curveSelected, const Transformation &transformation)
Populate the table with the specified Curve.
CmdMediator * cmdMediator()
Accessor for commands to process the Document.
Definition: MainWindow.cpp:350
Document & document()
Provide the Document to commands, primarily for undo/redo processing.
Definition: CmdMediator.cpp:72
NonPdfReturn load(const QString &fileName, QImage &image, ImportCropping importCropping, bool isErrorReportRegressionTest) const
Try to load the specified file. Success is indicated in the function return value.
Definition: NonPdf.cpp:18
void setModelCoords(const DocumentModelCoords &modelCoords)
Set method for DocumentModelCoords.
Definition: Document.cpp:974
void wakeUp()
Enable all widgets in the status bar. This is called just after a Document becomes active...
Definition: StatusBar.cpp:343
BackgroundImage selectOriginal(BackgroundImage backgroundImage)
Make original background visible, for DigitizeStateColorPicker.
void setDirectoryImportOpenFromFilename(const QString &fileName)
Save the current Import/Open directory, after user has accepted the Import/Open dialog.
void setPixmap(bool isGnuplot, const Transformation &transformation, const DocumentModelGridRemoval &modelGridRemoval, const DocumentModelColorFilter &modelColorFilter, const QPixmap &pixmapOriginal, const QString &curveSelected)
Update the images of all states, rather than just the current state.
Check Document state.
Definition: DocumentScrub.h:15
double mapToFactor(ZoomFactor zoomFactor) const
Return the floating precision zoom factor given the enum value.
void updateAfterCommand(CmdMediator &cmdMediator, double highlightOpacity, GeometryWindow *geometryWindow, const Transformation &transformation)
Update the Points and their Curves after executing a command.
void setDelimiter(ExportDelimiter exportDelimiter)
Set method for delimiter.
void setLocale(QLocale::Language language, QLocale::Country country)
Set method for locale given attributes.
void handleKeyPress(CmdMediator *cmdMediator, Qt::Key key, bool atLeastOneSelectedItem)
See DigitizeStateAbstractBase::handleKeyPress.
void setPixmap(const QImage &image)
Set method for the background pixmap.
Definition: Document.cpp:1047
QStringList selectedPointIdentifiers(const QList< QGraphicsItem * > &items) const
Return list of selected point identifiers.
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
void showTemporaryMessage(const QString &temporaryMessage)
Show temporary message in status bar.
void retrievePoints(const Transformation &transformation, QList< QPoint > &points, QList< double > &ordinals) const
Retrieve points from clipboard.
bool load(const QString &filename, QImage &image) const
Load image from jpeg2000 file.
Definition: Jpeg2000.cpp:192
static void bindToMainWindow(MainWindow *mainWindow)
Bind to MainWindow so this class can access the command stack.
void setImageIsLoaded(CmdMediator *cmdMediator, bool imageIsLoaded)
Set the image so QGraphicsView cursor and drag mode are accessible.
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
int maximumGridLines() const
Maximum number of grid lines.
void setCoordSystemIndex(CoordSystemIndex coordSystemIndex)
Set the index of current active CoordSystem.
Definition: Document.cpp:916
void updateSettingsDigitizeCurve(const DocumentModelDigitizeCurve &modelDigitizeCurve)
Update with new curve digitization styles.
bool dragDropExport() const
Get method for drag and drop export.
void loadMainWindowModel(CmdMediator &cmdMediator, const MainWindowModel &modelMainWindow)
Replaced load method since the main window settings are independent of document, unlike other DlgSett...
This class consolidates utility routines that deal with graphics items that are getting extracted fro...
bool isModified() const
Dirty flag.
Definition: CmdMediator.cpp:82
void cmdFileExport(const QString &fileName)
Export file. This is called from a file script command.
Definition: MainWindow.cpp:324
bool smallDialogs() const
Get method for small dialogs flag.
Strategy class for exporting to a file. This strategy is external to the Document class so that class...
Definition: ExportToFile.h:25
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
CoordSystemIndex coordSystemIndex() const
Index of current active CoordSystem.
Definition: Document.cpp:312
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
void setModelExport(const DocumentModelExportFormat &modelExport)
Set method for DocumentModelExportFormat.
Definition: Document.cpp:1005
Model for DlgSettingsDigitizeCurve and CmdSettingsDigitizeCurve.
GraphicsView & view()
View for the QImage and QGraphicsItems, without const.
Affine transformation between screen and graph coordinates, based on digitized axis points...
StatusBarMode statusBarMode() const
Current mode for status bar visibility. This is tracked locally so this class knows when to hide/show...
Definition: StatusBar.h:45
ZoomControl zoomControl() const
Get method for zoom control.
QString fileExtensionTsv() const
File extension for tsv export files.
Details for a specific Point.
Definition: PointStyle.h:20
Class for exporting during regression, when the Transformation has not yet been defined.
Container for all graph curves. The axes point curve is external to this class.
Definition: CurvesGraphs.h:24
void setBackgroundImage(BackgroundImage backgroundImage)
Transition to the specified state. This method is used by classes outside of the state machine to tri...
Model for DlgSettingsColorFilter and CmdSettingsColorFilter.
GraphicsScene & scene()
Scene container for the QImage and QGraphicsItems.
void updateSettingsGridDisplay(const DocumentModelGridDisplay &modelGridDisplay)
Update with new grid display properties.
void setEnabled(bool enabled)
Show the style with semi-transparency or full-transparency to indicate if associated Curve is active ...
void updateSettingsCurveStyles(const CurveStyles &modelCurveStyles)
Update with new curve styles.
void setModelCurveStyles(const CurveStyles &modelCurveStyles)
Set method for CurveStyles.
Definition: Document.cpp:981
bool browserIsEmpty() const
When browser is empty, it is pointless to show it.
Facade class that wraps around all of the create classes for MainWindow.
Definition: CreateFacade.h:16
CurveStyles modelCurveStyles() const
Get method for CurveStyles.
Definition: Document.cpp:700
QGraphicsView class with event handling added. Typically the events are sent to the active digitizing...
Definition: GraphicsView.h:20
virtual void doCopy()
Copy the current selection to the clipboard.
Model for DlgSettingsMainWindow.
void appendNewCmd(CmdMediator *cmdMediator, QUndoCommand *cmd)
Append just-created QUndoCommand to command stack. This is called from DigitizeStateAbstractBase subc...
void create(MainWindow &mw)
Create QAction facade.
bool canRedo() const
Return true if there is a command available.
DocumentModelAxesChecker modelAxesChecker() const
Get method for DocumentModelAxesChecker.
Definition: Document.cpp:679
void updateModelSegments(const DocumentModelSegments &modelSegments)
Update the segments given the new settings.
bool canPaste(const Transformation &transformation, const QSize &viewSize) const
Return true if there is good data in the clipboard for pasting, and that operation is compatible with...
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
void resetOnLoad()
Reset, when loading a document after the first, to same state that first document was at when loaded...
void coordTextForStatusBar(QPointF cursorScreen, QString &coordsScreen, QString &coordsGraph, QString &resolutionGraph, bool usingScaleBar)
Return string descriptions of cursor coordinates for status bar.
Command for adding one or more graph points. This is for Segment Fill mode.
void setCurveSelected(bool isGnuplot, const Transformation &transformation, const DocumentModelGridRemoval &modelGridRemoval, const DocumentModelColorFilter &modelColorFilter, const QString &curveSelected)
Update the selected curve.
void resetPositionHasChangedFlags()
Reset positionHasChanged flag for all items. Typically this is done as part of mousePressEvent.
void signalDropRegression(QString)
Send drag and drop regression test url.
void fileExport(const QString &filename) const
Export to the specified file. This is called when the Transformation has not been defined...
QPixmap pixmap() const
See Document::pixmap.
void setModelColorFilter(const DocumentModelColorFilter &modelColorFilter)
Set method for DocumentModelColorFilter.
Definition: Document.cpp:956
void close()
Open Document is being closed so remove the background.
QImage imageForCurveState() const
Image for the Curve state, even if the current state is different.
Priority::Value getPriority() const
Returns unused priority.
Definition: Category.cpp:19
Model for DlgSettingsCoords and CmdSettingsCoords.
void setVisible(bool visible)
Make all grid lines visible or hidden.
Definition: GridLines.cpp:41
void updateAfterCommand()
See GraphicsScene::updateAfterCommand.
QString fileExtensionCsv() const
File extension for csv export files.
Curve that overlays the current scene so the regression-fitted curve is visible.
Definition: FittingCurve.h:16
void updateSettingsColorFilter(const DocumentModelColorFilter &modelColorFilter)
Update with new color filter properties.
Command for deleting all selected Points.
Definition: CmdDelete.h:18
void setMaximumGridLines(int maximumGridLines)
Set method for maximum number of grid lines.
void updateSettingsAxesChecker(const DocumentModelAxesChecker &modelAxesChecker)
Update with new axes indicator properties.
void updateSettingsPointMatch(const DocumentModelPointMatch &modelPointMatch)
Update with new point match properties.
void updateSettingsGeneral(const DocumentModelGeneral &modelGeneral)
Update with new general properties.
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
void redo(MainWindow &mainWindow)
Apply the next command. Requires non-empty stack.
void setPointStyle(const PointStyle &pointStyle)
Apply the PointStyle of the currently selected curve.
QImage imageFiltered() const
Background image that has been filtered for the current curve. This asserts if a curve-specific image...
Definition: MainWindow.cpp:834
void updateSettingsGridRemoval(const DocumentModelGridRemoval &modelGridRemoval)
Update with new grid removal properties.
void showTemporaryMessage(const QString &message)
Show temporary message in status bar. After a short interval the message will disappear.
Definition: StatusBar.cpp:268
void exportToFile(const DocumentModelExportFormat &modelExport, const Document &document, const MainWindowModel &modelMainWindow, const Transformation &transformation, QTextStream &str) const
Export Document points according to the settings.
double highlightOpacity() const
Get method for highlight opacity.
void updateCurveStyles(const CurveStyles &modelCurveStyles)
Update curve styles after settings changed.
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
QStringList curvesGraphsNames() const
See CurvesGraphs::curvesGraphsNames.
Definition: Document.cpp:347
void unsetColorFilterSettings()
Apply no color filter.
Dialog for setting the advanced parameters in a newly imported Document.
Wizard for setting up the checklist guide.
const ColorFilterSettings colorFilterSettings(const QString &curveName) const
Get method for copying one color filter. Cannot return just a reference or else there is a warning ab...
void handleMouseMove(CmdMediator *cmdMediator, QPointF pos)
See DigitizeStateAbstractBase::handleMouseMove.
int pdfResolution() const
Get method for resolution of imported PDF files, in dots per inch.
QDir getDirectoryExportSave() const
Get the current Export/Save directory.
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
bool transformIsDefined() const
Transform is defined when at least three axis points have been digitized.
Model for DlgSettingsAxesChecker and CmdSettingsAxesChecker.
QString filterTsv() const
QFileDialog filter for TSV files.
void updateSettingsExportFormat(const DocumentModelExportFormat &modelExport)
Update with new export properties.
Import of point data from clipboard.
virtual bool eventFilter(QObject *, QEvent *)
Catch secret keypresses.
Definition: MainWindow.cpp:371
void startLoadImage(const QUrl &url)
Start the asynchronous loading of an image from the specified url.
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
bool loadsAsDigFile(const QString &urlString) const
Returns true if specified file name can be loaded as a DIG file.
void resetOnLoad()
Reset, when loading a document after the first, to same state that first document was at when loaded...
Perform calculations to determine the next zoom setting given the current zoom setting, when zooming in or out.
bool isGnuplot() const
Get method for gnuplot flag.
Definition: MainWindow.cpp:839
CurveStyle curveStyle(const QString &curveName) const
CurveStyle in specified curve.
Definition: CurveStyles.cpp:79
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
void setZoomControl(ZoomControl zoomControl)
Set method for zoom control.
void setMainTitleBarFormat(MainTitleBarFormat mainTitleBarFormat)
Set method for MainWindow titlebar filename format.
void handleMouseRelease(CmdMediator *cmdMediator, QPointF pos)
See DigitizeStateAbstractBase::handleMouseRelease.
void captureGraphicsItems(QGraphicsScene &scene)
Take a snapshot of the graphics items.
Definition: Ghosts.cpp:26
Command queue stack.
Definition: CmdMediator.h:23
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
void setZoomFactorInitial(ZoomFactorInitial zoomFactorInitial)
Set method for initial zoom factor.
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
void signalZoom(int)
Send zoom selection, picked from menu or keystroke, to StatusBar.
Model for DlgSettingsSegments and CmdSettingsSegments.
void destroyGhosts(QGraphicsScene &scene)
Destory ghosts. Called at end of algorithm.
Definition: Ghosts.cpp:119
void setImageReplaceRenamesDocument(bool imageReplaceRenamesDocument)
Set method for image replace renames document.
void cmdFileImport(const QString &fileName)
Import file. This is called from a file script command.
Definition: MainWindow.cpp:333
void setCurvesGraphs(const CurvesGraphs &curvesGraphs)
Let CmdAbstract classes overwrite CurvesGraphs.
Definition: Document.cpp:930
void updateAxesChecker(CmdMediator &cmdMediator, const Transformation &transformation)
Apply the new DocumentModelAxesChecker.
void resizeEvent(QResizeEvent *event)
Intercept resize event so graphics scene can be appropriately resized when in Fill mode...
QString selectedCurveName() const
Currently selected curve name. This is used to set the selected curve combobox in MainWindow...
void updateSettingsCoords(const DocumentModelCoords &modelCoords)
Update with new coordinate properties.
void triggerStateTransition(bool isGnuplot, TransformationState transformationState, CmdMediator &cmdMediator, const Transformation &transformation, const QString &selectedGraphCurve)
Trigger a state transition to be performed immediately.
void loadCommands(MainWindow &mainWindow, Document &document, QXmlStreamReader &reader)
Load commands from serialized xml.
void update(const CmdMediator &cmdMediator, bool documentIsExported)
Update using current CmdMediator/Document state.
ZoomFactorInitial zoomFactorInitial() const
Get method for initial zoom factor.
File that manages a command stack for regression testing of file import/open/export/close.
Definition: FileCmdScript.h:20
void setSmallDialogs(bool smallDialogs)
Set method for small dialogs flag.
PdfReturn load(const QString &fileName, QImage &image, int resolution, ImportCropping importCropping, bool isErrorReportRegressionTest) const
Try to load the specified file. Success is indicated in the function return value.
Definition: Pdf.cpp:25
void check(MainWindow &mainWindow, const Document &document) const
Check document state.
void handleMousePress(CmdMediator *cmdMediator, QPointF pos)
See DigitizeStateAbstractBase::handleMousePress.
Add point and line handling to generic QGraphicsScene.
Definition: GraphicsScene.h:36
QString filterCsv() const
QFileDialog filter for CSV files.
QStringList fileExtensionsWithAsterisks() const
File extensions for use in file dialogs.
ImportCropping importCropping() const
Get method for import cropping.
Command for moving all selected Points by a specified translation.
Definition: CmdCopy.h:18
DocumentModelGridDisplay modelGridDisplay() const
Get method for DocumentModelGridDisplay.
Definition: Document.cpp:728
void saveXml(QXmlStreamWriter &writer) const
Save document to xml.
Definition: Document.cpp:882
QLocale locale() const
Get method for locale.
Model for DlgSettingsGridRemoval and CmdSettingsGridRemoval. The settings are unstable until the user...
QString reasonForUnsuccessfulRead() const
See Document::reasonForUnsuccessfulRead.
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
void updateSettingsSegments(const DocumentModelSegments &modelSegments)
Update with new segments properties.
Command for changing the currently selected CoordSystem.
ZoomFactor zoomIn(ZoomFactor currentZoomFactor, double m11, double m22, bool actionZoomFillIsChecked) const
Zoom in.
void showCurves(bool show, bool showAll=false, const QString &curveName="")
Show or hide all Curves (if showAll is true) or just the selected Curve (if showAll is false);...
void setPdfResolution(int resolution)
Set method for resolution of imported PDF files, in dots per inch.
Dialog to be displayed whenever some operation or processing cannot be performed since the axis point...
void updateGraphicsLinesToMatchGraphicsPoints()
Update the graphics lines so they follow the graphics points, after a drag, addition, removal, and such.
Persist the directory between successive Import/Open operations, or successive Export/Save operations...
void populateCurvesGraphs(CoordSystemIndex coordSystemIndex, CurvesGraphs &curvesGraphs)
Create entries in CurvesGraphs for each curve name that user provided.
DocumentModelGridRemoval modelGridRemoval() const
Get method for DocumentModelGridRemoval.
Definition: Document.cpp:735
void setImportCropping(ImportCropping importCropping)
Set method for import cropping.
MainWindowModel modelMainWindow() const
Get method for main window model.
QString templateHtml(CoordSystemIndex coordSystemIndex) const
Template html comprising the checklist for display.
QStringList unite(CmdMediator *cmdMediator, const QStringList &pointIdentifiersIn) const
Add.
QStringList curvesGraphsNames() const
See CurvesGraphs::curvesGraphsNames.
Definition: CmdMediator.cpp:62
void setTemplateHtml(const QString &html, const QStringList &curveNames)
Populate the browser with template html.
DocumentModelExportFormat modelExport() const
Get method for DocumentModelExportFormat.
Definition: Document.cpp:714
virtual void clear()
Clear stale information.
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
QStringList curveNames(CoordSystemIndex coordSystemIndex) const
Curve names to be placed into Document.
bool successfulRead() const
Wrapper for Document::successfulRead.
About Engauge dialog. This provides a hidden shortcut for triggering ENGAUGE_ASSERT.
Definition: DlgAbout.h:15
bool overrideCsvTsv() const
Get method for csv/tsv format override.
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
virtual void clear()
Clear stale information.
virtual void showEvent(QShowEvent *)
Processing performed after gui becomes available.
bool transformIsDefined() const
Return true if all three axis points have been defined.
void requestImmediateStateTransition(CmdMediator *cmdMediator, DigitizeState digitizeState)
Perform immediate state transition. Called from outside state machine.
virtual void doCopy()
Copy the current selection to the clipboard.
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
void updateSettingsCurveList(const CurvesGraphs &curvesGraphs)
Update with new curves.