Seamly2D
Code documentation
mapplication.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * *
3  * Copyright (C) 2017 Seamly, LLC *
4  * *
5  * https://github.com/fashionfreedom/seamly2d *
6  * *
7  ***************************************************************************
8  **
9  ** Seamly2D is free software: you can redistribute it and/or modify
10  ** it under the terms of the GNU General Public License as published by
11  ** the Free Software Foundation, either version 3 of the License, or
12  ** (at your option) any later version.
13  **
14  ** Seamly2D is distributed in the hope that it will be useful,
15  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  ** GNU General Public License for more details.
18  **
19  ** You should have received a copy of the GNU General Public License
20  ** along with Seamly2D. If not, see <http://www.gnu.org/licenses/>.
21  **
22  **************************************************************************
23 
24  ************************************************************************
25  **
26  ** @file mapplication.cpp
27  ** @author Roman Telezhynskyi <dismine(at)gmail.com>
28  ** @date 8 7, 2015
29  **
30  ** @brief
31  ** @copyright
32  ** This source code is part of the Valentine project, a pattern making
33  ** program, whose allow create and modeling patterns of clothing.
34  ** Copyright (C) 2015 Seamly2D project
35  ** <https://github.com/fashionfreedom/seamly2d> All Rights Reserved.
36  **
37  ** Seamly2D is free software: you can redistribute it and/or modify
38  ** it under the terms of the GNU General Public License as published by
39  ** the Free Software Foundation, either version 3 of the License, or
40  ** (at your option) any later version.
41  **
42  ** Seamly2D is distributed in the hope that it will be useful,
43  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
44  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45  ** GNU General Public License for more details.
46  **
47  ** You should have received a copy of the GNU General Public License
48  ** along with Seamly2D. If not, see <http://www.gnu.org/licenses/>.
49  **
50  *************************************************************************/
51 
52 #include "mapplication.h"
53 #include "version.h"
54 #include "tmainwindow.h"
55 #include "../ifc/exception/vexceptionobjecterror.h"
56 #include "../ifc/exception/vexceptionbadid.h"
57 #include "../ifc/exception/vexceptionconversionerror.h"
58 #include "../ifc/exception/vexceptionemptyparameter.h"
59 #include "../ifc/exception/vexceptionwrongid.h"
60 #include "../vmisc/logging.h"
61 #include "../vmisc/vsysexits.h"
62 #include "../vmisc/diagnostic.h"
63 #include "../qmuparser/qmuparsererror.h"
64 
65 #include <Qt>
66 #include <QDir>
67 #include <QFileOpenEvent>
68 #include <QLocalSocket>
69 #include <QResource>
70 #include <QTranslator>
71 #include <QPointer>
72 #include <QLocalServer>
73 #include <QMessageBox>
74 #include <iostream>
75 #include <QGridLayout>
76 #include <QSpacerItem>
77 #include <QThread>
78 #include <QStandardPaths>
79 
80 QT_WARNING_PUSH
81 QT_WARNING_DISABLE_CLANG("-Wmissing-prototypes")
82 QT_WARNING_DISABLE_INTEL(1418)
83 
84 Q_LOGGING_CATEGORY(mApp, "m.application")
85 
87 
88 #include <QCommandLineParser>
89 
90 //---------------------------------------------------------------------------------------------------------------------
91 inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
92 {
93  Q_UNUSED(context)
94 
95  // Why on earth didn't Qt want to make failed signal/slot connections qWarning?
96  if ((type == QtDebugMsg) && msg.contains(QStringLiteral("::connect")))
97  {
98  type = QtWarningMsg;
99  }
100 
101 #if defined(V_NO_ASSERT)
102  // I have decided to hide this annoing message for release builds.
103  if ((type == QtWarningMsg) && msg.contains(QStringLiteral("QSslSocket: cannot resolve")))
104  {
105  type = QtDebugMsg;
106  }
107 
108  if ((type == QtWarningMsg) && msg.contains(QStringLiteral("setGeometry: Unable to set geometry")))
109  {
110  type = QtDebugMsg;
111  }
112 #endif //defined(V_NO_ASSERT)
113 
114 #if defined(Q_OS_MAC)
115  // Hide Qt bug 'Assertion when reading an icns file'
116  // https://bugreports.qt.io/browse/QTBUG-45537
117  // Remove after Qt fix will be released
118  if ((type == QtWarningMsg) && msg.contains(QStringLiteral("QICNSHandler::read()")))
119  {
120  type = QtDebugMsg;
121  }
122 
123  // See issue #568
124  if (msg.contains(QStringLiteral("Error receiving trust for a CA certificate")))
125  {
126  type = QtDebugMsg;
127  }
128 #endif
129 
130  // this is another one that doesn't make sense as just a debug message. pretty serious
131  // sign of a problem
132  // http://www.developer.nokia.com/Community/Wiki/QPainter::begin:Paint_device_returned_engine_%3D%3D_0_(Known_Issue)
133  if ((type == QtDebugMsg) && msg.contains(QStringLiteral("QPainter::begin"))
134  && msg.contains(QStringLiteral("Paint device returned engine")))
135  {
136  type = QtWarningMsg;
137  }
138 
139  // This qWarning about "Cowardly refusing to send clipboard message to hung application..."
140  // is something that can easily happen if you are debugging and the application is paused.
141  // As it is so common, not worth popping up a dialog.
142  if ((type == QtWarningMsg) && msg.contains(QStringLiteral("QClipboard::event"))
143  && msg.contains(QStringLiteral("Cowardly refusing")))
144  {
145  type = QtDebugMsg;
146  }
147 
148  // only the GUI thread should display message boxes. If you are
149  // writing a multithreaded application and the error happens on
150  // a non-GUI thread, you'll have to queue the message to the GUI
151  QCoreApplication *instance = QCoreApplication::instance();
152  const bool isGuiThread = instance && (QThread::currentThread() == instance->thread());
153 
154  switch (type)
155  {
156  case QtDebugMsg:
157  vStdOut() << QApplication::translate("mNoisyHandler", "DEBUG:") << msg << "\n";
158  return;
159  case QtWarningMsg:
160  vStdErr() << QApplication::translate("mNoisyHandler", "WARNING:") << msg << "\n";
161  break;
162  case QtCriticalMsg:
163  vStdErr() << QApplication::translate("mNoisyHandler", "CRITICAL:") << msg << "\n";
164  break;
165  case QtFatalMsg:
166  vStdErr() << QApplication::translate("mNoisyHandler", "FATAL:") << msg << "\n";
167  break;
168  #if QT_VERSION > QT_VERSION_CHECK(5, 4, 2)
169  case QtInfoMsg:
170  vStdOut() << QApplication::translate("mNoisyHandler", "INFO:") << msg << "\n";
171  break;
172  #endif
173  default:
174  break;
175  }
176 
177  if (isGuiThread)
178  {
179  //fixme: trying to make sure there are no save/load dialogs are opened, because error message during them will
180  //lead to crash
181  const bool topWinAllowsPop = (QApplication::activeModalWidget() == nullptr) ||
182  !QApplication::activeModalWidget()->inherits("QFileDialog");
183  QMessageBox messageBox;
184 
185  switch (type)
186  {
187  case QtWarningMsg:
188  messageBox.setWindowTitle(QApplication::translate("mNoisyHandler", "Warning"));
189  messageBox.setIcon(QMessageBox::Warning);
190  break;
191  case QtCriticalMsg:
192  messageBox.setWindowTitle(QApplication::translate("mNoisyHandler", "Critical Error"));
193  messageBox.setIcon(QMessageBox::Critical);
194  break;
195  case QtFatalMsg:
196  messageBox.setWindowTitle(QApplication::translate("mNoisyHandler", "Fatal Error"));
197  messageBox.setIcon(QMessageBox::Critical);
198  break;
199  #if QT_VERSION > QT_VERSION_CHECK(5, 4, 2)
200  case QtInfoMsg:
201  messageBox.setWindowTitle(QApplication::translate("mNoisyHandler", "Information"));
202  messageBox.setIcon(QMessageBox::Information);
203  break;
204  #endif
205  case QtDebugMsg:
206  Q_UNREACHABLE(); //-V501
207  break;
208  default:
209  break;
210  }
211 
212  if (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg)
213  {
214  if (not qApp->IsTestMode())
215  {
216  if (topWinAllowsPop)
217  {
218  messageBox.setText(msg);
219  messageBox.setStandardButtons(QMessageBox::Ok);
220  messageBox.setWindowModality(Qt::ApplicationModal);
221  messageBox.setModal(true);
222  #ifndef QT_NO_CURSOR
223  QGuiApplication::setOverrideCursor(Qt::ArrowCursor);
224  #endif
225  messageBox.setWindowFlags(messageBox.windowFlags() & ~Qt::WindowContextHelpButtonHint);
226  messageBox.exec();
227  #ifndef QT_NO_CURSOR
228  QGuiApplication::restoreOverrideCursor();
229  #endif
230  }
231  }
232  }
233 
234  if (QtFatalMsg == type)
235  {
236  abort();
237  }
238  }
239  else
240  {
241  if (type != QtDebugMsg && type != QtWarningMsg)
242  {
243  abort(); // be NOISY unless overridden!
244  }
245  }
246 }
247 
248 //---------------------------------------------------------------------------------------------------------------------
249 MApplication::MApplication(int &argc, char **argv)
250  : VAbstractApplication(argc, argv)
251  , mainWindows()
252  , localServer(nullptr)
253  , trVars(nullptr)
254  , dataBase(QPointer<MeasurementDatabaseDialog>())
255  , testMode(false)
256 {
257  //setApplicationDisplayName(VER_PRODUCTNAME_STR);
258  setApplicationName(VER_INTERNALNAME_ME_STR);
259  setOrganizationName(VER_COMPANYNAME_STR);
260  setOrganizationDomain(VER_COMPANYDOMAIN_STR);
261  // Setting the Application version
262  setApplicationVersion(APP_VERSION_STR);
263  // We have been running SeamlyMe in two different cases.
264  // The first inside own bundle where info.plist is works fine, but the second,
265  // when we run inside Seamly2D's bundle, require direct setting the icon.
266  setWindowIcon(QIcon(":/seamlymeicon/64x64/logo.png"));
267 }
268 
269 //---------------------------------------------------------------------------------------------------------------------
271 {
272  for (int i = 0; i < mainWindows.size(); ++i)
273  {
274  TMainWindow *window = mainWindows.at(i);
275  delete window;
276  }
277 
278  delete trVars;
279  if (not dataBase.isNull())
280  {
281  delete dataBase;
282  }
283 }
284 
285 //---------------------------------------------------------------------------------------------------------------------
286 /**
287  * @brief notify Reimplemented from QApplication::notify().
288  * @param receiver receiver.
289  * @param event event.
290  * @return value that is returned from the receiver's event handler.
291  */
292 // reimplemented from QApplication so we can throw exceptions in slots
293 bool MApplication::notify(QObject *receiver, QEvent *event)
294 {
295  try
296  {
297  return QApplication::notify(receiver, event);
298  }
299  catch (const VExceptionObjectError &e)
300  {
301  qCCritical(mApp, "%s\n\n%s\n\n%s", qUtf8Printable(tr("Error parsing file. Program will be terminated.")), //-V807
302  qUtf8Printable(e.ErrorMessage()), qUtf8Printable(e.DetailedInformation()));
303  exit(V_EX_DATAERR);
304  }
305  catch (const VExceptionBadId &e)
306  {
307  qCCritical(mApp, "%s\n\n%s\n\n%s", qUtf8Printable(tr("Error bad id. Program will be terminated.")),
308  qUtf8Printable(e.ErrorMessage()), qUtf8Printable(e.DetailedInformation()));
309  exit(V_EX_DATAERR);
310  }
311  catch (const VExceptionConversionError &e)
312  {
313  qCCritical(mApp, "%s\n\n%s\n\n%s", qUtf8Printable(tr("Error can't convert value. Program will be terminated.")),
314  qUtf8Printable(e.ErrorMessage()), qUtf8Printable(e.DetailedInformation()));
315  exit(V_EX_DATAERR);
316  }
317  catch (const VExceptionEmptyParameter &e)
318  {
319  qCCritical(mApp, "%s\n\n%s\n\n%s", qUtf8Printable(tr("Error empty parameter. Program will be terminated.")),
320  qUtf8Printable(e.ErrorMessage()), qUtf8Printable(e.DetailedInformation()));
321  exit(V_EX_DATAERR);
322  }
323  catch (const VExceptionWrongId &e)
324  {
325  qCCritical(mApp, "%s\n\n%s\n\n%s", qUtf8Printable(tr("Error wrong id. Program will be terminated.")),
326  qUtf8Printable(e.ErrorMessage()), qUtf8Printable(e.DetailedInformation()));
327  exit(V_EX_DATAERR);
328  }
329  catch (const VExceptionToolWasDeleted &e)
330  {
331  qCCritical(mApp, "%s\n\n%s\n\n%s",
332  qUtf8Printable("Unhadled deleting tool. Continue use object after deleting!"),
333  qUtf8Printable(e.ErrorMessage()), qUtf8Printable(e.DetailedInformation()));
334  exit(V_EX_DATAERR);
335  }
336  catch (const VException &e)
337  {
338  qCCritical(mApp, "%s\n\n%s\n\n%s", qUtf8Printable(tr("Something's wrong!!")),
339  qUtf8Printable(e.ErrorMessage()), qUtf8Printable(e.DetailedInformation()));
340  return true;
341  }
342  // These last two cases special. I found that we can't show here modal dialog with error message.
343  // Somehow program doesn't waite untile an error dialog will be closed. But if ignore this program will hang.
344  catch (const qmu::QmuParserError &e)
345  {
346  qCCritical(mApp, "%s", qUtf8Printable(tr("Parser error: %1. Program will be terminated.").arg(e.GetMsg())));
347  exit(V_EX_DATAERR);
348  }
349  catch (std::exception &e)
350  {
351  qCCritical(mApp, "%s", qUtf8Printable(tr("Exception thrown: %1. Program will be terminated.").arg(e.what())));
352  exit(V_EX_SOFTWARE);
353  }
354  return false;
355 }
356 
357 //---------------------------------------------------------------------------------------------------------------------
359 {
360  return testMode;
361 }
362 
363 //---------------------------------------------------------------------------------------------------------------------
364 /**
365  * @brief IsAppInGUIMode little hack that allow to have access to application state from VAbstractApplication class.
366  */
368 {
369  return IsTestMode();
370 }
371 
372 //---------------------------------------------------------------------------------------------------------------------
374 {
375  Clean();
376  if (mainWindows.isEmpty())
377  {
378  NewMainWindow();
379  }
380  return mainWindows[0];
381 }
382 
383 //---------------------------------------------------------------------------------------------------------------------
385 {
386  Clean();
387  QList<TMainWindow*> list;
388  for (int i = 0; i < mainWindows.count(); ++i)
389  {
390  list.append(mainWindows.at(i));
391  }
392  return list;
393 }
394 
395 //---------------------------------------------------------------------------------------------------------------------
397 {
398  qInstallMessageHandler(noisyFailureMsgHandler);
399 
400  OpenSettings();
402  QDir().mkpath(settings->GetDefPathTemplate());
403  QDir().mkpath(settings->GetDefPathIndividualMeasurements());
404  QDir().mkpath(settings->GetDefPathMultisizeMeasurements());
405  QDir().mkpath(settings->GetDefPathLabelTemplate());
406 
407  qCDebug(mApp, "Version: %s", qUtf8Printable(APP_VERSION_STR));
408  qCDebug(mApp, "Build revision: %s", BUILD_REVISION);
409  qCDebug(mApp, "%s", qUtf8Printable(buildCompatibilityString()));
410  qCDebug(mApp, "Built on %s at %s", __DATE__, __TIME__);
411  qCDebug(mApp, "Command-line arguments: %s", qUtf8Printable(arguments().join(", ")));
412  qCDebug(mApp, "Process ID: %s", qUtf8Printable(QString().setNum(applicationPid())));
413 
414  loadTranslations(QLocale().name());// By default the console version uses system locale
415 
416  static const char * GENERIC_ICON_TO_CHECK = "document-open";
417  if (QIcon::hasThemeIcon(GENERIC_ICON_TO_CHECK) == false)
418  {
419  //If there is no default working icon theme then we should
420  //use an icon theme that we provide via a .qrc file
421  //This case happens under Windows and Mac OS X
422  //This does not happen under GNOME or KDE
423  QIcon::setThemeName("win.icon.theme");
424  }
425 }
426 
427 //---------------------------------------------------------------------------------------------------------------------
429 {
430  return trVars;
431 }
432 
433 //---------------------------------------------------------------------------------------------------------------------
435 {
436  if (trVars != nullptr)
437  {
438  trVars->Retranslate();
439  }
440  else
441  {
442  trVars = new VTranslateVars();
443  }
444 }
445 
446 //---------------------------------------------------------------------------------------------------------------------
447 bool MApplication::event(QEvent *e)
448 {
449  switch(e->type())
450  {
451  // In Mac OS X the QFileOpenEvent event is generated when user perform "Open With" from Finder (this event is
452  // Mac specific).
453  case QEvent::FileOpen:
454  {
455  QFileOpenEvent *fileOpenEvent = static_cast<QFileOpenEvent *>(e);
456  const QString macFileOpen = fileOpenEvent->file();
457  if(not macFileOpen.isEmpty())
458  {
459  TMainWindow *mw = MainWindow();
460  if (mw)
461  {
462  mw->LoadFile(macFileOpen); // open file in existing window
463  }
464  return true;
465  }
466  break;
467  }
468 #if defined(Q_OS_MAC)
469  case QEvent::ApplicationActivate:
470  {
471  Clean();
472  TMainWindow *mw = MainWindow();
473  if (mw && not mw->isMinimized())
474  {
475  mw->show();
476  }
477  return true;
478  }
479 #endif //defined(Q_OS_MAC)
480  default:
481  return VAbstractApplication::event(e);
482  }
483  return VAbstractApplication::event(e);
484 }
485 
486 //---------------------------------------------------------------------------------------------------------------------
488 {
489  settings = new VSeamlyMeSettings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(),
490  QCoreApplication::applicationName(), this);
491 }
492 
493 //---------------------------------------------------------------------------------------------------------------------
495 {
496  SCASSERT(settings != nullptr)
497  return qobject_cast<VSeamlyMeSettings *>(settings);
498 }
499 
500 //---------------------------------------------------------------------------------------------------------------------
502 {
503  if (dataBase.isNull())
504  {
506  dataBase->setAttribute(Qt::WA_DeleteOnClose, true);
507  dataBase->setModal(false);
508  dataBase->show();
509  }
510  else
511  {
512  dataBase->activateWindow();
513  }
514 }
515 
516 //---------------------------------------------------------------------------------------------------------------------
518 {
519  if (not dataBase.isNull())
520  {
521  dataBase->retranslateGroups();
522  }
523 }
524 
525 //---------------------------------------------------------------------------------------------------------------------
527 {
529  for (int i=0; i < list.size(); ++i)
530  {
531  list.at(i)->RetranslateTable();
532  }
533 }
534 
535 //---------------------------------------------------------------------------------------------------------------------
536 void MApplication::ParseCommandLine(const SocketConnection &connection, const QStringList &arguments)
537 {
538  QCommandLineParser parser;
539  parser.setApplicationDescription(tr("Seamly2D's measurements editor."));
540  parser.addHelpOption();
541  parser.addVersionOption();
542  parser.addPositionalArgument("filename", tr("The measurement file."));
543  //-----
544  QCommandLineOption heightOption(QStringList() << "e" << "height",
545  tr("Open with the base height. Valid values: %1cm.")
546  .arg(VMeasurement::WholeListHeights(Unit::Cm).join(", ")),
547  tr("The base height"));
548  parser.addOption(heightOption);
549  //-----
550  QCommandLineOption sizeOption(QStringList() << "s" << "size",
551  tr("Open with the base size. Valid values: %1cm.").arg(VMeasurement::WholeListSizes(Unit::Cm).join(", ")),
552  tr("The base size"));
553  parser.addOption(sizeOption);
554  //-----
555  QCommandLineOption unitOption(QStringList() << "u" << "unit",
556  tr("Set pattern file unit: cm, mm, inch."),
557  tr("The pattern unit"));
558  parser.addOption(unitOption);
559  //-----
560  QCommandLineOption testOption(QStringList() << "test",
561  tr("Use for unit testing. Run the program and open a file without showing the main window."));
562  parser.addOption(testOption);
563  //-----
564  QCommandLineOption scalingOption(QStringList() << LONG_OPTION_NO_HDPI_SCALING,
565  tr("Disable high dpi scaling. Call this option if has problem with scaling (by default scaling enabled). "
566  "Alternatively you can use the %1 environment variable.").arg("QT_AUTO_SCREEN_SCALE_FACTOR=0"));
567  parser.addOption(scalingOption);
568  //-----
569  parser.process(arguments);
570 
571  bool flagHeight = false;
572  bool flagSize = false;
573  bool flagUnit = false;
574 
575  int size = 0;
576  int height = 0;
577  Unit unit = Unit::Cm;
578 
579  if (parser.isSet(heightOption))
580  {
581  const QString heightValue = parser.value(heightOption);
582  if (VMeasurement::IsGradationHeightValid(heightValue))
583  {
584  flagHeight = true;
585  height = heightValue.toInt();
586  }
587  else
588  {
589  qCCritical(mApp, "%s\n",
590  qPrintable(tr("Invalid base height argument. Must be %1cm.")
591  .arg(VMeasurement::WholeListHeights(Unit::Cm).join(", "))));
592  parser.showHelp(V_EX_USAGE);
593  }
594  }
595 
596  if (parser.isSet(sizeOption))
597  {
598  const QString sizeValue = parser.value(sizeOption);
599  if (VMeasurement::IsGradationSizeValid(sizeValue))
600  {
601  flagSize = true;
602  size = sizeValue.toInt();
603  }
604  else
605  {
606  qCCritical(mApp, "%s\n",
607  qPrintable(tr("Invalid base size argument. Must be %1cm.")
608  .arg(VMeasurement::WholeListSizes(Unit::Cm).join(", "))));
609  parser.showHelp(V_EX_USAGE);
610  }
611  }
612 
613  {
614  const QString unitValue = parser.value(unitOption);
615  if (not unitValue.isEmpty())
616  {
617 
618  const QStringList units = QStringList() << unitMM << unitCM << unitINCH;
619  if (units.contains(unitValue))
620  {
621  flagUnit = true;
622  unit = StrToUnits(unitValue);
623  }
624  else
625  {
626  qCCritical(mApp, "%s\n", qPrintable(tr("Invalid base size argument. Must be cm, mm or inch.")));
627  parser.showHelp(V_EX_USAGE);
628  }
629  }
630  }
631 
632  testMode = parser.isSet(testOption);
633 
634  if (not testMode && connection == SocketConnection::Client)
635  {
636  const QString serverName = QCoreApplication::applicationName();
637  QLocalSocket socket;
638  socket.connectToServer(serverName);
639  if (socket.waitForConnected(1000))
640  {
641  qCDebug(mApp, "Connected to the server '%s'", qUtf8Printable(serverName));
642  QTextStream stream(&socket);
643  stream << QCoreApplication::arguments().join(";;");
644  stream.flush();
645  socket.waitForBytesWritten();
646  qApp->exit(V_EX_OK);
647  return;
648  }
649 
650  qCDebug(mApp, "Can't establish connection to the server '%s'", qUtf8Printable(serverName));
651 
652  localServer = new QLocalServer(this);
653  connect(localServer, &QLocalServer::newConnection, this, &MApplication::NewLocalSocketConnection);
654  if (not localServer->listen(serverName))
655  {
656  qCDebug(mApp, "Can't begin to listen for incoming connections on name '%s'",
657  qUtf8Printable(serverName));
658  if (localServer->serverError() == QAbstractSocket::AddressInUseError)
659  {
660  QLocalServer::removeServer(serverName);
661  if (not localServer->listen(serverName))
662  {
663  qCWarning(mApp, "%s",
664  qUtf8Printable(tr("Can't begin to listen for incoming connections on name '%1'").arg(serverName)));
665  }
666  }
667  }
668 
669  loadTranslations(SeamlyMeSettings()->GetLocale());
670  }
671 
672  const QStringList args = parser.positionalArguments();
673  if (args.count() > 0)
674  {
675  if (testMode && args.count() > 1)
676  {
677  qCCritical(mApp, "%s\n", qPrintable(tr("Test mode doesn't support Opening several files.")));
678  parser.showHelp(V_EX_USAGE);
679  }
680 
681  for (int i = 0; i < args.size(); ++i)
682  {
683  NewMainWindow();
684  if (not MainWindow()->LoadFile(args.at(i)))
685  {
686  if (testMode)
687  {
688  return; // process only one input file
689  }
690  delete MainWindow();
691  continue;
692  }
693 
694  if (flagSize)
695  {
696  MainWindow()->SetBaseMSize(size);
697  }
698 
699  if (flagHeight)
700  {
701  MainWindow()->SetBaseMHeight(height);
702  }
703 
704  if (flagUnit)
705  {
706  MainWindow()->SetPUnit(unit);
707  }
708  }
709  }
710  else
711  {
712  if (not testMode)
713  {
714  NewMainWindow();
715  }
716  else
717  {
718  qCCritical(mApp, "%s\n", qPrintable(tr("Please, provide one input file.")));
719  parser.showHelp(V_EX_USAGE);
720  }
721  }
722 
723  if (testMode)
724  {
725  qApp->exit(V_EX_OK); // close program after processing in console mode
726  }
727 }
728 
729 //---------------------------------------------------------------------------------------------------------------------
731 {
732  TMainWindow *seamlyme = new TMainWindow();
733  mainWindows.prepend(seamlyme);
734  if (not qApp->IsTestMode())
735  {
736  seamlyme->show();
737  }
738  return seamlyme;
739 }
740 
741 //---------------------------------------------------------------------------------------------------------------------
743 {
745 }
746 
747 //---------------------------------------------------------------------------------------------------------------------
749 {
750  QLocalSocket *socket = localServer->nextPendingConnection();
751  if (not socket)
752  {
753  return;
754  }
755  socket->waitForReadyRead(1000);
756  QTextStream stream(socket);
757  const QString arg = stream.readAll();
758  if (not arg.isEmpty())
759  {
760  ParseCommandLine(SocketConnection::Server, arg.split(";;"));
761  }
762  delete socket;
763  MainWindow()->raise();
764  MainWindow()->activateWindow();
765 }
766 
767 //---------------------------------------------------------------------------------------------------------------------
769 {
770  // cleanup any deleted main windows first
771  for (int i = mainWindows.count() - 1; i >= 0; --i)
772  {
773  if (mainWindows.at(i).isNull())
774  {
775  mainWindows.removeAt(i);
776  }
777  }
778 }
QLocalServer * localServer
Definition: mapplication.h:114
virtual bool notify(QObject *receiver, QEvent *event) Q_DECL_OVERRIDE
notify Reimplemented from QApplication::notify().
void NewLocalSocketConnection()
void RetranslateTables()
virtual bool IsAppInGUIMode() const Q_DECL_OVERRIDE
IsAppInGUIMode little hack that allow to have access to application state from VAbstractApplication c...
bool IsTestMode() const
virtual void OpenSettings() Q_DECL_OVERRIDE
MApplication(int &argc, char **argv)
QList< TMainWindow * > MainWindows()
void InitOptions()
virtual ~MApplication() Q_DECL_OVERRIDE
virtual const VTranslateVars * TrVars() Q_DECL_OVERRIDE
virtual void InitTrVars() Q_DECL_OVERRIDE
void ShowDataBase()
void retranslateGroups()
virtual bool event(QEvent *e) Q_DECL_OVERRIDE
QPointer< MeasurementDatabaseDialog > dataBase
Definition: mapplication.h:116
TMainWindow * MainWindow()
VTranslateVars * trVars
Definition: mapplication.h:115
VSeamlyMeSettings * SeamlyMeSettings()
QList< QPointer< TMainWindow > > mainWindows
Definition: mapplication.h:113
TMainWindow * NewMainWindow()
void ParseCommandLine(const SocketConnection &connection, const QStringList &arguments)
void SetBaseMSize(int size)
bool LoadFile(const QString &path)
void SetPUnit(Unit unit)
void SetBaseMHeight(int height)
VCommonSettings * settings
settings pointer to settings. Help hide constructor creation settings. Make make code more readable.
void loadTranslations(const QString &locale)
static QString GetDefPathMultisizeMeasurements()
static QString GetDefPathTemplate()
static QString GetDefPathIndividualMeasurements()
static QString GetDefPathLabelTemplate()
The VExceptionBadId class for exception bad id.
The VExceptionConversionError class for exception of conversion error.
The VExceptionEmptyParameter class for exception empty parameter.
The VExceptionObjectError class for exception object error.
virtual QString ErrorMessage() const Q_DECL_OVERRIDE
ErrorMessage return main error message.
virtual QString DetailedInformation() const Q_DECL_OVERRIDE
DetailedInformation return detailed information about error.
The VExceptionWrongId class for exception wrong id.
The VException class parent for all exception. Could be use for abstract exception.
Definition: vexception.h:66
virtual const char * what() const Q_DECL_OVERRIDE
Definition: vexception.cpp:161
static QStringList WholeListSizes(Unit patternUnit)
static QStringList WholeListHeights(Unit patternUnit)
static bool IsGradationSizeValid(const QString &size)
static bool IsGradationHeightValid(const QString &height)
virtual void Retranslate() Q_DECL_OVERRIDE
Error class of the parser.
Unit StrToUnits(const QString &unit)
Definition: def.cpp:670
const QString unitMM
Definition: def.cpp:200
const QString LONG_OPTION_NO_HDPI_SCALING
Definition: def.cpp:535
const QString unitCM
Definition: def.cpp:201
const QString unitINCH
Definition: def.cpp:202
#define SCASSERT(cond)
Definition: def.h:317
Unit
Definition: def.h:105
QT_WARNING_PUSH QT_WARNING_POP void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
SocketConnection
Definition: mapplication.h:70
QString buildCompatibilityString()
#define VER_COMPANYDOMAIN_STR
#define VER_COMPANYNAME_STR
const QString APP_VERSION_STR
#define VER_INTERNALNAME_ME_STR
Definition: version.h:57
#define qApp
Definition: vapplication.h:67
#define translate(context, source)
Definition: vcmdexport.cpp:41
static const auto V_EX_USAGE
Definition: vsysexits.h:65
static const auto V_EX_DATAERR
Definition: vsysexits.h:68
QTextStream & vStdOut()
Definition: vsysexits.h:114
static const auto V_EX_OK
Definition: vsysexits.h:63
static const auto V_EX_SOFTWARE
Definition: vsysexits.h:79
QTextStream & vStdErr()
Definition: vsysexits.h:106