Engauge Digitizer  2
main.cpp
1 /******************************************************************************************************
2  * (C) 2014 markummitchell@github.com. This file is part of Engauge Digitizer, which is released *
3  * under GNU General Public License version 2 (GPLv2) or (at your option) any later version. See file *
4  * LICENSE or go to gnu.org/licenses for details. Distribution requires prior written permission. *
5  ******************************************************************************************************/
6 
7 #include "ColorFilterMode.h"
8 #include "FittingCurveCoefficients.h"
9 #include "ImportImageExtensions.h"
10 #include "Logger.h"
11 #include "MainWindow.h"
12 #include <QApplication>
13 #include <QCoreApplication>
14 #include <QDebug>
15 #include <QDir>
16 #include <QFileInfo>
17 #include <QMessageBox>
18 #include <QObject>
19 #include <QProcessEnvironment>
20 #include <QStyleFactory>
21 #include "TranslatorContainer.h"
22 #include "ZoomFactor.h"
23 
24 using namespace std;
25 
26 const QString CMD_DEBUG ("debug");
27 const QString CMD_ERROR_REPORT ("errorreport");
28 const QString CMD_EXPORT_ONLY ("exportonly");
29 const QString CMD_EXTRACT_IMAGE_ONLY ("extractimageonly");
30 const QString CMD_FILE_CMD_SCRIPT ("filecmdscript");
31 const QString CMD_GNUPLOT ("gnuplot");
32 const QString CMD_HELP ("help");
33 const QString CMD_REGRESSION ("regression");
34 const QString CMD_RESET ("reset");
35 const QString CMD_STYLES ("styles"); // Not to be confused with -style option that qt handles
36 const QString DASH ("-");
37 const QString DASH_DEBUG ("-" + CMD_DEBUG);
38 const QString DASH_ERROR_REPORT ("-" + CMD_ERROR_REPORT);
39 const QString DASH_EXTRACT_IMAGE_ONLY ("-" + CMD_EXTRACT_IMAGE_ONLY);
40 const QString DASH_EXPORT_ONLY ("-" + CMD_EXPORT_ONLY);
41 const QString DASH_FILE_CMD_SCRIPT ("-" + CMD_FILE_CMD_SCRIPT);
42 const QString DASH_GNUPLOT ("-" + CMD_GNUPLOT);
43 const QString DASH_HELP ("-" + CMD_HELP);
44 const QString DASH_REGRESSION ("-" + CMD_REGRESSION);
45 const QString DASH_RESET ("-" + CMD_RESET);
46 const QString DASH_STYLES ("-" + CMD_STYLES);
47 const QString ENGAUGE_LOG_FILE (".engauge.log");
48 
49 // Prototypes
50 bool checkFileExists (const QString &file);
51 QString engaugeLogFilename ();
52 bool engaugeLogFilenameAttempt (const QString &path,
53  QString &pathAndFile);
54 void parseCmdLine (int argc,
55  char **argv,
56  bool &isDebug,
57  bool &isReset,
58  QString &errorReportFile,
59  QString &fileCmdScriptFile,
60  bool &isErrorReportRegressionTest,
61  bool &isGnuplot,
62  bool &isExportOnly,
63  bool &isExtractImageOnly,
64  QString &extractImageOnlyExtension,
65  QStringList &loadStartupFiles,
66  QStringList &commandLineWithoutLoadStartupFiles);
67 void sanityCheckLoadStartupFiles (bool isRepeatingFlag,
68  const QString &dashForRepeatingFlag,
69  const QString &errorReportFile,
70  const QString &fileCmdScriptFile,
71  const QStringList &loadStartupFiles);
72 void sanityCheckValue (bool requiredCondition,
73  const QString &arg,
74  const QString &msgUnadorned);
75 void showMessageAndQuit (const QString &msg);
76 void showStylesAndQuit ();
77 void showUsageAndQuit ();
78 
79 // Functions
80 bool checkFileExists (const QString &file)
81 {
82  QFileInfo check (file);
83  return check.exists() && check.isFile();
84 }
85 
86 QString engaugeLogFilename()
87 {
88  QString pathAndFile; // Return empty value in OSX which is unused
89 
90 #if !defined(OSX_RELEASE) && !defined(WIN_RELEASE) && !defined(APPIMAGE_RELEASE)
91  QProcessEnvironment env;
92 
93  // Make multiple attempts until a directory is found where the log file can be written
94  if (!engaugeLogFilenameAttempt (QCoreApplication::applicationDirPath(), pathAndFile)) {
95  if (!engaugeLogFilenameAttempt (env.value ("HOME"), pathAndFile)) {
96  if (!engaugeLogFilenameAttempt (env.value ("TEMP"), pathAndFile)) {
97  pathAndFile = ENGAUGE_LOG_FILE; // Current directory will have to do
98  }
99  }
100  }
101 #endif
102 
103  return pathAndFile;
104 }
105 
106 bool engaugeLogFilenameAttempt (const QString &path,
107  QString &pathAndFile)
108 {
109  bool success = false;
110 
111  // Test if file can be opened. Checking permissions on directory is unreliable in Windows/OSX
112  pathAndFile = QString ("%1%2%3")
113  .arg (path)
114  .arg (QDir::separator())
115  .arg (ENGAUGE_LOG_FILE);
116  QFile file (pathAndFile);
117  if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
118  // Success
119  file.close();
120  file.remove(); // Cleanup
121  success = true;
122  }
123 
124  return success;
125 }
126 
127 int main(int argc, char *argv[])
128 {
129  qRegisterMetaType<ColorFilterMode> ("ColorFilterMode");
130  qRegisterMetaType<FittingCurveCoefficients> ("FilterCurveCoefficients");
131  qRegisterMetaType<ZoomFactor> ("ZoomFactor");
132 
133  QApplication app(argc, argv);
134 
135  // Translations
136  TranslatorContainer translatorContainer (app); // Must exist until execution terminates
137 
138  // Command line
139  bool isDebug, isReset, isGnuplot, isErrorReportRegressionTest, isExportOnly, isExtractImageOnly;
140  QString errorReportFile, extractImageOnlyExtension, fileCmdScriptFile;
141  QStringList loadStartupFiles, commandLineWithoutLoadStartupFiles;
142  parseCmdLine (argc,
143  argv,
144  isDebug,
145  isReset,
146  errorReportFile,
147  fileCmdScriptFile,
148  isErrorReportRegressionTest,
149  isGnuplot,
150  isExportOnly,
151  isExtractImageOnly,
152  extractImageOnlyExtension,
153  loadStartupFiles,
154  commandLineWithoutLoadStartupFiles);
155 
156  // Logging
157  initializeLogging ("engauge",
158  engaugeLogFilename(),
159  isDebug);
160  LOG4CPP_INFO_S ((*mainCat)) << "main args=" << QApplication::arguments().join (" ").toLatin1().data();
161 
162  // Create and show main window
163  MainWindow w (errorReportFile,
164  fileCmdScriptFile,
165  isErrorReportRegressionTest,
166  isGnuplot,
167  isReset,
168  isExportOnly,
169  isExtractImageOnly,
170  extractImageOnlyExtension,
171  loadStartupFiles,
172  commandLineWithoutLoadStartupFiles);
173  w.show();
174 
175  // Event loop
176  return app.exec();
177 }
178 
179 void parseCmdLine (int argc,
180  char **argv,
181  bool &isDebug,
182  bool &isReset,
183  QString &errorReportFile,
184  QString &fileCmdScriptFile,
185  bool &isErrorReportRegressionTest,
186  bool &isGnuplot,
187  bool &isExportOnly,
188  bool &isExtractImageOnly,
189  QString &extractImageOnlyExtension,
190  QStringList &loadStartupFiles,
191  QStringList &commandLineWithoutLoadStartupFiles)
192 {
193  bool showUsage = false;
194 
195  ImportImageExtensions importImageExtensions;
196 
197  // State
198  bool nextIsErrorReportFile = false;
199  bool nextIsExtractImageOnly = false;
200  bool nextIsFileCmdScript = false;
201 
202  // Defaults
203  isDebug = false;
204  isReset = false;
205  errorReportFile = "";
206  fileCmdScriptFile = "";
207  isErrorReportRegressionTest = false;
208  isGnuplot = false;
209  isExportOnly = false;
210  isExtractImageOnly = false;
211  extractImageOnlyExtension = "";
212 
213  for (int i = 1; i < argc; i++) {
214 
215  bool isLoadStartupFile = false;
216 
217  if (nextIsErrorReportFile) {
218  sanityCheckValue (checkFileExists (argv [i]),
219  argv [i],
220  QObject::tr ("is not a valid file name"));
221  errorReportFile = argv [i];
222  nextIsErrorReportFile = false;
223  } else if (nextIsExtractImageOnly) {
224  sanityCheckValue (importImageExtensions.offers (argv [i]),
225  argv [i],
226  QObject::tr ("is not a valid image file extension"));
227  extractImageOnlyExtension = argv [i];
228  nextIsExtractImageOnly = false;
229  } else if (nextIsFileCmdScript) {
230  sanityCheckValue (checkFileExists (argv [i]),
231  argv [i],
232  QObject::tr ("is not a valid file name"));
233  fileCmdScriptFile = argv [i];
234  nextIsFileCmdScript = false;
235  } else if (strcmp (argv [i], DASH_DEBUG.toLatin1().data()) == 0) {
236  isDebug = true;
237  } else if (strcmp (argv [i], DASH_ERROR_REPORT.toLatin1().data()) == 0) {
238  nextIsErrorReportFile = true;
239  } else if (strcmp (argv [i], DASH_EXPORT_ONLY.toLatin1().data()) == 0) {
240  isExportOnly = true;
241  } else if (strcmp (argv [i], DASH_EXTRACT_IMAGE_ONLY.toLatin1().data()) == 0) {
242  isExtractImageOnly = true;
243  nextIsExtractImageOnly = true;
244  } else if (strcmp (argv [i], DASH_FILE_CMD_SCRIPT.toLatin1().data()) == 0) {
245  nextIsFileCmdScript = true;
246  } else if (strcmp (argv [i], DASH_GNUPLOT.toLatin1().data()) == 0) {
247  isGnuplot = true;
248  } else if (strcmp (argv [i], DASH_HELP.toLatin1().data()) == 0) {
249  showUsage = true; // User requested help
250  } else if (strcmp (argv [i], DASH_REGRESSION.toLatin1().data()) == 0) {
251  isErrorReportRegressionTest = true;
252  } else if (strcmp (argv [i], DASH_RESET.toLatin1().data()) == 0) {
253  isReset = true;
254  } else if (strcmp (argv [i], DASH_STYLES.toLatin1().data()) == 0) {
255  showStylesAndQuit ();
256  } else if (strncmp (argv [i], DASH.toLatin1().data(), 1) == 0) {
257  showUsage = true; // User entered an unrecognized token
258  } else {
259  // MainWindow will change current directory (which is often some obscure application directory),
260  // so relative paths must be changed in advance to absolute so the files can still be found
261  QString fileName = argv [i];
262  QFileInfo fInfo (fileName);
263  if (fInfo.isRelative()) {
264  fileName = fInfo.absoluteFilePath();
265  }
266 
267  isLoadStartupFile = true;
268  loadStartupFiles << fileName; // Save file name
269  }
270 
271  // keep command line arguments
272  if (!isLoadStartupFile) {
273  commandLineWithoutLoadStartupFiles << argv [i];
274  }
275  }
276 
277  // Sanity checks
278  sanityCheckLoadStartupFiles (isExportOnly,
279  DASH_EXPORT_ONLY,
280  errorReportFile,
281  fileCmdScriptFile,
282  loadStartupFiles);
283  sanityCheckLoadStartupFiles (isExtractImageOnly,
284  DASH_EXTRACT_IMAGE_ONLY,
285  errorReportFile,
286  fileCmdScriptFile,
287  loadStartupFiles);
288 
289  // Usage
290  if (showUsage || nextIsErrorReportFile || nextIsExtractImageOnly || nextIsFileCmdScript) {
291 
292  showUsageAndQuit ();
293 
294  }
295 }
296 
297 void sanityCheckLoadStartupFiles (bool isRepeatingFlag,
298  const QString &dashForRepeatingFlag,
299  const QString &errorReportFile,
300  const QString &fileCmdScriptFile,
301  const QStringList &loadStartupFiles)
302 {
303  if (isRepeatingFlag && (!errorReportFile.isEmpty() ||
304  !fileCmdScriptFile.isEmpty() ||
305  loadStartupFiles.size() == 0)) {
306 
307  // Condition that at only load files are specified, and there is at least one of them, is not satisfied so
308  // show more specific error message than showUsageAndQuit, and then quit
309  QString msg;
310  QTextStream str (&msg);
311  str << dashForRepeatingFlag.toLatin1().data() << " " << QObject::tr ("is used only with one or more load files");
312  showMessageAndQuit (msg);
313  }
314 }
315 
316 void sanityCheckValue (bool requiredCondition,
317  const QString &arg,
318  const QString &msgUnadorned)
319 {
320  if (!requiredCondition) {
321 
322  // Required condition is not satisfied. Show a more specific error message than showUsageAndQuit and then quit
323  QString msg = QString ("%1 %2")
324  .arg (arg)
325  .arg (msgUnadorned);
326  showMessageAndQuit (msg);
327  }
328 }
329 
330 void showMessageAndQuit (const QString &msg)
331 {
332  // Show message in QMessageBox instead of cout or cerr since console output is disabled in Microsoft Windows
333  QMessageBox::critical (0,
334  QObject::tr ("Engauge Digitizer"),
335  msg);
336  exit (0);
337 }
338 
339 void showStylesAndQuit ()
340 {
341  QString msg;
342  QTextStream str (&msg);
343  str << QObject::tr ("Available styles") << ": " << QStyleFactory::keys ().join (", ");
344  showMessageAndQuit (msg);
345 }
346 
347 void showUsageAndQuit ()
348 {
349  QString msg;
350  QTextStream str (&msg);
351  str << "<html>Usage: engauge "
352  << "[" << DASH_DEBUG.toLatin1().data() << "] "
353  << "[" << DASH_ERROR_REPORT.toLatin1().data() << " &lt;file&gt;] "
354  << "[" << DASH_EXPORT_ONLY.toLatin1().data() << "] "
355  << "[" << DASH_EXTRACT_IMAGE_ONLY.toLatin1().data() << " &lt;extension&gt;] "
356  << "[" << DASH_FILE_CMD_SCRIPT.toLatin1().data() << " &lt;file&gt; "
357  << "[" << DASH_GNUPLOT.toLatin1().data() << "] "
358  << "[" << DASH_HELP.toLatin1().data() << "] "
359  << "[" << DASH_REGRESSION.toLatin1().data() << "] "
360  << "[" << DASH_RESET.toLatin1().data () << "] "
361  << "[" << DASH_STYLES.toLatin1().data () << "] "
362  << "[&lt;load_file1&gt;] [&lt;load_file2&gt;] ..." << endl
363  << "<table>"
364  << "<tr>"
365  << "<td>" << DASH_DEBUG.toLatin1().data() << "</td>"
366  << "<td>"
367  << QObject::tr ("Enables extra debug information. Used for debugging").toLatin1().data()
368  << "</td>"
369  << "</tr>"
370  << "<tr>"
371  << "<td>" << DASH_ERROR_REPORT.toLatin1().data() << "</td>"
372  << "<td>"
373  << QObject::tr ("Specifies an error report file as input. Used for debugging and testing").toLatin1().data()
374  << "</td>"
375  << "</tr>"
376  << "<tr>"
377  << "<td>" << DASH_EXPORT_ONLY.toLatin1().data() << "</td>"
378  << "<td>"
379  << QObject::tr ("Export each loaded startup file, which must have all axis points defined, then stop").toLatin1().data()
380  << "</td>"
381  << "</tr>"
382  << "<tr>"
383  << "<td>" << DASH_EXTRACT_IMAGE_ONLY.toLatin1().data() << "</td>"
384  << "<td>"
385  << QObject::tr ("Extract image in each loaded startup file to a file with the specified extension, then stop").toLatin1().data()
386  << "</td>"
387  << "</tr>"
388  << "<tr>"
389  << "<td>" << DASH_FILE_CMD_SCRIPT.toLatin1().data() << "</td>"
390  << "<td>"
391  << QObject::tr ("Specifies a file command script file as input. Used for debugging and testing").toLatin1().data()
392  << "</td>"
393  << "</tr>"
394  << "<tr>"
395  << "<td>" << DASH_GNUPLOT.toLatin1().data() << "</td>"
396  << "<td>"
397  << QObject::tr ("Output diagnostic gnuplot input files. Used for debugging").toLatin1().data()
398  << "</td>"
399  << "</tr>"
400  << "<tr>"
401  << "<td>" << DASH_HELP.toLatin1().data() << "</td>"
402  << "<td>"
403  << QObject::tr ("Show this help information").toLatin1().data()
404  << "</td>"
405  << "</tr>"
406  << "<tr>"
407  << "<td>" << DASH_REGRESSION.toLatin1().data() << "</td>"
408  << "<td>"
409  << QObject::tr ("Executes the error report file or file command script. Used for regression testing").toLatin1().data()
410  << "</td>"
411  << "</tr>"
412  << "<tr>"
413  << "<td>" << DASH_RESET.toLatin1().data() << "</td>"
414  << "<td>"
415  << QObject::tr ("Removes all stored settings, including window positions. Used when windows start up offscreen").toLatin1().data()
416  << "</td>"
417  << "</tr>"
418  << "<tr>"
419  << "<td>" << DASH_STYLES.toLatin1().data() << "</td>"
420  << "<td>"
421  << QObject::tr ("Show a list of available styles that can be used with the -style command").toLatin1().data()
422  << "</td>"
423  << "</tr>"
424  << "<tr>"
425  << "<td>" << QString ("&lt;load file&gt; ").toLatin1().data() << "</td>"
426  << "<td>"
427  << QObject::tr ("File(s) to be imported or opened at startup").toLatin1().data()
428  << "</td>"
429  << "</tr>"
430  << "</table></html>";
431 
432  showMessageAndQuit (msg);
433 }
Provides list of file extensions for import.
bool offers(const QString &fileExtension) const
Return true if specified file extension is supported.
Class that stores QTranslator objects for the duration of application execution.
Main window consisting of menu, graphics scene, status bar and optional toolbars as a Single Document...
Definition: MainWindow.h:91