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