From 5a10bd7f63a79ba8194ce1b046434657799a2090 Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Sun, 16 Feb 2020 19:18:39 +0200 Subject: [PATCH] Puzzle application class. --- src/app/puzzle/main.cpp | 65 ++- src/app/puzzle/puzzle.pri | 6 +- src/app/puzzle/puzzle.pro | 38 +- src/app/puzzle/puzzleapplication.cpp | 635 +++++++++++++++++++++++++++ src/app/puzzle/puzzleapplication.h | 94 ++++ src/app/puzzle/puzzlemainwindow.cpp | 8 + src/app/puzzle/puzzlemainwindow.h | 2 + src/app/puzzle/stable.cpp | 30 ++ src/app/puzzle/stable.h | 72 +++ src/app/puzzle/version.h | 39 ++ src/app/tape/mapplication.cpp | 3 +- src/app/tape/mapplication.h | 2 +- src/libs/vmisc/vmisc.pri | 6 +- src/libs/vmisc/vpuzzlesettings.cpp | 37 ++ src/libs/vmisc/vpuzzlesettings.h | 46 ++ 15 files changed, 1052 insertions(+), 31 deletions(-) create mode 100644 src/app/puzzle/puzzleapplication.cpp create mode 100644 src/app/puzzle/puzzleapplication.h create mode 100644 src/app/puzzle/stable.cpp create mode 100644 src/app/puzzle/stable.h create mode 100644 src/app/puzzle/version.h create mode 100644 src/libs/vmisc/vpuzzlesettings.cpp create mode 100644 src/libs/vmisc/vpuzzlesettings.h diff --git a/src/app/puzzle/main.cpp b/src/app/puzzle/main.cpp index f214dd3a0..86d126b18 100644 --- a/src/app/puzzle/main.cpp +++ b/src/app/puzzle/main.cpp @@ -25,14 +25,67 @@ ** along with Valentina. If not, see . ** *************************************************************************/ -#include "puzzlemainwindow.h" -#include + +#include // For QT_REQUIRE_VERSION +#include + +#include "puzzleapplication.h" +#include "../fervor/fvupdater.h" +#include "../vmisc/vsysexits.h" +#include "../vmisc/def.h" + + +#if defined(APPIMAGE) && defined(Q_OS_LINUX) +#if QT_VERSION < QT_VERSION_CHECK(5, 12, 0) +# include "../vmisc/backport/qscopeguard.h" +#else +# include +#endif +# include "../vmisc/appimage.h" +#endif // defined(APPIMAGE) && defined(Q_OS_LINUX) int main(int argc, char *argv[]) { - QApplication a(argc, argv); - PuzzleMainWindow w; - w.show(); +#if defined(APPIMAGE) && defined(Q_OS_LINUX) + /* Fix path to ICU_DATA when run AppImage.*/ + char *exe_dir = IcuDataPath("/../share/icu"); + auto FreeMemory = qScopeGuard([exe_dir] {free(exe_dir);}); +#endif // defined(APPIMAGE) && defined(Q_OS_LINUX) - return a.exec(); + Q_INIT_RESOURCE(puzzleicon); + + QT_REQUIRE_VERSION(argc, argv, "5.4.0")// clazy:exclude=qstring-arg,qstring-allocations + +#if defined(Q_OS_WIN) + VAbstractApplication::WinAttachConsole(); +#endif + +#ifndef Q_OS_MAC // supports natively + InitHighDpiScaling(argc, argv); +#endif //Q_OS_MAC + + PuzzleApplication app(argc, argv); + app.InitOptions(); + + if (FvUpdater::IsStaledTestBuild()) + { + qWarning() << QApplication::translate("Puzzle", + "This test build is older than %1 days. To provide you with better " + "quality service we restrict the lifetime you can use a test build. " + "To continue using Puzzle please update to newer test build. The " + "application will be shut down.") + .arg(FvUpdater::testBuildLifetime); + return V_EX_UNAVAILABLE; + } + + QTimer::singleShot(0, &app, &PuzzleApplication::ProcessCMD); + +#if defined(APPIMAGE) && defined(Q_OS_LINUX) + if (exe_dir) + { + qDebug() << "Path to ICU folder:" << exe_dir; + } +#endif // defined(APPIMAGE) && defined(Q_OS_LINUX) + + return app.exec(); } diff --git a/src/app/puzzle/puzzle.pri b/src/app/puzzle/puzzle.pri index 863a4d1a2..101fff4e3 100644 --- a/src/app/puzzle/puzzle.pri +++ b/src/app/puzzle/puzzle.pri @@ -3,13 +3,15 @@ SOURCES += \ $$PWD/main.cpp \ - $$PWD/puzzlemainwindow.cpp + $$PWD/puzzlemainwindow.cpp \ + $$PWD/puzzleapplication.cpp *msvc*:SOURCES += $$PWD/stable.cpp HEADERS += \ $$PWD/puzzlemainwindow.h \ - $$PWD/stable.h + $$PWD/stable.h \ + $$PWD/puzzleapplication.h FORMS += \ $$PWD/puzzlemainwindow.ui diff --git a/src/app/puzzle/puzzle.pro b/src/app/puzzle/puzzle.pro index b0255b2c2..7463ba832 100644 --- a/src/app/puzzle/puzzle.pro +++ b/src/app/puzzle/puzzle.pro @@ -7,7 +7,7 @@ # File with common stuff for whole project include(../../../common.pri) -QT += core gui widgets +QT += core gui widgets network xml xmlpatterns printsupport # Name of binary file TARGET = puzzle @@ -224,32 +224,32 @@ noRunPath{ # For enable run qmake with CONFIG+=noRunPath #win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vpatterndb/$${DESTDIR}/vpatterndb.lib #else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vpatterndb/$${DESTDIR}/libvpatterndb.a -## Fervor static library (depend on VMisc, IFC) -#unix|win32: LIBS += -L$$OUT_PWD/../../libs/fervor/$${DESTDIR}/ -lfervor +# Fervor static library (depend on VMisc, IFC) +unix|win32: LIBS += -L$$OUT_PWD/../../libs/fervor/$${DESTDIR}/ -lfervor -#INCLUDEPATH += $$PWD/../../libs/fervor -#DEPENDPATH += $$PWD/../../libs/fervor +INCLUDEPATH += $$PWD/../../libs/fervor +DEPENDPATH += $$PWD/../../libs/fervor -#win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/fervor/$${DESTDIR}/fervor.lib -#else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/fervor/$${DESTDIR}/libfervor.a +win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/fervor/$${DESTDIR}/fervor.lib +else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/fervor/$${DESTDIR}/libfervor.a -## IFC static library (depend on QMuParser, VMisc) -#unix|win32: LIBS += -L$$OUT_PWD/../../libs/ifc/$${DESTDIR}/ -lifc +# IFC static library (depend on QMuParser, VMisc) +unix|win32: LIBS += -L$$OUT_PWD/../../libs/ifc/$${DESTDIR}/ -lifc -#INCLUDEPATH += $$PWD/../../libs/ifc -#DEPENDPATH += $$PWD/../../libs/ifc +INCLUDEPATH += $$PWD/../../libs/ifc +DEPENDPATH += $$PWD/../../libs/ifc -#win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/ifc/$${DESTDIR}/ifc.lib -#else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/ifc/$${DESTDIR}/libifc.a +win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/ifc/$${DESTDIR}/ifc.lib +else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/ifc/$${DESTDIR}/libifc.a -##VMisc static library -#unix|win32: LIBS += -L$$OUT_PWD/../../libs/vmisc/$${DESTDIR}/ -lvmisc +#VMisc static library +unix|win32: LIBS += -L$$OUT_PWD/../../libs/vmisc/$${DESTDIR}/ -lvmisc -#INCLUDEPATH += $$PWD/../../libs/vmisc -#DEPENDPATH += $$PWD/../../libs/vmisc +INCLUDEPATH += $$PWD/../../libs/vmisc +DEPENDPATH += $$PWD/../../libs/vmisc -#win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vmisc/$${DESTDIR}/vmisc.lib -#else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vmisc/$${DESTDIR}/libvmisc.a +win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vmisc/$${DESTDIR}/vmisc.lib +else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vmisc/$${DESTDIR}/libvmisc.a ## VLayout static library (depend on VGeometry) #unix|win32: LIBS += -L$$OUT_PWD/../../libs/vlayout/$${DESTDIR}/ -lvlayout diff --git a/src/app/puzzle/puzzleapplication.cpp b/src/app/puzzle/puzzleapplication.cpp new file mode 100644 index 000000000..a0a749d85 --- /dev/null +++ b/src/app/puzzle/puzzleapplication.cpp @@ -0,0 +1,635 @@ +/************************************************************************ + ** + ** @file puzzleapplication.cpp + ** @author Roman Telezhynskyi + ** @date 16 2, 2020 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentina project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2020 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ + +#include "puzzleapplication.h" +#include "version.h" +#include "puzzlemainwindow.h" +#include "../ifc/exception/vexceptionobjecterror.h" +#include "../ifc/exception/vexceptionbadid.h" +#include "../ifc/exception/vexceptionconversionerror.h" +#include "../ifc/exception/vexceptionemptyparameter.h" +#include "../ifc/exception/vexceptionwrongid.h" +#include "../vmisc/vsysexits.h" +#include "../vmisc/diagnostic.h" +#include "../vmisc/qt_dispatch/qt_dispatch.h" + +#include + +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wmissing-prototypes") +QT_WARNING_DISABLE_INTEL(1418) + +Q_LOGGING_CATEGORY(mApp, "m.application") + +QT_WARNING_POP + +#include +#include +#include + +//--------------------------------------------------------------------------------------------------------------------- +inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + // only the GUI thread should display message boxes. If you are + // writing a multithreaded application and the error happens on + // a non-GUI thread, you'll have to queue the message to the GUI + QCoreApplication *instance = QCoreApplication::instance(); + const bool isGuiThread = instance && (QThread::currentThread() == instance->thread()); + + if (not isGuiThread) + { + auto Handler = [](QtMsgType type, const QMessageLogContext &context, const QString &msg) + { + noisyFailureMsgHandler(type, context, msg); + }; + + q_dispatch_async_main(Handler, type, context, msg); + return; + } + + // Why on earth didn't Qt want to make failed signal/slot connections qWarning? + if ((type == QtDebugMsg) && msg.contains(QStringLiteral("::connect"))) + { + type = QtWarningMsg; + } + +#if defined(V_NO_ASSERT) + // I have decided to hide this annoing message for release builds. + if ((type == QtWarningMsg) && msg.contains(QStringLiteral("QSslSocket: cannot resolve"))) + { + type = QtDebugMsg; + } + + if ((type == QtWarningMsg) && msg.contains(QStringLiteral("setGeometry: Unable to set geometry"))) + { + type = QtDebugMsg; + } +#endif //defined(V_NO_ASSERT) + +#if defined(Q_OS_MAC) +# if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) && QT_VERSION < QT_VERSION_CHECK(5, 7, 0) + // Try hide very annoying, Qt related, warnings in Mac OS X + // QNSView mouseDragged: Internal mouse button tracking invalid (missing Qt::LeftButton) + // https://bugreports.qt.io/browse/QTBUG-42846 + if ((type == QtWarningMsg) && msg.contains(QStringLiteral("QNSView"))) + { + type = QtDebugMsg; + } +# endif + + // Hide Qt bug 'Assertion when reading an icns file' + // https://bugreports.qt.io/browse/QTBUG-45537 + // Remove after Qt fix will be released + if ((type == QtWarningMsg) && msg.contains(QStringLiteral("QICNSHandler::read()"))) + { + type = QtDebugMsg; + } + + // See issue #568 + if (msg.contains(QStringLiteral("Error receiving trust for a CA certificate"))) + { + type = QtDebugMsg; + } +#endif + + // this is another one that doesn't make sense as just a debug message. pretty serious + // sign of a problem + // http://www.developer.nokia.com/Community/Wiki/QPainter::begin:Paint_device_returned_engine_%3D%3D_0_(Known_Issue) + if ((type == QtDebugMsg) && msg.contains(QStringLiteral("QPainter::begin")) + && msg.contains(QStringLiteral("Paint device returned engine"))) + { + type = QtWarningMsg; + } + + // This qWarning about "Cowardly refusing to send clipboard message to hung application..." + // is something that can easily happen if you are debugging and the application is paused. + // As it is so common, not worth popping up a dialog. + if ((type == QtWarningMsg) && msg.contains(QStringLiteral("QClipboard::event")) + && msg.contains(QStringLiteral("Cowardly refusing"))) + { + type = QtDebugMsg; + } + + switch (type) + { + case QtDebugMsg: + vStdOut() << QApplication::translate("mNoisyHandler", "DEBUG:") << msg << "\n"; + return; + case QtWarningMsg: + vStdErr() << QApplication::translate("mNoisyHandler", "WARNING:") << msg << "\n"; + break; + case QtCriticalMsg: + vStdErr() << QApplication::translate("mNoisyHandler", "CRITICAL:") << msg << "\n"; + break; + case QtFatalMsg: + vStdErr() << QApplication::translate("mNoisyHandler", "FATAL:") << msg << "\n"; + break; + #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) + case QtInfoMsg: + vStdOut() << QApplication::translate("mNoisyHandler", "INFO:") << msg << "\n"; + break; + #endif + default: + break; + } + + vStdOut().flush(); + vStdErr().flush(); + + if (isGuiThread) + { + //fixme: trying to make sure there are no save/load dialogs are opened, because error message during them will + //lead to crash + const bool topWinAllowsPop = (QApplication::activeModalWidget() == nullptr) || + !QApplication::activeModalWidget()->inherits("QFileDialog"); + QMessageBox messageBox; + switch (type) + { + case QtWarningMsg: + messageBox.setWindowTitle(QApplication::translate("mNoisyHandler", "Warning")); + messageBox.setIcon(QMessageBox::Warning); + break; + case QtCriticalMsg: + messageBox.setWindowTitle(QApplication::translate("mNoisyHandler", "Critical error")); + messageBox.setIcon(QMessageBox::Critical); + break; + case QtFatalMsg: + messageBox.setWindowTitle(QApplication::translate("mNoisyHandler", "Fatal error")); + messageBox.setIcon(QMessageBox::Critical); + break; + #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) + case QtInfoMsg: + messageBox.setWindowTitle(QApplication::translate("mNoisyHandler", "Information")); + messageBox.setIcon(QMessageBox::Information); + break; + #endif + case QtDebugMsg: + Q_UNREACHABLE(); //-V501 + break; + default: + break; + } + + if (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg) + { + if (not qApp->IsTestMode()) + { + if (topWinAllowsPop) + { + messageBox.setText(VAbstractApplication::ClearMessage(msg)); + messageBox.setStandardButtons(QMessageBox::Ok); + messageBox.setWindowModality(Qt::ApplicationModal); + messageBox.setModal(true); + #ifndef QT_NO_CURSOR + QGuiApplication::setOverrideCursor(Qt::ArrowCursor); + #endif + messageBox.exec(); + #ifndef QT_NO_CURSOR + QGuiApplication::restoreOverrideCursor(); + #endif + } + } + } + + if (QtFatalMsg == type) + { + abort(); + } + } + else + { + if (type != QtDebugMsg) + { + abort(); // be NOISY unless overridden! + } + } +} + +//--------------------------------------------------------------------------------------------------------------------- +PuzzleApplication::PuzzleApplication(int &argc, char **argv) + :VAbstractApplication(argc, argv), + mainWindows(), + localServer(nullptr), + testMode(false) +{ + setApplicationDisplayName(VER_PRODUCTNAME_STR); + setApplicationName(VER_INTERNALNAME_STR); + setOrganizationName(VER_COMPANYNAME_STR); + setOrganizationDomain(VER_COMPANYDOMAIN_STR); + // Setting the Application version + setApplicationVersion(APP_VERSION_STR); + // We have been running Puzzle in two different cases. + // The first inside own bundle where info.plist is works fine, but the second, + // when we run inside Valentina's bundle, require direct setting the icon. + setWindowIcon(QIcon(":/puzzleicon/64x64/logo.png")); +} + +//--------------------------------------------------------------------------------------------------------------------- +PuzzleApplication::~PuzzleApplication() +{ + qDeleteAll(mainWindows); +} + +//--------------------------------------------------------------------------------------------------------------------- +/** + * @brief notify Reimplemented from QApplication::notify(). + * @param receiver receiver. + * @param event event. + * @return value that is returned from the receiver's event handler. + */ +// reimplemented from QApplication so we can throw exceptions in slots +bool PuzzleApplication::notify(QObject *receiver, QEvent *event) +{ + try + { + return QApplication::notify(receiver, event); + } + catch (const VExceptionObjectError &e) + { + qCCritical(mApp, "%s\n\n%s\n\n%s", qUtf8Printable(tr("Error parsing file. Program will be terminated.")), //-V807 + qUtf8Printable(e.ErrorMessage()), qUtf8Printable(e.DetailedInformation())); + exit(V_EX_DATAERR); + } + catch (const VExceptionBadId &e) + { + qCCritical(mApp, "%s\n\n%s\n\n%s", qUtf8Printable(tr("Error bad id. Program will be terminated.")), + qUtf8Printable(e.ErrorMessage()), qUtf8Printable(e.DetailedInformation())); + exit(V_EX_DATAERR); + } + catch (const VExceptionConversionError &e) + { + qCCritical(mApp, "%s\n\n%s\n\n%s", qUtf8Printable(tr("Error can't convert value. Program will be terminated.")), + qUtf8Printable(e.ErrorMessage()), qUtf8Printable(e.DetailedInformation())); + exit(V_EX_DATAERR); + } + catch (const VExceptionEmptyParameter &e) + { + qCCritical(mApp, "%s\n\n%s\n\n%s", qUtf8Printable(tr("Error empty parameter. Program will be terminated.")), + qUtf8Printable(e.ErrorMessage()), qUtf8Printable(e.DetailedInformation())); + exit(V_EX_DATAERR); + } + catch (const VExceptionWrongId &e) + { + qCCritical(mApp, "%s\n\n%s\n\n%s", qUtf8Printable(tr("Error wrong id. Program will be terminated.")), + qUtf8Printable(e.ErrorMessage()), qUtf8Printable(e.DetailedInformation())); + exit(V_EX_DATAERR); + } + catch (const VExceptionToolWasDeleted &e) + { + qCCritical(mApp, "%s\n\n%s\n\n%s", + qUtf8Printable("Unhadled deleting tool. Continue use object after deleting!"), + qUtf8Printable(e.ErrorMessage()), qUtf8Printable(e.DetailedInformation())); + exit(V_EX_DATAERR); + } + catch (const VException &e) + { + qCCritical(mApp, "%s\n\n%s\n\n%s", qUtf8Printable(tr("Something's wrong!!")), + qUtf8Printable(e.ErrorMessage()), qUtf8Printable(e.DetailedInformation())); + return true; + } + catch (std::exception &e) + { + qCCritical(mApp, "%s", qUtf8Printable(tr("Exception thrown: %1. Program will be terminated.").arg(e.what()))); + exit(V_EX_SOFTWARE); + } + return false; +} + +//--------------------------------------------------------------------------------------------------------------------- +bool PuzzleApplication::IsTestMode() const +{ + return testMode; +} + +//--------------------------------------------------------------------------------------------------------------------- +/** + * @brief IsAppInGUIMode little hack that allow to have access to application state from VAbstractApplication class. + */ +bool PuzzleApplication::IsAppInGUIMode() const +{ + return IsTestMode(); +} + +//--------------------------------------------------------------------------------------------------------------------- +PuzzleMainWindow *PuzzleApplication::MainWindow() +{ + Clean(); + if (mainWindows.isEmpty()) + { + NewMainWindow(); + } + return mainWindows[0]; +} + +//--------------------------------------------------------------------------------------------------------------------- +QList PuzzleApplication::MainWindows() +{ + Clean(); + QList list; + for (auto &w : mainWindows) + { + list.append(w); + } + return list; +} + +//--------------------------------------------------------------------------------------------------------------------- +PuzzleMainWindow *PuzzleApplication::NewMainWindow() +{ + PuzzleMainWindow *puzzle = new PuzzleMainWindow(); + mainWindows.prepend(puzzle); + if (not qApp->IsTestMode()) + { + puzzle->show(); + } + return puzzle; +} + +//--------------------------------------------------------------------------------------------------------------------- +void PuzzleApplication::InitOptions() +{ + qInstallMessageHandler(noisyFailureMsgHandler); + + OpenSettings(); + + qCDebug(mApp, "Version: %s", qUtf8Printable(APP_VERSION_STR)); + qCDebug(mApp, "Build revision: %s", BUILD_REVISION); + qCDebug(mApp, "%s", qUtf8Printable(buildCompatibilityString())); + qCDebug(mApp, "Built on %s at %s", __DATE__, __TIME__); + qCDebug(mApp, "Command-line arguments: %s", qUtf8Printable(arguments().join(", "))); + qCDebug(mApp, "Process ID: %s", qUtf8Printable(QString().setNum(applicationPid()))); + + LoadTranslation(QLocale().name());// By default the console version uses system locale + + static const char * GENERIC_ICON_TO_CHECK = "document-open"; + if (QIcon::hasThemeIcon(GENERIC_ICON_TO_CHECK) == false) + { + //If there is no default working icon theme then we should + //use an icon theme that we provide via a .qrc file + //This case happens under Windows and Mac OS X + //This does not happen under GNOME or KDE + QIcon::setThemeName("win.icon.theme"); + } + ActivateDarkMode(); +} + +//--------------------------------------------------------------------------------------------------------------------- +const VTranslateVars *PuzzleApplication::TrVars() +{ + return nullptr; +} + +//--------------------------------------------------------------------------------------------------------------------- +void PuzzleApplication::OpenSettings() +{ + settings = new VPuzzleSettings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), + QCoreApplication::applicationName(), this); +} + +//--------------------------------------------------------------------------------------------------------------------- +VPuzzleSettings *PuzzleApplication::PuzzleSettings() +{ + SCASSERT(settings != nullptr) + return qobject_cast(settings); +} + +//--------------------------------------------------------------------------------------------------------------------- +void PuzzleApplication::ActivateDarkMode() +{ + VPuzzleSettings *settings = qApp->PuzzleSettings(); + if (settings->GetDarkMode()) + { + QFile f(":qdarkstyle/style.qss"); + if (!f.exists()) + { + qDebug()<<"Unable to set stylesheet, file not found\n"; + } + else + { + f.open(QFile::ReadOnly | QFile::Text); + QTextStream ts(&f); + qApp->setStyleSheet(ts.readAll()); + } + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void PuzzleApplication::ParseCommandLine(const SocketConnection &connection, const QStringList &arguments) +{ + QCommandLineParser parser; + parser.setApplicationDescription(tr("Valentina's manual layout editor.")); + parser.addHelpOption(); + parser.addVersionOption(); + parser.addPositionalArgument("filename", tr("The raw layout file.")); + //----- + QCommandLineOption testOption(QStringList() << "test", + tr("Use for unit testing. Run the program and open a file without showing the main window.")); + parser.addOption(testOption); + //----- + QCommandLineOption scalingOption(QStringList() << LONG_OPTION_NO_HDPI_SCALING, + tr("Disable high dpi scaling. Call this option if has problem with scaling (by default scaling enabled). " + "Alternatively you can use the %1 environment variable.").arg("QT_AUTO_SCREEN_SCALE_FACTOR=0")); + parser.addOption(scalingOption); + //----- + parser.process(arguments); + + testMode = parser.isSet(testOption); + + if (not testMode && connection == SocketConnection::Client) + { + const QString serverName = QCoreApplication::applicationName(); + QLocalSocket socket; + socket.connectToServer(serverName); + if (socket.waitForConnected(1000)) + { + qCDebug(mApp, "Connected to the server '%s'", qUtf8Printable(serverName)); + QTextStream stream(&socket); + stream << QCoreApplication::arguments().join(";;"); + stream.flush(); + socket.waitForBytesWritten(); + qApp->exit(V_EX_OK); + return; + } + + qCDebug(mApp, "Can't establish connection to the server '%s'", qUtf8Printable(serverName)); + + localServer = new QLocalServer(this); + connect(localServer, &QLocalServer::newConnection, this, &PuzzleApplication::NewLocalSocketConnection); + if (not localServer->listen(serverName)) + { + qCDebug(mApp, "Can't begin to listen for incoming connections on name '%s'", + qUtf8Printable(serverName)); + if (localServer->serverError() == QAbstractSocket::AddressInUseError) + { + QLocalServer::removeServer(serverName); + if (not localServer->listen(serverName)) + { + qCWarning(mApp, "%s", + qUtf8Printable(tr("Can't begin to listen for incoming connections on name '%1'").arg(serverName))); + } + } + } + + LoadTranslation(PuzzleSettings()->GetLocale()); + } + + const QStringList args = parser.positionalArguments(); + if (args.count() > 0) + { + if (testMode && args.count() > 1) + { + qCCritical(mApp, "%s\n", qPrintable(tr("Test mode doesn't support openning several files."))); + parser.showHelp(V_EX_USAGE); + } + + for (auto &arg : args) + { + NewMainWindow(); + if (not MainWindow()->LoadFile(arg)) + { + if (testMode) + { + return; // process only one input file + } + delete MainWindow(); + continue; + } + } + } + else + { + if (not testMode) + { + NewMainWindow(); + } + else + { + qCCritical(mApp, "%s\n", qPrintable(tr("Please, provide one input file."))); + parser.showHelp(V_EX_USAGE); + } + } + + if (testMode) + { + qApp->exit(V_EX_OK); // close program after processing in console mode + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void PuzzleApplication::ProcessCMD() +{ + ParseCommandLine(SocketConnection::Client, arguments()); +} + +//--------------------------------------------------------------------------------------------------------------------- +bool PuzzleApplication::event(QEvent *e) +{ + switch(e->type()) + { + // In Mac OS X the QFileOpenEvent event is generated when user perform "Open With" from Finder (this event is + // Mac specific). + case QEvent::FileOpen: + { + QFileOpenEvent *fileOpenEvent = static_cast(e); + const QString macFileOpen = fileOpenEvent->file(); + if(not macFileOpen.isEmpty()) + { + PuzzleMainWindow *mw = MainWindow(); + if (mw) + { + mw->LoadFile(macFileOpen); // open file in existing window + } + return true; + } + break; + } +#if defined(Q_OS_MAC) + case QEvent::ApplicationActivate: + { + Clean(); + PuzzleMainWindow *mw = PuzzleMainWindow(); + if (mw && not mw->isMinimized()) + { + mw->show(); + } + return true; + } +#endif //defined(Q_OS_MAC) + default: + return VAbstractApplication::event(e); + } + return VAbstractApplication::event(e); +} + +//--------------------------------------------------------------------------------------------------------------------- +void PuzzleApplication::InitTrVars() +{ + // do nothing +} + +//--------------------------------------------------------------------------------------------------------------------- +void PuzzleApplication::AboutToQuit() +{ + // If try to use the method QApplication::exit program can't sync settings and show warning about QApplication + // instance. Solution is to call sync() before quit. + // Connect this slot with VApplication::aboutToQuit. + Settings()->sync(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void PuzzleApplication::NewLocalSocketConnection() +{ + QLocalSocket *socket = localServer->nextPendingConnection(); + if (not socket) + { + return; + } + socket->waitForReadyRead(1000); + QTextStream stream(socket); + const QString arg = stream.readAll(); + if (not arg.isEmpty()) + { + ParseCommandLine(SocketConnection::Server, arg.split(";;")); + } + delete socket; + MainWindow()->raise(); + MainWindow()->activateWindow(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void PuzzleApplication::Clean() +{ + // cleanup any deleted main windows first + for (int i = mainWindows.count() - 1; i >= 0; --i) + { + if (mainWindows.at(i).isNull()) + { + mainWindows.removeAt(i); + } + } +} diff --git a/src/app/puzzle/puzzleapplication.h b/src/app/puzzle/puzzleapplication.h new file mode 100644 index 000000000..9f2b15760 --- /dev/null +++ b/src/app/puzzle/puzzleapplication.h @@ -0,0 +1,94 @@ +/************************************************************************ + ** + ** @file puzzleapplication.h + ** @author Roman Telezhynskyi + ** @date 16 2, 2020 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentina project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2020 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ +#ifndef PUZZLEAPPLICATION_H +#define PUZZLEAPPLICATION_H + +#include "../vmisc/def.h" +#include "../vmisc/vpuzzlesettings.h" +#include "../vmisc/vabstractapplication.h" + +class PuzzleApplication;// use in define +class PuzzleMainWindow; +class QLocalServer; + +#if defined(qApp) +#undef qApp +#endif +#define qApp (static_cast(VAbstractApplication::instance())) + +enum class SocketConnection : bool {Client = false, Server = true}; + +class PuzzleApplication : public VAbstractApplication +{ + Q_OBJECT +public: + PuzzleApplication(int &argc, char **argv); + virtual ~PuzzleApplication() override; + + virtual bool notify(QObject * receiver, QEvent * event) override; + + bool IsTestMode() const; + virtual bool IsAppInGUIMode() const override; + PuzzleMainWindow *MainWindow(); + QList MainWindows(); + PuzzleMainWindow *NewMainWindow(); + + void InitOptions(); + + + virtual const VTranslateVars *TrVars() override; + + virtual void OpenSettings() override; + VPuzzleSettings *PuzzleSettings(); + void ActivateDarkMode(); + + void ParseCommandLine(const SocketConnection &connection, const QStringList &arguments); + +public slots: + void ProcessCMD(); + +protected: + virtual void InitTrVars() override; + virtual bool event(QEvent *e) override; + +protected slots: + virtual void AboutToQuit() override; + +private slots: + void NewLocalSocketConnection(); + +private: + Q_DISABLE_COPY(PuzzleApplication) + QList > mainWindows; + QLocalServer *localServer; + bool testMode; + + void Clean(); +}; + +#endif // PUZZLEAPPLICATION_H diff --git a/src/app/puzzle/puzzlemainwindow.cpp b/src/app/puzzle/puzzlemainwindow.cpp index 2c0321e4c..d690f7918 100644 --- a/src/app/puzzle/puzzlemainwindow.cpp +++ b/src/app/puzzle/puzzlemainwindow.cpp @@ -28,6 +28,7 @@ #include "puzzlemainwindow.h" #include "ui_puzzlemainwindow.h" +//--------------------------------------------------------------------------------------------------------------------- PuzzleMainWindow::PuzzleMainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::PuzzleMainWindow) @@ -35,7 +36,14 @@ PuzzleMainWindow::PuzzleMainWindow(QWidget *parent) : ui->setupUi(this); } +//--------------------------------------------------------------------------------------------------------------------- PuzzleMainWindow::~PuzzleMainWindow() { delete ui; } + +//--------------------------------------------------------------------------------------------------------------------- +bool PuzzleMainWindow::LoadFile(const QString &path) +{ + +} diff --git a/src/app/puzzle/puzzlemainwindow.h b/src/app/puzzle/puzzlemainwindow.h index bf1943946..8f489647d 100644 --- a/src/app/puzzle/puzzlemainwindow.h +++ b/src/app/puzzle/puzzlemainwindow.h @@ -42,6 +42,8 @@ public: explicit PuzzleMainWindow(QWidget *parent = nullptr); virtual ~PuzzleMainWindow(); + bool LoadFile(const QString &path); + private: Q_DISABLE_COPY(PuzzleMainWindow) Ui::PuzzleMainWindow *ui; diff --git a/src/app/puzzle/stable.cpp b/src/app/puzzle/stable.cpp new file mode 100644 index 000000000..460b5cb9c --- /dev/null +++ b/src/app/puzzle/stable.cpp @@ -0,0 +1,30 @@ +/************************************************************************ + ** + ** @file stable.cpp + ** @author Roman Telezhynskyi + ** @date November 15, 2013 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentina project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2013-2015 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ + +// Build the precompiled headers. +#include "stable.h" diff --git a/src/app/puzzle/stable.h b/src/app/puzzle/stable.h new file mode 100644 index 000000000..4611a10a7 --- /dev/null +++ b/src/app/puzzle/stable.h @@ -0,0 +1,72 @@ +/************************************************************************ + ** + ** @file stable.h + ** @author Roman Telezhynskyi + ** @date November 15, 2013 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentina project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2013-2015 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ + +#ifndef STABLE_H +#define STABLE_H + +/* I like to include this pragma too, so the build log indicates if pre-compiled headers were in use. */ +#pragma message("Compiling precompiled headers for tape utility.\n") + +/* Add C includes here */ + +#if defined __cplusplus +/* Add C++ includes here */ +#include + +/*In all cases we need include core header for getting defined values*/ +#ifdef QT_CORE_LIB +# include +#endif + +#ifdef QT_GUI_LIB +# include +#endif + +#ifdef QT_XML_LIB +# include +#endif + +//In Windows you can't use same header in all modes. +#if !defined(Q_OS_WIN) +# ifdef QT_WIDGETS_LIB +# include +# endif + + //Build doesn't work, if include this headers on Windows. +# ifdef QT_XMLPATTERNS_LIB +# include +# endif + +# ifdef QT_NETWORK_LIB +# include +# endif +#endif/*Q_OS_WIN*/ + +#endif /*__cplusplus*/ + +#endif // STABLE_H diff --git a/src/app/puzzle/version.h b/src/app/puzzle/version.h new file mode 100644 index 000000000..76e256844 --- /dev/null +++ b/src/app/puzzle/version.h @@ -0,0 +1,39 @@ +/************************************************************************ + ** + ** @file version.h + ** @author Roman Telezhynskyi + ** @date November 15, 2013 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentina project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2013-2015 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ + +#ifndef VERSION_H +#define VERSION_H + +#include "../../libs/vmisc/projectversion.h" + +#define VER_INTERNALNAME_STR "Puzzle" +#define VER_ORIGINALFILENAME_STR "puzzle.exe" +#define VER_PRODUCTNAME_STR "Tape" +#define VER_FILEDESCRIPTION_STR "Valentina's manual layout creator." + +#endif // VERSION_H diff --git a/src/app/tape/mapplication.cpp b/src/app/tape/mapplication.cpp index 385ea06b8..2eb7d73e9 100644 --- a/src/app/tape/mapplication.cpp +++ b/src/app/tape/mapplication.cpp @@ -412,8 +412,9 @@ void MApplication::InitOptions() ActivateDarkMode(); QResource::registerResource(diagramsPath()); } -// Dark mode +//--------------------------------------------------------------------------------------------------------------------- +// Dark mode void MApplication::ActivateDarkMode() { VTapeSettings *settings = qApp->TapeSettings(); diff --git a/src/app/tape/mapplication.h b/src/app/tape/mapplication.h index dc8cea765..a121ced88 100644 --- a/src/app/tape/mapplication.h +++ b/src/app/tape/mapplication.h @@ -68,7 +68,7 @@ public: virtual void OpenSettings() override; VTapeSettings *TapeSettings(); - void ActivateDarkMode(); + void ActivateDarkMode(); QString diagramsPath() const; diff --git a/src/libs/vmisc/vmisc.pri b/src/libs/vmisc/vmisc.pri index e62231411..c91baa68f 100644 --- a/src/libs/vmisc/vmisc.pri +++ b/src/libs/vmisc/vmisc.pri @@ -13,7 +13,8 @@ SOURCES += \ $$PWD/vtablesearch.cpp \ $$PWD/dialogs/dialogexporttocsv.cpp \ $$PWD/literals.cpp \ - $$PWD/vmodifierkey.cpp + $$PWD/vmodifierkey.cpp \ + $$PWD/vpuzzlesettings.cpp *msvc*:SOURCES += $$PWD/stable.cpp @@ -50,7 +51,8 @@ HEADERS += \ $$PWD/vdatastreamenum.h \ $$PWD/vmodifierkey.h \ $$PWD/typedef.h \ - $$PWD/backport/qscopeguard.h + $$PWD/backport/qscopeguard.h \ + $$PWD/vpuzzlesettings.h contains(DEFINES, APPIMAGE) { SOURCES += \ diff --git a/src/libs/vmisc/vpuzzlesettings.cpp b/src/libs/vmisc/vpuzzlesettings.cpp new file mode 100644 index 000000000..337fcb69a --- /dev/null +++ b/src/libs/vmisc/vpuzzlesettings.cpp @@ -0,0 +1,37 @@ +/************************************************************************ + ** + ** @file vpuzzlesettings.cpp + ** @author Roman Telezhynskyi + ** @date 16 2, 2020 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentina project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2020 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ +#include "vpuzzlesettings.h" + + +//--------------------------------------------------------------------------------------------------------------------- +VPuzzleSettings::VPuzzleSettings(Format format, Scope scope, const QString &organization, const QString &application, + QObject *parent) + :VCommonSettings(format, scope, organization, application, parent) +{ + +} diff --git a/src/libs/vmisc/vpuzzlesettings.h b/src/libs/vmisc/vpuzzlesettings.h new file mode 100644 index 000000000..f4ab19b01 --- /dev/null +++ b/src/libs/vmisc/vpuzzlesettings.h @@ -0,0 +1,46 @@ +/************************************************************************ + ** + ** @file vpuzzlesettings.h + ** @author Roman Telezhynskyi + ** @date 16 2, 2020 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentina project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 2020 Valentina project + ** All Rights Reserved. + ** + ** Valentina is free software: you can redistribute it and/or modify + ** it under the terms of the GNU General Public License as published by + ** the Free Software Foundation, either version 3 of the License, or + ** (at your option) any later version. + ** + ** Valentina is distributed in the hope that it will be useful, + ** but WITHOUT ANY WARRANTY; without even the implied warranty of + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ** GNU General Public License for more details. + ** + ** You should have received a copy of the GNU General Public License + ** along with Valentina. If not, see . + ** + *************************************************************************/ +#ifndef VPUZZLESETTINGS_H +#define VPUZZLESETTINGS_H + +#include + +#include "vcommonsettings.h" + +class VPuzzleSettings : public VCommonSettings +{ + Q_OBJECT +public: + VPuzzleSettings(Format format, Scope scope, const QString &organization, const QString &application = QString(), + QObject *parent = nullptr); + +private: + Q_DISABLE_COPY(VPuzzleSettings) +}; + +#endif // VPUZZLESETTINGS_H