53 #include "../ifc/exception/vexceptionobjecterror.h"
54 #include "../ifc/exception/vexceptionbadid.h"
55 #include "../ifc/exception/vexceptionconversionerror.h"
56 #include "../ifc/exception/vexceptionemptyparameter.h"
57 #include "../ifc/exception/vexceptionwrongid.h"
58 #include "../vwidgets/vmaingraphicsview.h"
59 #include "../version.h"
60 #include "../vmisc/logging.h"
61 #include "../vmisc/vmath.h"
62 #include "../qmuparser/qmuparsererror.h"
63 #include "../mainwindow.h"
69 #include <QTemporaryFile>
71 #include <QTemporaryFile>
73 #include <QStandardPaths>
74 #include <QMessageBox>
77 #include <QtXmlPatterns>
81 QT_WARNING_DISABLE_CLANG(
"-Wmissing-prototypes")
82 QT_WARNING_DISABLE_INTEL(1418)
84 Q_LOGGING_CATEGORY(vApp, "v.application")
94 if ((type == QtDebugMsg) && msg.contains(QStringLiteral(
"::connect")))
99 #if defined(V_NO_ASSERT)
101 if ((type == QtWarningMsg) && msg.contains(QStringLiteral(
"QSslSocket: cannot resolve")))
106 if ((type == QtWarningMsg) && msg.contains(QStringLiteral(
"setGeometry: Unable to set geometry")))
112 #if defined(Q_OS_MAC)
116 if ((type == QtWarningMsg) && msg.contains(QStringLiteral(
"QICNSHandler::read()")))
122 if (msg.contains(QStringLiteral(
"Error receiving trust for a CA certificate")))
131 if ((type == QtDebugMsg) && msg.contains(QStringLiteral(
"QPainter::begin"))
132 && msg.contains(QStringLiteral(
"Paint device returned engine")))
140 if ((type == QtWarningMsg) && msg.contains(QStringLiteral(
"QClipboard::event"))
141 && msg.contains(QStringLiteral(
"Cowardly refusing")))
149 QCoreApplication *instance = QCoreApplication::instance();
150 const bool isGuiThread = instance && (QThread::currentThread() == instance->thread());
153 QString debugdate =
"[" + QDateTime::currentDateTime().toString(QStringLiteral(
"yyyy.MM.dd hh:mm:ss"));
158 debugdate += QString(
":DEBUG:%1(%2)] %3: %4: %5").arg(context.file).arg(context.line)
159 .arg(context.function).arg(context.category).arg(msg);
163 debugdate += QString(
":WARNING:%1(%2)] %3: %4: %5").arg(context.file).arg(context.line)
164 .arg(context.function).arg(context.category).arg(msg);
168 debugdate += QString(
":CRITICAL:%1(%2)] %3: %4: %5").arg(context.file).arg(context.line)
169 .arg(context.function).arg(context.category).arg(msg);
173 debugdate += QString(
":FATAL:%1(%2)] %3: %4: %5").arg(context.file).arg(context.line)
174 .arg(context.function).arg(context.category).arg(msg);
177 #if QT_VERSION > QT_VERSION_CHECK(5, 4, 2)
179 debugdate += QString(
":INFO:%1(%2)] %3: %4: %5").arg(context.file).arg(context.line)
180 .arg(context.function).arg(context.category).arg(msg);
188 (*
qApp->LogFile()) << debugdate << Qt::endl;
195 const bool topWinAllowsPop = (QApplication::activeModalWidget() ==
nullptr) ||
196 !QApplication::activeModalWidget()->inherits(
"QFileDialog");
198 QMessageBox messageBox;
204 messageBox.setIcon(QMessageBox::Warning);
208 messageBox.setIcon(QMessageBox::Critical);
212 messageBox.setIcon(QMessageBox::Critical);
214 #if QT_VERSION > QT_VERSION_CHECK(5, 4, 2)
217 messageBox.setIcon(QMessageBox::Information);
225 if (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg)
231 messageBox.setText(msg);
232 messageBox.setStandardButtons(QMessageBox::Ok);
233 messageBox.setWindowModality(Qt::ApplicationModal);
234 messageBox.setModal(
true);
236 QGuiApplication::setOverrideCursor(Qt::ArrowCursor);
238 messageBox.setWindowFlags(messageBox.windowFlags() & ~Qt::WindowContextHelpButtonHint);
241 QGuiApplication::restoreOverrideCursor();
247 if (QtFatalMsg == type)
254 if( QtDebugMsg != type && QtWarningMsg != type )
263 #if defined(Q_OS_WIN) && defined(Q_CC_GNU)
264 const QString VApplication::GistFileName = QStringLiteral(
"gist.json");
278 , autoSaveTimer(nullptr)
301 qCDebug(vApp,
"Application closing.");
302 qInstallMessageHandler(
nullptr);
314 qCDebug(vApp,
"Open new detached process.");
315 if (fileName.isEmpty())
317 qCDebug(vApp,
"New process without arguments. program = %s",
318 qUtf8Printable(QCoreApplication::applicationFilePath()));
320 if (QProcess::startDetached(QCoreApplication::applicationFilePath(), QStringList()))
322 qCDebug(vApp,
"The process was started successfully.");
326 qCWarning(vApp,
"Could not run process. The operation timed out or an error occurred.");
331 const QString run = QString(
"\"%1\" \"%2\"").arg(QCoreApplication::applicationFilePath()).arg(fileName);
332 qCDebug(vApp,
"New process with arguments. program = %s", qUtf8Printable(run));
334 if (QProcess::startDetached(QCoreApplication::applicationFilePath(), QStringList{fileName}))
336 qCDebug(vApp,
"The process was started successfully.");
340 qCWarning(vApp,
"Could not run process. The operation timed out or an error occurred.");
357 return QApplication::notify(receiver,
event);
361 qCCritical(vApp,
"%s\n\n%s\n\n%s", qUtf8Printable(tr(
"Error parsing file. Program will be terminated.")),
367 qCCritical(vApp,
"%s\n\n%s\n\n%s", qUtf8Printable(tr(
"Error bad id. Program will be terminated.")),
373 qCCritical(vApp,
"%s\n\n%s\n\n%s", qUtf8Printable(tr(
"Error can't convert value. Program will be terminated.")),
379 qCCritical(vApp,
"%s\n\n%s\n\n%s", qUtf8Printable(tr(
"Error empty parameter. Program will be terminated.")),
385 qCCritical(vApp,
"%s\n\n%s\n\n%s", qUtf8Printable(tr(
"Error wrong id. Program will be terminated.")),
391 qCCritical(vApp,
"%s\n\n%s\n\n%s",
392 qUtf8Printable(
"Unhadled deleting tool. Continue use object after deleting"),
398 qCCritical(vApp,
"%s\n\n%s\n\n%s", qUtf8Printable(tr(
"Something's wrong!!")),
407 qCCritical(vApp,
"%s", qUtf8Printable(tr(
"Parser error: %1. Program will be terminated.").arg(e.GetMsg())));
410 catch (std::exception& e)
412 qCCritical(vApp,
"%s", qUtf8Printable(tr(
"Exception thrown: %1. Program will be terminated.").arg(e.
what())));
421 const QString seamlyme = QStringLiteral(
"seamlyme");
423 QFileInfo seamlymeFile(QCoreApplication::applicationDirPath() +
"/" + seamlyme +
".exe");
424 if (seamlymeFile.exists())
426 return seamlymeFile.absoluteFilePath();
430 return QCoreApplication::applicationDirPath() +
"/../../seamlyme/bin/" + seamlyme +
".exe";
432 #elif defined(Q_OS_MAC)
433 QFileInfo seamlymeFile(QCoreApplication::applicationDirPath() +
"/" + seamlyme);
434 if (seamlymeFile.exists())
436 return seamlymeFile.absoluteFilePath();
440 QFileInfo file(QCoreApplication::applicationDirPath() +
"/../../seamlyme/bin/" + seamlyme);
443 return file.absoluteFilePath();
451 QFileInfo file(QCoreApplication::applicationDirPath() +
"/../../seamlyme/bin/" + seamlyme);
454 return file.absoluteFilePath();
458 QFileInfo seamlymeFile(QCoreApplication::applicationDirPath() +
"/" + seamlyme);
459 if (seamlymeFile.exists())
461 return seamlymeFile.absoluteFilePath();
474 #if defined(Q_OS_WIN) || defined(Q_OS_OSX)
475 const QString logDirPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString(),
476 QStandardPaths::LocateDirectory) +
"Seamly2D";
478 const QString logDirPath = QStandardPaths::locate(QStandardPaths::ConfigLocation, QString(),
479 QStandardPaths::LocateDirectory)
480 + QCoreApplication::organizationName();
488 return QString(
"%1/seamly2d-pid%2.log").arg(
LogDirPath()).arg(applicationPid());
495 if (logDir.exists() ==
false)
497 return logDir.mkpath(
".");
509 if (
lockLog->GetProtected()->open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
511 out.reset(
new QTextStream(
lockLog->GetProtected().get()));
513 qCDebug(vApp,
"Log file %s was locked.", qUtf8Printable(
LogPath()));
517 qCDebug(vApp,
"Error opening log file \'%s\'. All debug output redirected to console.",
523 qCDebug(vApp,
"Failed to lock %s", qUtf8Printable(
LogPath()));
531 logsDir.setNameFilters(QStringList(
"*.log"));
534 const QStringList allFiles = logsDir.entryList(QDir::NoDotAndDotDot | QDir::Files);
535 if (allFiles.isEmpty() ==
false)
537 qCDebug(vApp,
"Clearing old logs");
538 for (
int i = 0, sz = allFiles.size(); i < sz; ++i)
540 auto fn = allFiles.at(i);
549 qCDebug(vApp,
"Deleted %s", qUtf8Printable(info.absoluteFilePath()));
553 qCDebug(vApp,
"Could not delete %s", qUtf8Printable(info.absoluteFilePath()));
558 qCDebug(vApp,
"Failed to lock %s", qUtf8Printable(info.absoluteFilePath()));
565 qCDebug(vApp,
"There are no old logs.");
572 #if defined(Q_OS_WIN) && defined(Q_CC_GNU)
574 VApplication::DrMingw();
575 this->CollectReports();
582 qDebug()<<
"Build revision:"<<BUILD_REVISION;
584 qDebug()<<
"Built on"<<__DATE__<<
"at"<<__TIME__;
585 qDebug()<<
"Command-line arguments:"<<arguments();
586 qDebug()<<
"Process ID:"<<applicationPid();
593 static const char * GENERIC_ICON_TO_CHECK =
"document-open";
594 if (QIcon::hasThemeIcon(GENERIC_ICON_TO_CHECK) ==
false)
600 QIcon::setThemeName(
"win.icon.theme");
605 QDir().mkpath(
settings->GetDefPathLayout());
606 QDir().mkpath(
settings->GetDefPathPattern());
616 QStringList list = QStringList() <<
"de"
634 #if defined(Q_OS_WIN) && defined(Q_CC_GNU)
668 case QEvent::FileOpen:
670 QFileOpenEvent *fileOpenEvent =
static_cast<QFileOpenEvent *
>(e);
671 const QString macFileOpen = fileOpenEvent->file();
672 if(not macFileOpen.isEmpty())
683 #if defined(Q_OS_MAC)
684 case QEvent::ApplicationActivate:
694 return VAbstractApplication::event(e);
696 return VAbstractApplication::event(e);
707 settings =
new VSettings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(),
708 QCoreApplication::applicationName(),
this);
715 return qobject_cast<VSettings *>(
settings);
740 #if defined(Q_OS_WIN) && defined(Q_CC_GNU)
742 void VApplication::ClearOldReports()
const
744 const QString reportsDir = QString(
"%1/reports").arg(
qApp->applicationDirPath());
745 QDir reports(reportsDir);
746 if (reports.exists())
748 QStringList filters{
"*.log",
"*.RPT"};
749 QDir logsDir(reportsDir);
750 logsDir.setNameFilters(filters);
751 logsDir.setCurrent(reportsDir);
753 const QStringList allFiles = logsDir.entryList(QDir::NoDotAndDotDot | QDir::Files);
754 if (allFiles.isEmpty() ==
false)
756 const QDateTime now = QDateTime::currentDateTime();
757 for (
int i = 0; i < allFiles.size(); ++i)
759 QFileInfo info(allFiles.at(i));
760 if (info.birthTime().daysTo(now) > 30)
762 QFile(allFiles.at(i)).remove();
770 void VApplication::GatherLogs()
const
772 QTextStream *
out =
nullptr;
773 QFile *log =
new QFile(QString(
"%1/seamly2d.log").arg(
LogDirPath()));
774 if (log->open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
776 out =
new QTextStream(log);
778 QStringList filters{
"*.log"};
780 logsDir.setNameFilters(filters);
783 const QStringList allFiles = logsDir.entryList(QDir::NoDotAndDotDot | QDir::Files);
784 if (allFiles.isEmpty() ==
false)
786 for (
int i = 0, sz = allFiles.size(); i < sz; ++i)
788 auto fn = allFiles.at(i);
790 if (info.fileName() ==
"seamly2d.log")
799 *
out <<
"--------------------------" << Qt::endl;
800 if (tmp.GetProtected()->open(QIODevice::ReadOnly | QIODevice::Text))
802 QTextStream in(tmp.GetProtected().get());
805 *
out << in.readLine() << Qt::endl;
807 tmp.GetProtected()->close();
811 *
out <<
"Log file error:" + tmp.GetProtected()->errorString() << Qt::endl;
816 qCDebug(vApp,
"Failed to lock %s", qUtf8Printable(info.absoluteFilePath()));
822 *
out <<
"Could not find logs.";
833 void VApplication::DrMingw()
835 QFile drmingw(
"exchndl.dll");
836 if (drmingw.exists())
838 LoadLibrary(L
"exchndl.dll");
843 void VApplication::CollectReports()
const
846 const QString reportName = QString(
"%1/%2.RPT").arg(applicationDirPath())
847 .arg(QFileInfo(arguments().at(0)).baseName());
848 QFile reportFile(reportName);
849 if (reportFile.exists())
859 connect(
this, &VApplication::aboutToQuit,
this, &VApplication::CleanGist, Qt::UniqueConnection);
860 SendReport(reportName);
864 CollectReport(reportName);
870 void VApplication::CollectReport(
const QString &reportName)
const
872 const QString reportsDir = QString(
"%1/reports").arg(
qApp->applicationDirPath());
873 QDir reports(reportsDir);
874 if (reports.exists() ==
false)
879 const QDateTime now = QDateTime::currentDateTime();
880 const QString timestamp = now.toString(QLatin1String(
"yyyy.MM.dd-hh_mm_ss"));
881 QString filename = QString(
"%1/reports/crash-%2.RPT").arg(
qApp->applicationDirPath()).arg(timestamp);
883 QFile reportFile(reportName);
884 reportFile.copy(filename);
887 filename = QString(
"%1/reports/log-%2.log").arg(
qApp->applicationDirPath()).arg(timestamp);
889 QFile logFile(QString(
"%1/seamly2d.log").arg(
LogDirPath()));
890 logFile.copy(filename);
894 void VApplication::CleanGist()
const
896 QFile gistFile(GistFileName);
897 if (gistFile.exists())
904 void VApplication::SendReport(
const QString &reportName)
const
907 QFile reportFile(reportName);
908 if (reportFile.open(QIODevice::ReadOnly | QIODevice::Text))
910 content = ReadFileForSending(reportFile);
915 content =
"RPT file error:" + reportFile.errorString() +
"\r\n";
919 content.append(QString(
"-------------------------------")+
"\r\n");
920 content.append(QString(
"Version:%1").arg(
APP_VERSION)+
"\r\n");
921 content.append(QString(
"Build revision:%1").arg(BUILD_REVISION)+
"\r\n");
922 content.append(QString(
"Based on Qt %1 (32 bit)").arg(QT_VERSION_STR)+
"\r\n");
923 content.append(QString(
"Built on %1 at %2").arg(__DATE__).arg(__TIME__)+
"\r\n");
924 content.append(QString(
"Web site:http://seamly.net/ ")+
"\r\n");
925 content.append(
"\r\n");
940 const QDateTime now = QDateTime::currentDateTime();
941 const QString timestamp = now.toString(QLatin1String(
"yyyy/MM/dd hh:mm:ss:zzz"));
942 const QString report = QString(
"Crash report was created %2").arg(timestamp);
944 QJsonObject reportObject;
945 reportObject.insert(QStringLiteral(
"description"), QJsonValue(report));
946 reportObject.insert(QStringLiteral(
"public"), QJsonValue(QString(
"true")));
948 content.append(QString(
"\r\n-------------------------------\r\n"));
949 content.append(QString(
"Log:")+
"\r\n");
952 QFile logFile(QString(
"%1/seamly2d.log").arg(
LogDirPath()));
953 if (logFile.open(QIODevice::ReadOnly | QIODevice::Text))
955 const QString logContent = ReadFileForSending(logFile);
956 if (logContent.isEmpty())
958 content.append(
"Log file is empty.");
962 content.append(logContent);
968 content.append(
"\r\n Log file error:" + logFile.errorString() +
"\r\n");
971 const QString contentSection = QStringLiteral(
"content");
972 QJsonObject contentObject;
973 contentObject.insert(contentSection, QJsonValue(content));
975 const QString filesSection = QStringLiteral(
"files");
976 QJsonObject fileObject;
977 fileObject.insert(QFileInfo(reportName).fileName(), QJsonValue(contentObject));
978 reportObject.insert(filesSection, QJsonValue(fileObject));
980 QFile gistFile(GistFileName);
981 if (!gistFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
983 qDebug(
"Couldn't open gist file.");
988 QJsonDocument saveRep(reportObject);
989 gistFile.write(saveRep.toJson());
992 const QString curl = QString(
"%1/curl.exe").arg(
qApp->applicationDirPath());
993 QFile curlFile(curl);
994 if (curlFile.exists())
997 const QStringList token = QStringList()<<
"eb"<<
"78"<<
"63"<<
"4e"<<
"de"<<
"77"<<
"f7"<<
"e6"<<
"01"<<
"4a"<<
"c3"<<
"60"
998 <<
"96"<<
"b0"<<
"2d"<<
"54"<<
"fb"<<
"8e"<<
"af"<<
"ec";
1000 const QString arg = QString(
"curl.exe -k -H \"Authorization: bearer ")+token.join(
"")+
1001 QString(
"\" -H \"Accept: application/json\" -H \"Content-type: application/json\" -X POST "
1002 "--data @gist.json https://api.github.com/gists");
1005 proc.start(arg, args);
1006 proc.waitForFinished(10000);
1007 reportFile.remove();
1011 CollectReport(reportName);
1017 QString VApplication::ReadFileForSending(QFile &file)
const
1020 QTextStream in(&file);
1023 content.append(in.readLine()+
"\r\n");
The MainWindow class main windows.
bool LoadPattern(const QString &fileName, const QString &customMeasureFile=QString())
LoadPattern open pattern file.
QWidget * mainWindow
mainWindow pointer to main window. Usefull if need create modal dialog. Without pointer to main windo...
VCommonSettings * settings
settings pointer to settings. Help hide constructor creation settings. Make make code more readable.
void loadTranslations(const QString &locale)
QString SeamlyMeFilePath() const
virtual void OpenSettings() Q_DECL_OVERRIDE
OpenSettings get acsses to application settings.
std::shared_ptr< QTextStream > out
QString LogDirPath() const
VSettings * Seamly2DSettings()
static void NewSeamly2D(const QString &fileName=QString())
NewSeamly2D start Seamly2D in new process, send path to pattern file in argument.
virtual bool notify(QObject *receiver, QEvent *event) Q_DECL_OVERRIDE
notify Reimplemented from QApplication::notify().
virtual ~VApplication() Q_DECL_OVERRIDE
virtual bool IsAppInGUIMode() const Q_DECL_OVERRIDE
IsAppInGUIMode little hack that allow to have access to application state from VAbstractApplication c...
std::shared_ptr< VLockGuard< QFile > > lockLog
virtual bool event(QEvent *e) Q_DECL_OVERRIDE
void ClearOldLogs() const
VApplication(int &argc, char **argv)
VApplication constructor.
bool CreateLogDir() const
virtual void InitTrVars() Q_DECL_OVERRIDE
const VCommandLinePtr CommandLine() const
static QStringList LabelLanguages()
virtual const VTranslateVars * TrVars() Q_DECL_OVERRIDE
static VCommandLinePtr instance
static VCommandLinePtr Get(const QCoreApplication &app)
bool GetSendReportState() const
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.
virtual const char * what() const Q_DECL_OVERRIDE
const std::shared_ptr< Guarded > & GetProtected() const
Error class of the parser.
QString buildCompatibilityString()
#define VER_COMPANYDOMAIN_STR
#define VER_COMPANYNAME_STR
const QString APP_VERSION_STR
#define VER_INTERNALNAME_STR
QT_WARNING_PUSH QT_WARNING_POP Q_DECL_CONSTEXPR auto DAYS_TO_KEEP_LOGS
void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
#define translate(context, source)
std::shared_ptr< VCommandLine > VCommandLinePtr
QT_WARNING_PUSH void VlpCreateLock(std::shared_ptr< VLockGuard< Guarded >> &r, const QString &lockName, int stale=0, int timeout=0)
static const auto V_EX_DATAERR
static const auto V_EX_SOFTWARE