From 7e2082fc0a3985a7c24d704c31d72e203f1f9e9a Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Sun, 13 Sep 2015 15:00:33 +0300 Subject: [PATCH 01/17] Portable DebugBreak(). --HG-- branch : develop --- src/libs/vmisc/debugbreak.h | 128 ++++++++++++++++++++++++++++++++++++ src/libs/vmisc/def.h | 20 ++---- src/libs/vmisc/vmisc.pri | 3 +- 3 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 src/libs/vmisc/debugbreak.h diff --git a/src/libs/vmisc/debugbreak.h b/src/libs/vmisc/debugbreak.h new file mode 100644 index 000000000..9a1926bbd --- /dev/null +++ b/src/libs/vmisc/debugbreak.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2011-2015, Scott Tsai + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DEBUG_BREAK_H +#define DEBUG_BREAK_H + +#ifdef _MSC_VER + +#define debug_break __debugbreak + +#else + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + /* gcc optimizers consider code after __builtin_trap() dead. + * Making __builtin_trap() unsuitable for breaking into the debugger */ + DEBUG_BREAK_PREFER_BUILTIN_TRAP_TO_SIGTRAP = 0, +}; + +#if defined(__i386__) || defined(__x86_64__) +enum { HAVE_TRAP_INSTRUCTION = 1, }; +__attribute__((gnu_inline, always_inline)) +static void __inline__ trap_instruction(void) +{ + __asm__ volatile("int $0x03"); +} +#elif defined(__thumb__) +enum { HAVE_TRAP_INSTRUCTION = 1, }; +/* FIXME: handle __THUMB_INTERWORK__ */ +__attribute__((gnu_inline, always_inline)) +static void __inline__ trap_instruction(void) +{ + /* See 'arm-linux-tdep.c' in GDB source. + * Both instruction sequences below work. */ +#if 1 + /* 'eabi_linux_thumb_le_breakpoint' */ + __asm__ volatile(".inst 0xde01"); +#else + /* 'eabi_linux_thumb2_le_breakpoint' */ + __asm__ volatile(".inst.w 0xf7f0a000"); +#endif + + /* Known problem: + * After a breakpoint hit, can't stepi, step, or continue in GDB. + * 'step' stuck on the same instruction. + * + * Workaround: a new GDB command, + * 'debugbreak-step' is defined in debugbreak-gdb.py + * that does: + * (gdb) set $instruction_len = 2 + * (gdb) tbreak *($pc + $instruction_len) + * (gdb) jump *($pc + $instruction_len) + */ +} +#elif defined(__arm__) && !defined(__thumb__) +enum { HAVE_TRAP_INSTRUCTION = 1, }; +__attribute__((gnu_inline, always_inline)) +static void __inline__ trap_instruction(void) +{ + /* See 'arm-linux-tdep.c' in GDB source, + * 'eabi_linux_arm_le_breakpoint' */ + __asm__ volatile(".inst 0xe7f001f0"); + /* Has same known problem and workaround + * as Thumb mode */ +} +#elif defined(__aarch64__) +enum { HAVE_TRAP_INSTRUCTION = 1, }; +__attribute__((gnu_inline, always_inline)) +static void __inline__ trap_instruction(void) +{ + /* See 'aarch64-tdep.c' in GDB source, + * 'aarch64_default_breakpoint' */ + __asm__ volatile(".inst 0xd4200000"); +} +#else +enum { HAVE_TRAP_INSTRUCTION = 0, }; +#endif + +__attribute__((gnu_inline, always_inline)) +static void __inline__ debug_break(void) +{ + if (HAVE_TRAP_INSTRUCTION) { + trap_instruction(); + } else if (DEBUG_BREAK_PREFER_BUILTIN_TRAP_TO_SIGTRAP) { + /* raises SIGILL on Linux x86{,-64}, to continue in gdb: + * (gdb) handle SIGILL stop nopass + * */ + __builtin_trap(); + } else { + raise(SIGTRAP); + } +} + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/src/libs/vmisc/def.h b/src/libs/vmisc/def.h index 80b3ab5cd..2d4b4b016 100644 --- a/src/libs/vmisc/def.h +++ b/src/libs/vmisc/def.h @@ -36,6 +36,8 @@ #include #endif /* Q_OS_WIN */ +#include "debugbreak.h" + #define SceneSize 50000 #define DefPointRadius 1.5//mm @@ -160,7 +162,6 @@ enum class GSizes : unsigned char { ALL, * https://stackoverflow.com/questions/1721543/continue-to-debug-after-failed-assertion-on-linux-c-c */ #ifndef V_NO_ASSERT -#ifdef Q_OS_WIN32 #ifdef Q_CC_MSVC #define SCASSERT(cond) \ { \ @@ -168,11 +169,11 @@ enum class GSizes : unsigned char { ALL, { \ qDebug("ASSERT: %s in %s (%s:%u)", \ #cond, __FUNCSIG__, __FILE__, __LINE__); \ - DebugBreak(); \ + debug_break(); \ } \ } \ -#else // GCC (Windows) +#else // GCC/Clang #define SCASSERT(cond) \ { \ @@ -180,23 +181,12 @@ enum class GSizes : unsigned char { ALL, { \ qDebug("ASSERT: %s in %s (%s:%u)", \ #cond, __PRETTY_FUNCTION__, __FILE__, __LINE__);\ - DebugBreak(); \ + debug_break(); \ } \ } \ #endif /*Q_CC_MSVC*/ -#else // UNIX -#define SCASSERT(cond) \ -{ \ - if (!(cond)) \ - { \ - qDebug("ASSERT: %s in %s (%s:%u)", \ - #cond, __PRETTY_FUNCTION__, __FILE__, __LINE__);\ - std::raise(SIGTRAP); \ - } \ -} \ -#endif /* Q_OS_WIN32 */ #else // define but disable this function if debugging is not set #define SCASSERT(cond) qt_noop(); #endif /* V_NO_ASSERT */ diff --git a/src/libs/vmisc/vmisc.pri b/src/libs/vmisc/vmisc.pri index 4d29598c7..3f7187d56 100644 --- a/src/libs/vmisc/vmisc.pri +++ b/src/libs/vmisc/vmisc.pri @@ -25,4 +25,5 @@ HEADERS += \ $$PWD/vabstractapplication.h \ $$PWD/projectversion.h \ $$PWD/vcommonsettings.h \ - $$PWD/vtapesettings.h + $$PWD/vtapesettings.h \ + debugbreak.h From 48d4ed59053382da8159f33ad3c0ff4575bc083f Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Mon, 14 Sep 2015 10:30:14 +0300 Subject: [PATCH 02/17] More pretty looking macros. --HG-- branch : develop --- src/libs/vmisc/def.h | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/libs/vmisc/def.h b/src/libs/vmisc/def.h index 2d4b4b016..d2b0cf9cd 100644 --- a/src/libs/vmisc/def.h +++ b/src/libs/vmisc/def.h @@ -162,31 +162,23 @@ enum class GSizes : unsigned char { ALL, * https://stackoverflow.com/questions/1721543/continue-to-debug-after-failed-assertion-on-linux-c-c */ #ifndef V_NO_ASSERT + #ifdef Q_CC_MSVC -#define SCASSERT(cond) \ -{ \ - if (!(cond)) \ - { \ - qDebug("ASSERT: %s in %s (%s:%u)", \ - #cond, __FUNCSIG__, __FILE__, __LINE__); \ - debug_break(); \ - } \ -} \ - +#define V_PRETTY_FUNCTION __FUNCSIG__ #else // GCC/Clang +#define V_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#endif /*Q_CC_MSVC*/ #define SCASSERT(cond) \ { \ if (!(cond)) \ { \ qDebug("ASSERT: %s in %s (%s:%u)", \ - #cond, __PRETTY_FUNCTION__, __FILE__, __LINE__);\ + #cond, V_PRETTY_FUNCTION, __FILE__, __LINE__); \ debug_break(); \ } \ } \ -#endif /*Q_CC_MSVC*/ - #else // define but disable this function if debugging is not set #define SCASSERT(cond) qt_noop(); #endif /* V_NO_ASSERT */ From 1e806ccc08f50aba5b5d9a8163cf069054deed79 Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Mon, 14 Sep 2015 10:38:04 +0300 Subject: [PATCH 03/17] New contributors. --HG-- branch : develop --- AUTHORS.txt | 7 ++++++- src/app/valentina/dialogs/dialogaboutapp.ui | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/AUTHORS.txt b/AUTHORS.txt index b245f676b..697abc497 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -26,4 +26,9 @@ Patch contributors: * Mac OS package. Peter Gsellmann * Testing - + Alex Zaharov + * Developing. + Valentina Zhuravska + * Developing. + * Testing. + * Translation. diff --git a/src/app/valentina/dialogs/dialogaboutapp.ui b/src/app/valentina/dialogs/dialogaboutapp.ui index 5806ee247..371e8d05f 100644 --- a/src/app/valentina/dialogs/dialogaboutapp.ui +++ b/src/app/valentina/dialogs/dialogaboutapp.ui @@ -214,7 +214,9 @@ Robert Martin Michaela Orth Rina Rivera Fritz Rometsch -Felix Ulber +Felix Ulber +Alex Zaharov +Valentina Zhuravska From cf0d1981cab1fc88b1edb651b11f8b564304e90d Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Mon, 14 Sep 2015 14:53:49 +0300 Subject: [PATCH 04/17] Moved lock-guarding abilities to separated template class. Changed application/mainwindow to use it. Most likelly mainwindow may be upgraded more, since lock can hold and manage memory for complex object like QFile, also it can have stored deallocator for the object (lambdas), so this is effectively should simple code, when you have allocation and freening resources declared on the same screen. --HG-- branch : develop --- src/app/tape/tmainwindow.cpp | 40 ++--- src/app/tape/tmainwindow.h | 9 +- src/app/valentina/core/vapplication.cpp | 113 +++++-------- src/app/valentina/core/vapplication.h | 9 +- src/app/valentina/mainwindow.cpp | 33 +--- src/app/valentina/mainwindow.h | 9 +- src/libs/vmisc/vabstractapplication.cpp | 44 ------ src/libs/vmisc/vabstractapplication.h | 8 +- src/libs/vmisc/vlockguard.h | 200 ++++++++++++++++++++++++ src/libs/vmisc/vmisc.pri | 3 +- 10 files changed, 268 insertions(+), 200 deletions(-) create mode 100644 src/libs/vmisc/vlockguard.h diff --git a/src/app/tape/tmainwindow.cpp b/src/app/tape/tmainwindow.cpp index e8a8331e3..8f433f598 100644 --- a/src/app/tape/tmainwindow.cpp +++ b/src/app/tape/tmainwindow.cpp @@ -37,6 +37,7 @@ #include "../ifc/xml/vvitconverter.h" #include "../ifc/xml/vvstconverter.h" #include "../ifc/xml/vpatternconverter.h" +#include "../vmisc/vlockguard.h" #include "vlitepattern.h" #include "../qmuparser/qmudef.h" #include "../vtools/dialogs/support/dialogeditwrongformula.h" @@ -48,9 +49,6 @@ #include #include #include -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) -# include -#endif #define DIALOG_MAX_FORMULA_HEIGHT 64 @@ -98,10 +96,6 @@ TMainWindow::~TMainWindow() delete m; } -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - delete lock; // Unlock pattern file -#endif - delete ui; } @@ -182,18 +176,13 @@ void TMainWindow::LoadFile(const QString &path) } } -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - lock = new QLockFile(QFileInfo(path).fileName()+".lock"); - lock->setStaleLockTime(0); - if (not MApplication::TryLock(lock)) + VlpCreateLock(lock, QFileInfo(path).fileName()+".lock"); + + if (lock->GetLockError() == QLockFile::LockFailedError) { - if (lock->error() == QLockFile::LockFailedError) - { - qCCritical(tMainWindow, "%s", tr("This file already opened in another window.").toUtf8().constData()); - return; - } + qCCritical(tMainWindow, "%s", tr("This file already opened in another window.").toUtf8().constData()); + return; } -#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) try { @@ -839,18 +828,13 @@ void TMainWindow::ImportFromPattern() return; } -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - QLockFile *lock = new QLockFile(QFileInfo(mPath).fileName()+".lock"); - lock->setStaleLockTime(0); - if (not MApplication::TryLock(lock)) + VLockGuard tmp(QFileInfo(mPath).fileName()+".lock"); + + if (tmp.GetLockError() == QLockFile::LockFailedError) { - if (lock->error() == QLockFile::LockFailedError) - { - qCCritical(tMainWindow, "%s", tr("This file already opened in another window.").toUtf8().constData()); - return; - } + qCCritical(tMainWindow, "%s", tr("This file already opened in another window.").toUtf8().constData()); + return; } -#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) #ifdef Q_OS_WIN32 qt_ntfs_permission_lookup++; // turn checking on @@ -878,8 +862,6 @@ void TMainWindow::ImportFromPattern() qt_ntfs_permission_lookup--; // turn it off again #endif /*Q_OS_WIN32*/ - delete lock; // release a pattern file - measurements = FilterMeasurements(measurements, m->ListAll()); qint32 currentRow; diff --git a/src/app/tape/tmainwindow.h b/src/app/tape/tmainwindow.h index 889505403..816c590dd 100644 --- a/src/app/tape/tmainwindow.h +++ b/src/app/tape/tmainwindow.h @@ -33,6 +33,7 @@ #include #include "../vmisc/def.h" +#include "../vmisc/vlockguard.h" #include "../vformat/vmeasurements.h" namespace Ui @@ -43,9 +44,6 @@ namespace Ui class QComboBox; class QTableWidgetItem; class QLabel; -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) -class QLockFile; -#endif class TMainWindow : public QMainWindow { @@ -133,10 +131,7 @@ private: QComboBox *gradationSizes; QComboBox *comboBoxUnits; int formulaBaseHeight; - -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - QLockFile *lock; -#endif + VLockGuardPtr lock; void SetupMenu(); void InitWindow(); diff --git a/src/app/valentina/core/vapplication.cpp b/src/app/valentina/core/vapplication.cpp index 549a73cc2..fed2815d8 100644 --- a/src/app/valentina/core/vapplication.cpp +++ b/src/app/valentina/core/vapplication.cpp @@ -53,6 +53,8 @@ Q_LOGGING_CATEGORY(vApp, "v.application") +constexpr auto DAYS_TO_KEEP_LOGS = 3; + //--------------------------------------------------------------------------------------------------------------------- inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { @@ -170,12 +172,8 @@ const QString VApplication::GistFileName = QStringLiteral("gist.json"); VApplication::VApplication(int &argc, char **argv) : VAbstractApplication(argc, argv), trVars(nullptr), autoSaveTimer(nullptr), - log(nullptr), - #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - out(nullptr), logLock(nullptr) - #else + lockLog(), out(nullptr) - #endif { VCommandLine::Reset(); // making sure will create new instance...just in case we will ever do 2 objects of VApplication VCommandLine::Get(*this); @@ -187,16 +185,6 @@ VApplication::~VApplication() { qCDebug(vApp, "Application closing."); qInstallMessageHandler(0); // Resore the message handler - delete out; - - if (log != nullptr) - { - log->close(); - delete log; -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - delete logLock; -#endif - } delete trVars; VCommandLine::Reset(); } @@ -418,32 +406,25 @@ void VApplication::CreateLogDir() const //--------------------------------------------------------------------------------------------------------------------- void VApplication::BeginLogging() { - log = new QFile(LogPath()); - if (log->open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) - { - out = new QTextStream(log); - qInstallMessageHandler(noisyFailureMsgHandler); + VlpCreateLock(lockLog, LogPath()+".lock", [this](){return new QFile(LogPath());}); -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - logLock = new QLockFile(LogPath()+".lock"); - logLock->setStaleLockTime(0); - if (TryLock(logLock)) + if (lockLog->IsLocked()) + { + if (lockLog->GetProtected()->open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { + out.reset(new QTextStream(lockLog->GetProtected().get())); + qInstallMessageHandler(noisyFailureMsgHandler); qCDebug(vApp, "Log file %s was locked.", LogPath().toUtf8().constData()); } else { - qCDebug(vApp, "Failed to lock %s", LogPath().toUtf8().constData()); - qCDebug(vApp, "Error type: %d", logLock->error()); + qCDebug(vApp, "Error opening log file \'%s\'. All debug output redirected to console.", + LogPath().toUtf8().constData()); } -#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) } else { - delete log; - log = nullptr; - qCDebug(vApp, "Error opening log file \'%s\'. All debug output redirected to console.", - LogPath().toUtf8().constData()); + qCDebug(vApp, "Failed to lock %s", LogPath().toUtf8().constData()); } } @@ -459,45 +440,29 @@ void VApplication::ClearOldLogs() const if (allFiles.isEmpty() == false) { qCDebug(vApp, "Clearing old logs"); - for (int i = 0; i < allFiles.size(); ++i) + for (int i = 0, sz = allFiles.size(); i < sz; ++i) { - QFileInfo info(allFiles.at(i)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - QLockFile *lock = new QLockFile(info.absoluteFilePath() + ".lock"); - if (TryLock(lock)) + auto fn = allFiles.at(i); + QFileInfo info(fn); + if (info.created().daysTo(QDateTime::currentDateTime()) >= DAYS_TO_KEEP_LOGS) { - qCDebug(vApp, "Locked file %s", info.absoluteFilePath().toUtf8().constData()); - QFile oldLog(allFiles.at(i)); - if (oldLog.remove()) + VLockGuard tmp(info.absoluteFilePath() + ".lock", [&fn](){return new QFile(fn);}); + if (tmp.GetProtected() != nullptr) { - qCDebug(vApp, "Deleted %s", info.absoluteFilePath().toUtf8().constData()); + if (tmp.GetProtected()->remove()) + { + qCDebug(vApp, "Deleted %s", info.absoluteFilePath().toUtf8().constData()); + } + else + { + qCDebug(vApp, "Could not delete %s", info.absoluteFilePath().toUtf8().constData()); + } } else { - qCDebug(vApp, "Could not delete %s", info.absoluteFilePath().toUtf8().constData()); + qCDebug(vApp, "Failed to lock %s", info.absoluteFilePath().toUtf8().constData()); } } - else - { - qCDebug(vApp, "Failed to lock %s", info.absoluteFilePath().toUtf8().constData()); - } - - delete lock; - lock = nullptr; -#else - if (info.created().daysTo(QDateTime::currentDateTime()) >= 3) - { - QFile oldLog(allFiles.at(i)); - if (oldLog.remove()) - { - qCDebug(vApp, "Deleted %s", info.absoluteFilePath().toUtf8().constData()); - } - else - { - qCDebug(vApp, "Could not delete %s", info.absoluteFilePath().toUtf8().constData()); - } - } -#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) } } else @@ -614,7 +579,7 @@ void VApplication::StartLogging() //--------------------------------------------------------------------------------------------------------------------- QTextStream *VApplication::LogFile() { - return out; + return out.get(); } //--------------------------------------------------------------------------------------------------------------------- @@ -707,27 +672,28 @@ void VApplication::GatherLogs() const const QStringList allFiles = logsDir.entryList(QDir::NoDotAndDotDot | QDir::Files); if (allFiles.isEmpty() == false) { - for (int i = 0; i < allFiles.size(); ++i) + for (int i = 0, sz = allFiles.size(); i < sz; ++i) { - QFileInfo info(allFiles.at(i)); + auto fn = allFiles.at(i); + QFileInfo info(fn); if (info.fileName() == "valentina.log") { continue; } - QLockFile *logLock = new QLockFile(info.absoluteFilePath()+".lock"); - logLock->setStaleLockTime(0); - if (TryLock(logLock)) + + VLockGuard tmp(info.absoluteFilePath() + ".lock", [&fn](){return new QFile(fn);}); + + if (tmp.IsLocked()) { *out <<"--------------------------" << endl; - QFile logFile(info.absoluteFilePath()); - if (logFile.open(QIODevice::ReadOnly | QIODevice::Text)) + if (tmp.GetProtected()->open(QIODevice::ReadOnly | QIODevice::Text)) { - QTextStream in(&logFile); + QTextStream in(&tmp.GetProtected()); while (!in.atEnd()) { *out << in.readLine() << endl; } - logFile.close(); + tmp.GetProtected()->close(); } else { @@ -736,9 +702,8 @@ void VApplication::GatherLogs() const } else { - *out << "Could not lock" << info.absoluteFilePath() << "."; + qCDebug(vApp, "Failed to lock %s", info.absoluteFilePath().toUtf8().constData()); } - delete logLock; } } else diff --git a/src/app/valentina/core/vapplication.h b/src/app/valentina/core/vapplication.h index 8d522e6d3..28e24e84d 100644 --- a/src/app/valentina/core/vapplication.h +++ b/src/app/valentina/core/vapplication.h @@ -36,7 +36,6 @@ #include "vsettings.h" #include "vcmdexport.h" - class VApplication;// use in define class VMainGraphicsView; class VPattern; @@ -95,11 +94,9 @@ private: VTranslateVars *trVars; QTimer *autoSaveTimer; - QFile *log; - QTextStream *out; -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - QLockFile *logLock; -#endif + VLockGuardPtr lockLog; + std::shared_ptr out; + void InitLineWidth(); #if defined(Q_OS_WIN) && defined(Q_CC_GNU) diff --git a/src/app/valentina/mainwindow.cpp b/src/app/valentina/mainwindow.cpp index dd18cec5e..5610c83cb 100644 --- a/src/app/valentina/mainwindow.cpp +++ b/src/app/valentina/mainwindow.cpp @@ -62,9 +62,6 @@ #include #include #include -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) -# include -#endif #include #include @@ -1960,13 +1957,8 @@ void MainWindow::OnlineHelp() void MainWindow::Clear() { qCDebug(vMainWindow, "Reseting main window."); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - delete lock; // Unlock pattern file - lock = nullptr; + lock.reset(); qCDebug(vMainWindow, "Unlocked pattern file."); -#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - ui->actionDetails->setChecked(true); ui->actionDraw->setChecked(true); ui->actionLayout->setEnabled(true); @@ -3034,9 +3026,6 @@ MainWindow::~MainWindow() CancelTool(); CleanLayout(); -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - delete lock; // Unlock pattern file -#endif delete doc; delete sceneDetails; delete sceneDraw; @@ -3065,26 +3054,24 @@ bool MainWindow::LoadPattern(const QString &fileName, const QString& customMeasu return false; } -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) qCDebug(vMainWindow, "Loking file"); - lock = new QLockFile(fileName+".lock"); - lock->setStaleLockTime(0); - if (VApplication::TryLock(lock)) + VlpCreateLock(lock, fileName+".lock"); + + if (lock->IsLocked()) { qCDebug(vMainWindow, "Pattern file %s was locked.", fileName.toUtf8().constData()); } else { qCDebug(vMainWindow, "Failed to lock %s", fileName.toUtf8().constData()); - qCDebug(vMainWindow, "Error type: %d", lock->error()); - if (lock->error() == QLockFile::LockFailedError) + qCDebug(vMainWindow, "Error type: %d", lock->GetLockError()); + if (lock->GetLockError() == QLockFile::LockFailedError) { qCCritical(vMainWindow, "%s", tr("This file already opened in another window.").toUtf8().constData()); Clear(); return false; } } -#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) // On this stage scene empty. Fit scene size to view size VMainGraphicsView::NewSceneRect(sceneDraw, ui->view); @@ -3187,14 +3174,11 @@ QStringList MainWindow::GetUnlokedRestoreFileList() const for (int i = 0; i < files.size(); ++i) { // Seeking file that realy need reopen - QLockFile *lock = new QLockFile(files.at(i)+".lock"); - lock->setStaleLockTime(0); - if (VApplication::TryLock(lock)) + VLockGuard tmp(files.at(i)+".lock"); + if (tmp.IsLocked()) { restoreFiles.append(files.at(i)); } - delete lock; - lock = nullptr; } // Clearing list after filtering @@ -3204,7 +3188,6 @@ QStringList MainWindow::GetUnlokedRestoreFileList() const } qApp->ValentinaSettings()->SetRestoreFileList(files); - } return restoreFiles; #else diff --git a/src/app/valentina/mainwindow.h b/src/app/valentina/mainwindow.h index d934cfa93..d2c29555c 100644 --- a/src/app/valentina/mainwindow.h +++ b/src/app/valentina/mainwindow.h @@ -37,7 +37,7 @@ #include "tools/vtooluniondetails.h" #include "tools/drawTools/drawtools.h" #include "core/vcmdexport.h" -#include +#include "../vmisc/vlockguard.h" #include @@ -47,9 +47,6 @@ namespace Ui } class VToolOptionsPropertyBrowser; -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) -class QLockFile; -#endif /** * @brief The MainWindow class main windows. @@ -230,9 +227,7 @@ private: QComboBox *gradationHeights; QComboBox *gradationSizes; VToolOptionsPropertyBrowser *toolOptions; -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - QLockFile *lock; -#endif + VLockGuardPtr lock; void ToolBarOption(); void ToolBarStages(); diff --git a/src/libs/vmisc/vabstractapplication.cpp b/src/libs/vmisc/vabstractapplication.cpp index 26d612620..d687acd66 100644 --- a/src/libs/vmisc/vabstractapplication.cpp +++ b/src/libs/vmisc/vabstractapplication.cpp @@ -29,10 +29,6 @@ #include "vabstractapplication.h" #include "../vmisc/def.h" -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) -# include -#endif - //--------------------------------------------------------------------------------------------------------------------- VAbstractApplication::VAbstractApplication(int &argc, char **argv) :QApplication(argc, argv), @@ -116,43 +112,3 @@ double VAbstractApplication::fromPixel(double pix) const { return FromPixel(pix, _patternUnit); } - -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) -//--------------------------------------------------------------------------------------------------------------------- -bool VAbstractApplication::TryLock(QLockFile *lock) -{ - if (lock == nullptr) - { - return false; - } - - if (lock->tryLock()) - { - return true; - } - else - { - if (lock->error() == QLockFile::LockFailedError) - { - // This happens if a stale lock file exists and another process uses that PID. - // Try removing the stale file, which will fail if a real process is holding a - // file-level lock. A false error is more problematic than not locking properly - // on corner-case systems. - if (lock->removeStaleLockFile() == false || lock->tryLock() == false) - { - return false; - } - else - { - return true; - } - } - else - { - return false; - } - return false; - } -} - -#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) diff --git a/src/libs/vmisc/vabstractapplication.h b/src/libs/vmisc/vabstractapplication.h index d8c7fec6e..2c687f7ee 100644 --- a/src/libs/vmisc/vabstractapplication.h +++ b/src/libs/vmisc/vabstractapplication.h @@ -33,15 +33,13 @@ #include #include "def.h" #include "vsettings.h" +#include "vlockguard.h" class VAbstractApplication;// use in define class VTranslateVars; class VAbstractPattern; class VMainGraphicsView; class QUndoStack; -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) -class QLockFile; -#endif #if defined(qApp) #undef qApp @@ -89,10 +87,6 @@ public: QUndoStack *getUndoStack() const; -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - static bool TryLock(QLockFile *lock); -#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - protected: QUndoStack *undoStack; diff --git a/src/libs/vmisc/vlockguard.h b/src/libs/vmisc/vlockguard.h new file mode 100644 index 000000000..395a1a265 --- /dev/null +++ b/src/libs/vmisc/vlockguard.h @@ -0,0 +1,200 @@ +/************************************************************************ + ** + ** @file VLockGuard.h + ** @author Alex Zaharov + ** @author Roman Telezhynskyi + ** @date 14 9, 2015 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentine project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 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 VLOCKGUARD_H +#define VLOCKGUARD_H + +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) +#include +#define PEDANT_COMPILER ,lock(nullptr) +#else +#define PEDANT_COMPILER +#warning To have lock-file support you must use Qt 5.1+. Expect collissions when run 2 copies of the program. +#endif + +/*@brief + * This class creates Guarded object if and only if lock file taken. It keeps shared_ptr to object and lock-file. + * Can use optional object allocator and deleter. + * + * On older Qt lock assumed always taken and compile-time warning is shown. + * +*/ +template +class VLockGuard +{ +public: + VLockGuard(const QString& lockName, int stale = 0, int timeout = 0); + + template + VLockGuard(const QString& lockName, Alloc a, int stale = 0, int timeout=0); + + template + VLockGuard(const QString& lockName, Alloc a, Delete d, int stale = 0, int timeout=0); + + const std::shared_ptr &GetProtected() const; + int GetLockError() const; + bool IsLocked() const; + +private: + Q_DISABLE_COPY(VLockGuard) + + std::shared_ptr holder; + int lockError; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) + std::shared_ptr lock; +#endif + + bool TryLock(const QString &lockName, int stale, int timeout); +}; + +//--------------------------------------------------------------------------------------------------------------------- +template +VLockGuard::VLockGuard(const QString &lockName, int stale, int timeout) + : holder(nullptr), lockError(0) PEDANT_COMPILER +{ + if (TryLock(lockName, stale, timeout)) + { + holder.reset(new Guarded()); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +//using allocator lambdas seems logically better than supplying pointer, because we will take ownership of allocated +//object +template template +VLockGuard::VLockGuard(const QString& lockName, Alloc a, int stale, int timeout) + : holder(nullptr), lockError(0) PEDANT_COMPILER +{ + if (TryLock(lockName, stale, timeout)) + { + holder.reset(a()); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +template template +VLockGuard::VLockGuard(const QString& lockName, Alloc a, Delete d, int stale, int timeout) + : holder(nullptr), lockError(0) PEDANT_COMPILER +{ + if (TryLock(lockName, stale, timeout)) + { + holder.reset(a(), d); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +template +const std::shared_ptr &VLockGuard::GetProtected() const +{ + return holder; +} + +//--------------------------------------------------------------------------------------------------------------------- +template +int VLockGuard::GetLockError() const +{ + return lockError; +} + +//--------------------------------------------------------------------------------------------------------------------- +template +bool VLockGuard::IsLocked() const +{ + return holder != nullptr; +} + +//--------------------------------------------------------------------------------------------------------------------- +template +bool VLockGuard::TryLock(const QString &lockName, int stale, int timeout) +{ + bool res = true; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) + + lock.reset(new QLockFile(lockName)); + lock->setStaleLockTime(stale); + + for (int i = 0; i < 2 && !lock->tryLock(timeout); i++) + { + if (QLockFile::LockFailedError != lock->error()) + { + break; + } + else + { + // This happens if a stale lock file exists and another process uses that PID. + // Try removing the stale file, which will fail if a real process is holding a + // file-level lock. A false error is more problematic than not locking properly + // on corner-case systems. + lock->removeStaleLockFile(); + lock->tryLock(timeout); + } + } + res = QLockFile::NoError == (lockError = lock->error()); + if (!res) + { + lock.reset(); + } +#endif + return res; +} + +#undef PEDANT_COMPILER + +//use pointer and function below to persistent things like class-member, because lock is taken by constructor +//helper functions allow to write shorter creating and setting new lock-pointer + +//new C++11 - "template typedef" http://stackoverflow.com/questions/2795023/c-template-typedef +template +using VLockGuardPtr = std::shared_ptr>; + +template +void VlpCreateLock(VLockGuardPtr& r, const QString& lockName, int stale = 0, int timeout = 0) +{ + r.reset(new VLockGuard(lockName, stale, timeout)); +} + +template +void VlpCreateLock(VLockGuardPtr& r, const QString& lockName, Alloc a, int stale = 0, int timeout = 0) +{ + r.reset(new VLockGuard(lockName, a, stale, timeout)); +} + +template +void VlpCreateLock(VLockGuardPtr& r, const QString& lockName, Alloc a, Del d, int stale = 0, int timeout = 0) +{ + r.reset(new VLockGuard(lockName, a, d, stale, timeout)); +} + +#endif // VLOCKGUARD_H diff --git a/src/libs/vmisc/vmisc.pri b/src/libs/vmisc/vmisc.pri index 3f7187d56..775357374 100644 --- a/src/libs/vmisc/vmisc.pri +++ b/src/libs/vmisc/vmisc.pri @@ -26,4 +26,5 @@ HEADERS += \ $$PWD/projectversion.h \ $$PWD/vcommonsettings.h \ $$PWD/vtapesettings.h \ - debugbreak.h + $$PWD/debugbreak.h \ + $$PWD/vlockguard.h From 8dc048a47bf5b88460d2a9135ac835fcfe66c27a Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Tue, 15 Sep 2015 15:34:20 +0300 Subject: [PATCH 05/17] Fixed typo in measurement name. --HG-- branch : develop --- src/libs/vmisc/def.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/vmisc/def.cpp b/src/libs/vmisc/def.cpp index 51614c68a..1979f3f6a 100644 --- a/src/libs/vmisc/def.cpp +++ b/src/libs/vmisc/def.cpp @@ -193,7 +193,7 @@ const QString neckSideToBustSideB_M = QStringLiteral("neck_side_to_bus const QString shoulderTipToWaistB_1inOffset_M = QStringLiteral("shoulder_tip_to_waist_b_1in_offset"); // H13 // I const QString armShoulderTipToWristBent_M = QStringLiteral("arm_shoulder_tip_to_wrist_bent"); // I01 -const QString armShoulderTipToElbowBent_M = QStringLiteral(" arm_shoulder_tip_to_elbow_bent"); // I02 +const QString armShoulderTipToElbowBent_M = QStringLiteral("arm_shoulder_tip_to_elbow_bent"); // I02 const QString armElbowToWristBent_M = QStringLiteral("arm_elbow_to_wrist_bent"); // I03 const QString armElbowCircBent_M = QStringLiteral("arm_elbow_circ_bent"); // I04 const QString armShoulderTipToWrist_M = QStringLiteral("arm_shoulder_tip_to_wrist"); // I05 From 902fe178e508c1dbb7bc880cc9be6249ae1a20f0 Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Tue, 15 Sep 2015 19:49:24 +0300 Subject: [PATCH 06/17] Resolved issue #364. Add Search feature to Measurement dialogs. --HG-- branch : develop --- src/app/tape/tape.pri | 6 +- src/app/tape/tmainwindow.cpp | 54 ++++++++- src/app/tape/tmainwindow.h | 7 +- src/app/tape/tmainwindow.ui | 65 ++++++++++- src/app/tape/vtablesearch.cpp | 208 ++++++++++++++++++++++++++++++++++ src/app/tape/vtablesearch.h | 58 ++++++++++ 6 files changed, 392 insertions(+), 6 deletions(-) create mode 100644 src/app/tape/vtablesearch.cpp create mode 100644 src/app/tape/vtablesearch.h diff --git a/src/app/tape/tape.pri b/src/app/tape/tape.pri index ae955616d..745b28067 100644 --- a/src/app/tape/tape.pri +++ b/src/app/tape/tape.pri @@ -12,7 +12,8 @@ SOURCES += \ $$PWD/dialogs/tapeconfigdialog.cpp \ $$PWD/dialogs/configpages/tapeconfigurationpage.cpp \ $$PWD/dialogs/configpages/tapepathpage.cpp \ - $$PWD/vlitepattern.cpp + $$PWD/vlitepattern.cpp \ + $$PWD/vtablesearch.cpp HEADERS += \ $$PWD/tmainwindow.h \ @@ -25,7 +26,8 @@ HEADERS += \ $$PWD/dialogs/tapeconfigdialog.h \ $$PWD/dialogs/configpages/tapeconfigurationpage.h \ $$PWD/dialogs/configpages/tapepathpage.h \ - $$PWD/vlitepattern.h + $$PWD/vlitepattern.h \ + $$PWD/vtablesearch.h FORMS += \ $$PWD/tmainwindow.ui \ diff --git a/src/app/tape/tmainwindow.cpp b/src/app/tape/tmainwindow.cpp index 8f433f598..4e39e180b 100644 --- a/src/app/tape/tmainwindow.cpp +++ b/src/app/tape/tmainwindow.cpp @@ -68,9 +68,11 @@ TMainWindow::TMainWindow(QWidget *parent) gradationSizes(nullptr), comboBoxUnits(nullptr), formulaBaseHeight(0), - lock(nullptr) + lock(nullptr), + search() { ui->setupUi(this); + search = QSharedPointer(new VTableSearch(ui->tableWidget)); ui->tabWidget->setVisible(false); ui->mainToolBar->setContextMenuPolicy(Qt::PreventContextMenu); @@ -350,6 +352,24 @@ void TMainWindow::OpenTemplate() } } +//--------------------------------------------------------------------------------------------------------------------- +void TMainWindow::Find(const QString &term) +{ + search->Find(term); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TMainWindow::FindPrevious() +{ + search->FindPrevious(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void TMainWindow::FindNext() +{ + search->FindNext(); +} + //--------------------------------------------------------------------------------------------------------------------- void TMainWindow::closeEvent(QCloseEvent *event) { @@ -602,7 +622,9 @@ void TMainWindow::Remove() MeasurementsWasSaved(false); + search->RemoveRow(row); RefreshData(); + search->RefreshList(ui->lineEditFind->text()); if (ui->tableWidget->rowCount() > 0) { @@ -668,6 +690,7 @@ void TMainWindow::MoveUp() m->MoveUp(nameField->text()); MeasurementsWasSaved(false); RefreshData(); + search->RefreshList(ui->lineEditFind->text()); ui->tableWidget->selectRow(row-1); } @@ -685,6 +708,7 @@ void TMainWindow::MoveDown() m->MoveDown(nameField->text()); MeasurementsWasSaved(false); RefreshData(); + search->RefreshList(ui->lineEditFind->text()); ui->tableWidget->selectRow(row+1); } @@ -719,6 +743,8 @@ void TMainWindow::Fx() RefreshData(); + search->RefreshList(ui->lineEditFind->text()); + ui->tableWidget->selectRow(row); } delete dialog; @@ -749,7 +775,9 @@ void TMainWindow::AddCustom() m->AddEmptyAfter(nameField->text(), name); } + search->AddRow(currentRow); RefreshData(); + search->RefreshList(ui->lineEditFind->text()); ui->tableWidget->selectRow(currentRow); @@ -778,6 +806,8 @@ void TMainWindow::AddKnown() { m->AddEmpty(list.at(i)); } + + search->AddRow(currentRow); } } else @@ -795,11 +825,13 @@ void TMainWindow::AddKnown() { m->AddEmptyAfter(after, list.at(i)); } + search->AddRow(currentRow); after = list.at(i); } } RefreshData(); + search->RefreshList(ui->lineEditFind->text()); ui->tableWidget->selectRow(currentRow); @@ -888,6 +920,8 @@ void TMainWindow::ImportFromPattern() RefreshData(); + search->RefreshList(ui->lineEditFind->text()); + ui->tableWidget->selectRow(currentRow); MeasurementsWasSaved(false); @@ -899,6 +933,7 @@ void TMainWindow::ChangedSize(const QString &text) const int row = ui->tableWidget->currentRow(); data->SetSize(text.toInt()); RefreshData(); + search->RefreshList(ui->lineEditFind->text()); ui->tableWidget->selectRow(row); } @@ -908,6 +943,7 @@ void TMainWindow::ChangedHeight(const QString &text) const int row = ui->tableWidget->currentRow(); data->SetHeight(text.toInt()); RefreshData(); + search->RefreshList(ui->lineEditFind->text()); ui->tableWidget->selectRow(row); } @@ -1052,6 +1088,7 @@ void TMainWindow::SaveMName() m->SetMName(nameField->text(), newName); MeasurementsWasSaved(false); RefreshData(); + search->RefreshList(ui->lineEditFind->text()); ui->tableWidget->blockSignals(true); ui->tableWidget->selectRow(row); @@ -1121,6 +1158,7 @@ void TMainWindow::SaveMValue() const QTextCursor cursor = ui->plainTextEditFormula->textCursor(); RefreshData(); + search->RefreshList(ui->lineEditFind->text()); ui->tableWidget->blockSignals(true); ui->tableWidget->selectRow(row); @@ -1145,6 +1183,7 @@ void TMainWindow::SaveMBaseValue(double value) MeasurementsWasSaved(false); RefreshData(); + search->RefreshList(ui->lineEditFind->text()); ui->tableWidget->blockSignals(true); ui->tableWidget->selectRow(row); @@ -1167,6 +1206,7 @@ void TMainWindow::SaveMSizeIncrease(double value) MeasurementsWasSaved(false); RefreshData(); + search->RefreshList(ui->lineEditFind->text()); ui->tableWidget->blockSignals(true); ui->tableWidget->selectRow(row); @@ -1189,6 +1229,7 @@ void TMainWindow::SaveMHeightIncrease(double value) MeasurementsWasSaved(false); RefreshData(); + search->RefreshList(ui->lineEditFind->text()); ui->tableWidget->selectRow(row); } @@ -1446,6 +1487,10 @@ void TMainWindow::InitWindow() connect(ui->toolButtonExpr, &QToolButton::clicked, this, &TMainWindow::Fx); } + connect(ui->lineEditFind, &QLineEdit::textEdited, this, &TMainWindow::Find); + connect(ui->toolButtonFindPrevious, &QToolButton::clicked, this, &TMainWindow::FindPrevious); + connect(ui->toolButtonFindNext, &QToolButton::clicked, this, &TMainWindow::FindNext); + ui->plainTextEditNotes->setPlainText(m->Notes()); connect(ui->plainTextEditNotes, &QPlainTextEdit::textChanged, this, &TMainWindow::SaveNotes); @@ -1660,6 +1705,7 @@ void TMainWindow::SetDefaultSize(int value) //--------------------------------------------------------------------------------------------------------------------- void TMainWindow::RefreshData() { + data->ClearUniqueNames(); data->ClearVariables(VarType::Measurement); m->ReadMeasurements(); @@ -1799,6 +1845,10 @@ void TMainWindow::MFields(bool enabled) ui->pushButtonGrow->setEnabled(enabled); ui->toolButtonExpr->setEnabled(enabled); } + + ui->lineEditFind->setEnabled(enabled); + ui->toolButtonFindPrevious->setEnabled(enabled); + ui->toolButtonFindNext->setEnabled(enabled); } //--------------------------------------------------------------------------------------------------------------------- @@ -1985,6 +2035,8 @@ void TMainWindow::UpdatePatternUnit() ShowUnits(); RefreshTable(); + search->RefreshList(ui->lineEditFind->text()); + ui->tableWidget->selectRow(row); } diff --git a/src/app/tape/tmainwindow.h b/src/app/tape/tmainwindow.h index 816c590dd..3115d4127 100644 --- a/src/app/tape/tmainwindow.h +++ b/src/app/tape/tmainwindow.h @@ -35,6 +35,7 @@ #include "../vmisc/def.h" #include "../vmisc/vlockguard.h" #include "../vformat/vmeasurements.h" +#include "vtablesearch.h" namespace Ui { @@ -113,10 +114,11 @@ private slots: void SaveMFullName(); void NewWindow(); - void Preferences(); - void PatternUnitChanged(int index); + void Find(const QString &term); + void FindPrevious(); + void FindNext(); private: Q_DISABLE_COPY(TMainWindow) @@ -132,6 +134,7 @@ private: QComboBox *comboBoxUnits; int formulaBaseHeight; VLockGuardPtr lock; + QSharedPointer search; void SetupMenu(); void InitWindow(); diff --git a/src/app/tape/tmainwindow.ui b/src/app/tape/tmainwindow.ui index 6d9cd8037..d2ad073ef 100644 --- a/src/app/tape/tmainwindow.ui +++ b/src/app/tape/tmainwindow.ui @@ -7,7 +7,7 @@ 0 0 536 - 694 + 726 @@ -43,6 +43,69 @@ Measurements + + + + + + Find: + + + + + + + false + + + + + + + false + + + Find Previous + + + ... + + + + + + + + Ctrl+Shift+G + + + + + + + false + + + Find Next + + + ... + + + + + + + + Ctrl+G + + + false + + + + + diff --git a/src/app/tape/vtablesearch.cpp b/src/app/tape/vtablesearch.cpp new file mode 100644 index 000000000..330973824 --- /dev/null +++ b/src/app/tape/vtablesearch.cpp @@ -0,0 +1,208 @@ +/************************************************************************ + ** + ** @file vtablesearch.cpp + ** @author Roman Telezhynskyi + ** @date 15 9, 2015 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentine project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 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 . + ** + *************************************************************************/ + +#include "vtablesearch.h" +#include "../vmisc/def.h" + +//--------------------------------------------------------------------------------------------------------------------- +VTableSearch::VTableSearch(QTableWidget *table) + :table(table), + searchIndex(-1), + searchList() +{ +} + +//--------------------------------------------------------------------------------------------------------------------- +void VTableSearch::Clear() +{ + SCASSERT(table != nullptr); + + foreach(QTableWidgetItem *item, searchList) + { + if (item->row() % 2 != 0 && table->alternatingRowColors()) + { + item->setBackground(QPalette().alternateBase()); + } + else + { + item->setBackground(QPalette().base()); + } + } + searchIndex = -1; +} + +//--------------------------------------------------------------------------------------------------------------------- +void VTableSearch::ShowNext(int newIndex) +{ + if (not searchList.isEmpty()) + { + QTableWidgetItem *item = searchList.at(searchIndex); + item->setBackground(Qt::yellow); + + item = searchList.at(newIndex); + item->setBackground(Qt::red); + table->scrollToItem(item); + searchIndex = newIndex; + } + else + { + Clear(); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void VTableSearch::Find(const QString &term) +{ + SCASSERT(table != nullptr); + + const QList list = table->findItems(term, Qt::MatchContains); + + if (list.isEmpty() || term.isEmpty()) + { + Clear(); + } + else + { + Clear(); + + searchList = list; + foreach(QTableWidgetItem *item, searchList) + { + item->setBackground(Qt::yellow); + } + + searchIndex = 0; + QTableWidgetItem *item = searchList.at(searchIndex); + item->setBackground(Qt::red); + table->scrollToItem(item); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void VTableSearch::FindPrevious() +{ + int newIndex = searchIndex - 1; + + if (newIndex < 0) + { + newIndex = searchList.size() - 1; + } + + ShowNext(newIndex); +} + +//--------------------------------------------------------------------------------------------------------------------- +void VTableSearch::FindNext() +{ + int newIndex = searchIndex + 1; + + if (newIndex >= searchList.size()) + { + newIndex = 0; + } + + ShowNext(newIndex); +} + +//--------------------------------------------------------------------------------------------------------------------- +void VTableSearch::RemoveRow(int row) +{ + if (searchIndex < 0 || searchIndex >= searchList.size()) + { + return; + } + + const int indexRow = searchList.at(searchIndex)->row(); + + if (row <= indexRow) + { + foreach(QTableWidgetItem *item, searchList) + { + if (item->row() == row) + { + --searchIndex; + } + } + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void VTableSearch::AddRow(int row) +{ + if (searchIndex < 0 || searchIndex >= searchList.size()) + { + return; + } + + const int indexRow = searchList.at(searchIndex)->row(); + + if (row <= indexRow) + { + foreach(QTableWidgetItem *item, searchList) + { + if (item->row() == row) + { + ++searchIndex; + } + } + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void VTableSearch::RefreshList(const QString &term) +{ + SCASSERT(table != nullptr); + + if (term.isEmpty()) + { + return; + } + + searchList = table->findItems(term, Qt::MatchContains); + + foreach(QTableWidgetItem *item, searchList) + { + item->setBackground(Qt::yellow); + } + + if (not searchList.isEmpty()) + { + if (searchIndex < 0) + { + searchIndex = searchList.size() - 1; + } + else if (searchIndex >= searchList.size()) + { + searchIndex = 0; + } + + QTableWidgetItem *item = searchList.at(searchIndex); + item->setBackground(Qt::red); + table->scrollToItem(item); + } +} diff --git a/src/app/tape/vtablesearch.h b/src/app/tape/vtablesearch.h new file mode 100644 index 000000000..187f5571d --- /dev/null +++ b/src/app/tape/vtablesearch.h @@ -0,0 +1,58 @@ +/************************************************************************ + ** + ** @file vtablesearch.h + ** @author Roman Telezhynskyi + ** @date 15 9, 2015 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentine project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 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 VTABLESEARCH_H +#define VTABLESEARCH_H + +#include +#include + +class VTableSearch +{ +public: + VTableSearch(QTableWidget *table); + + void Find(const QString &term); + void FindPrevious(); + void FindNext(); + void RemoveRow(int row); + void AddRow(int row); + void RefreshList(const QString &term); + +private: + Q_DISABLE_COPY(VTableSearch) + + QTableWidget *table; + int searchIndex; + QList searchList; + + void Clear(); + void ShowNext(int newIndex); +}; + +#endif // VTABLESEARCH_H From 7718b0748e2b8378942e892da34c8fa3af22bc70 Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Tue, 15 Sep 2015 21:21:51 +0300 Subject: [PATCH 07/17] Show a message box with an error. Release a lock file after error. --HG-- branch : develop --- src/app/tape/mapplication.cpp | 90 +++++++++++++++++++++++++++++++++++ src/app/tape/tmainwindow.cpp | 2 + 2 files changed, 92 insertions(+) diff --git a/src/app/tape/mapplication.cpp b/src/app/tape/mapplication.cpp index 0533b44c9..bb9a11292 100644 --- a/src/app/tape/mapplication.cpp +++ b/src/app/tape/mapplication.cpp @@ -38,6 +38,8 @@ #include #include #include +#include +#include #if QT_VERSION < QT_VERSION_CHECK(5, 2, 0) # include "../../libs/vmisc/backport/qcommandlineparser.h" @@ -45,6 +47,92 @@ # include #endif +//--------------------------------------------------------------------------------------------------------------------- +inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + Q_UNUSED(context) + + // Why on earth didn't Qt want to make failed signal/slot connections qWarning? + if ((type == QtDebugMsg) && msg.contains("::connect")) + { + type = QtWarningMsg; + } + + // 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("QPainter::begin") && msg.contains("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) && QString(msg).contains("QClipboard::event") + && QString(msg).contains("Cowardly refusing")) + { + type = QtDebugMsg; + } + + // 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 (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 = (qApp->activeModalWidget() == nullptr) || + !qApp->activeModalWidget()->inherits("QFileDialog"); + QMessageBox messageBox; + switch (type) + { + case QtDebugMsg: + std::cerr << msg.toUtf8().constData(); + std::cerr.flush(); + return; + case QtWarningMsg: + messageBox.setIcon(QMessageBox::Warning); + break; + case QtCriticalMsg: + messageBox.setIcon(QMessageBox::Critical); + break; + case QtFatalMsg: + messageBox.setIcon(QMessageBox::Critical); + break; + default: + break; + } + + if (type == QtWarningMsg || type == QtCriticalMsg || type == QtFatalMsg) + { + if (topWinAllowsPop) + { + messageBox.setInformativeText(msg); + messageBox.setStandardButtons(QMessageBox::Ok); + messageBox.setWindowModality(Qt::ApplicationModal); + messageBox.setModal(true); + messageBox.exec(); + } + } + + if (QtFatalMsg == type) + { + abort(); + } + } + else + { + if (type != QtDebugMsg) + { + abort(); // be NOISY unless overridden! + } + } +} + //--------------------------------------------------------------------------------------------------------------------- MApplication::MApplication(int &argc, char **argv) :VAbstractApplication(argc, argv), @@ -138,6 +226,8 @@ QList MApplication::MainWindows() //--------------------------------------------------------------------------------------------------------------------- void MApplication::InitOptions() { + qInstallMessageHandler(noisyFailureMsgHandler); + OpenSettings(); qDebug()<<"Version:"<GetLockError() == QLockFile::LockFailedError) { qCCritical(tMainWindow, "%s", tr("This file already opened in another window.").toUtf8().constData()); + lock.reset(); return; } @@ -248,6 +249,7 @@ void TMainWindow::LoadFile(const QString &path) m = nullptr; delete data; data = nullptr; + lock.reset(); return; } } From f722964c11644e5cc06ebc97caa8fa2c00b9181f Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Wed, 16 Sep 2015 08:16:27 +0300 Subject: [PATCH 08/17] Add increment's formula to the export list. --HG-- branch : develop --- src/libs/ifc/xml/vabstractpattern.cpp | 23 +++++++++++++++++++++++ src/libs/ifc/xml/vabstractpattern.h | 1 + 2 files changed, 24 insertions(+) diff --git a/src/libs/ifc/xml/vabstractpattern.cpp b/src/libs/ifc/xml/vabstractpattern.cpp index 79ca5f56d..acb1113f2 100644 --- a/src/libs/ifc/xml/vabstractpattern.cpp +++ b/src/libs/ifc/xml/vabstractpattern.cpp @@ -1018,6 +1018,7 @@ QStringList VAbstractPattern::ListExpressions() const list << ListPointExpressions(); list << ListArcExpressions(); list << ListSplineExpressions(); + list << ListIncrementExpressions(); return list; } @@ -1176,6 +1177,28 @@ QStringList VAbstractPattern::ListPathPointExpressions() const return expressions; } +//--------------------------------------------------------------------------------------------------------------------- +QStringList VAbstractPattern::ListIncrementExpressions() const +{ + QStringList expressions; + const QDomNodeList list = elementsByTagName(TagIncrement); + for (int i=0; i < list.size(); ++i) + { + const QDomElement dom = list.at(i).toElement(); + + try + { + expressions.append(GetParametrString(dom, IncrementFormula)); + } + catch (VExceptionEmptyParameter &e) + { + Q_UNUSED(e) + } + } + + return expressions; +} + //--------------------------------------------------------------------------------------------------------------------- bool VAbstractPattern::IsVariable(const QString &token) const { diff --git a/src/libs/ifc/xml/vabstractpattern.h b/src/libs/ifc/xml/vabstractpattern.h index 4b03c80b0..e65a0d79c 100644 --- a/src/libs/ifc/xml/vabstractpattern.h +++ b/src/libs/ifc/xml/vabstractpattern.h @@ -248,6 +248,7 @@ private: QStringList ListArcExpressions() const; QStringList ListSplineExpressions() const; QStringList ListPathPointExpressions() const; + QStringList ListIncrementExpressions() const; bool IsVariable(const QString& token) const; bool IsPostfixOperator(const QString& token) const; From 2e2a6e8f5cc9638d0439f0a049fa172bc5592608 Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Wed, 16 Sep 2015 09:04:43 +0300 Subject: [PATCH 09/17] Validate unique measurement name. --HG-- branch : develop --- src/libs/ifc/schema/individual_measurements/v0.3.0.xsd | 4 ++++ src/libs/ifc/schema/standard_measurements/v0.4.0.xsd | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/libs/ifc/schema/individual_measurements/v0.3.0.xsd b/src/libs/ifc/schema/individual_measurements/v0.3.0.xsd index dcb80e614..063c6a49e 100644 --- a/src/libs/ifc/schema/individual_measurements/v0.3.0.xsd +++ b/src/libs/ifc/schema/individual_measurements/v0.3.0.xsd @@ -34,6 +34,10 @@ + + + + diff --git a/src/libs/ifc/schema/standard_measurements/v0.4.0.xsd b/src/libs/ifc/schema/standard_measurements/v0.4.0.xsd index fa14bdda1..04b047f89 100644 --- a/src/libs/ifc/schema/standard_measurements/v0.4.0.xsd +++ b/src/libs/ifc/schema/standard_measurements/v0.4.0.xsd @@ -35,6 +35,10 @@ + + + + From e75951eb8e88df33860b42cfac8d1f8c5977c87c Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Wed, 16 Sep 2015 09:14:00 +0300 Subject: [PATCH 10/17] Validate unique increment name. --HG-- branch : develop --- src/libs/ifc/schema/pattern/v0.2.0.xsd | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/ifc/schema/pattern/v0.2.0.xsd b/src/libs/ifc/schema/pattern/v0.2.0.xsd index 72519bb93..65456be2a 100644 --- a/src/libs/ifc/schema/pattern/v0.2.0.xsd +++ b/src/libs/ifc/schema/pattern/v0.2.0.xsd @@ -74,6 +74,10 @@ + + + + From b3f28699753a15017ca75b86e1fda698ac731db9 Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Wed, 16 Sep 2015 09:21:34 +0300 Subject: [PATCH 11/17] Fixed debug output. --HG-- branch : develop --- src/app/tape/mapplication.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/tape/mapplication.cpp b/src/app/tape/mapplication.cpp index bb9a11292..87b529e9d 100644 --- a/src/app/tape/mapplication.cpp +++ b/src/app/tape/mapplication.cpp @@ -91,8 +91,7 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con switch (type) { case QtDebugMsg: - std::cerr << msg.toUtf8().constData(); - std::cerr.flush(); + std::cerr << msg.toUtf8().constData() << std::endl; return; case QtWarningMsg: messageBox.setIcon(QMessageBox::Warning); From f2a111f0d299153003f4771030a9bf774207d608 Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Wed, 16 Sep 2015 11:03:15 +0300 Subject: [PATCH 12/17] Show popup message "Measurements was updated". Fixed path to measurements when sync. --HG-- branch : develop --- src/app/valentina/mainwindow.cpp | 12 ++++++++---- src/libs/vwidgets/vwidgetpopup.cpp | 19 +++++++++++++++++++ src/libs/vwidgets/vwidgetpopup.h | 2 ++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/app/valentina/mainwindow.cpp b/src/app/valentina/mainwindow.cpp index 5610c83cb..696064c43 100644 --- a/src/app/valentina/mainwindow.cpp +++ b/src/app/valentina/mainwindow.cpp @@ -46,6 +46,7 @@ #include "../vformat/vmeasurements.h" #include "../ifc/xml/vvstconverter.h" #include "../ifc/xml/vvitconverter.h" +#include "../vwidgets/vwidgetpopup.h" #include #include @@ -1159,13 +1160,16 @@ void MainWindow::SyncMeasurements() { if (mChanges) { - if(LoadMeasurements(doc->MPath())) + const QString path = AbsoluteMPath(curFile, doc->MPath()); + if(LoadMeasurements(path)) { - if (not watcher->files().contains(doc->MPath())) + if (not watcher->files().contains(path)) { - watcher->addPath(doc->MPath()); + watcher->addPath(path); } - helpLabel->setText(tr("Measurements updated")); + const QString msg = tr("Measurements was updated"); + helpLabel->setText(msg); + VWidgetPopup::PopupMessage(this, msg); doc->LiteParseTree(Document::LiteParse); mChanges = false; } diff --git a/src/libs/vwidgets/vwidgetpopup.cpp b/src/libs/vwidgets/vwidgetpopup.cpp index 5329dafca..44f102a0b 100644 --- a/src/libs/vwidgets/vwidgetpopup.cpp +++ b/src/libs/vwidgets/vwidgetpopup.cpp @@ -31,6 +31,9 @@ #include #include #include +#include + +#include "../vmisc/def.h" //--------------------------------------------------------------------------------------------------------------------- VWidgetPopup::VWidgetPopup(QWidget *parent) @@ -77,6 +80,22 @@ void VWidgetPopup::SetWidget(QWidget *widget, bool own) } } +//--------------------------------------------------------------------------------------------------------------------- +void VWidgetPopup::PopupMessage(QWidget *w, const QString &msg) +{ + SCASSERT(w != nullptr); + + VWidgetPopup *popup = new VWidgetPopup(); + QLabel *label = new QLabel(msg); + QFont f = label->font(); + f.setBold(true); + f.setPixelSize(16); + label->setFont(f); + popup->SetWidget(label); + popup->SetLifeTime(2000); + popup->Show(w->frameGeometry().center()); +} + //--------------------------------------------------------------------------------------------------------------------- void VWidgetPopup::Show(QPoint coord) { diff --git a/src/libs/vwidgets/vwidgetpopup.h b/src/libs/vwidgets/vwidgetpopup.h index eb6529435..aadb6cc19 100644 --- a/src/libs/vwidgets/vwidgetpopup.h +++ b/src/libs/vwidgets/vwidgetpopup.h @@ -67,6 +67,8 @@ public: int GetLifeTime() const; void SetLifeTime(int value); + static void PopupMessage(QWidget *w, const QString &msg); + public slots: /** Pops up the widget at global coordinates \a coord. */ void Show(QPoint coord); From ad28a200f7e7526b9596d606b956ae38c5039911 Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Wed, 16 Sep 2015 11:03:33 +0300 Subject: [PATCH 13/17] Removed unused variable. --HG-- branch : develop --- src/libs/vtools/undocommands/deletepatternpiece.cpp | 3 +-- src/libs/vtools/undocommands/deletepatternpiece.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/vtools/undocommands/deletepatternpiece.cpp b/src/libs/vtools/undocommands/deletepatternpiece.cpp index 28a509b86..65e774f1e 100644 --- a/src/libs/vtools/undocommands/deletepatternpiece.cpp +++ b/src/libs/vtools/undocommands/deletepatternpiece.cpp @@ -31,14 +31,13 @@ //--------------------------------------------------------------------------------------------------------------------- DeletePatternPiece::DeletePatternPiece(VAbstractPattern *doc, const QString &namePP, QUndoCommand *parent) - : VUndoCommand(QDomElement(), doc, parent), namePP(namePP), patternPiece(QDomElement()), mPath(QString()), + : VUndoCommand(QDomElement(), doc, parent), namePP(namePP), patternPiece(QDomElement()), previousPPName(QString()) { setText(tr("delete pattern piece %1").arg(namePP)); QDomElement patternP = doc->GetPPElement(namePP); patternPiece = patternP.cloneNode().toElement(); - mPath = doc->MPath(); QDomNode previousPP = patternP.previousSibling();//find previous pattern piece previousPPName = doc->GetParametrString(previousPP.toElement(), VAbstractPattern::AttrName, ""); } diff --git a/src/libs/vtools/undocommands/deletepatternpiece.h b/src/libs/vtools/undocommands/deletepatternpiece.h index bc7af8efb..641ceecdf 100644 --- a/src/libs/vtools/undocommands/deletepatternpiece.h +++ b/src/libs/vtools/undocommands/deletepatternpiece.h @@ -43,7 +43,6 @@ private: Q_DISABLE_COPY(DeletePatternPiece) QString namePP; QDomElement patternPiece; - QString mPath; QString previousPPName; }; From 54b3f642f3854734447552a540be7d1539221d84 Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Wed, 16 Sep 2015 14:29:01 +0300 Subject: [PATCH 14/17] Relock a pattern file after SaveAs. --HG-- branch : develop --- src/app/tape/tmainwindow.cpp | 30 +++++++++++++++++++ src/app/valentina/mainwindow.cpp | 50 +++++++++++++++++++++++++++----- 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/app/tape/tmainwindow.cpp b/src/app/tape/tmainwindow.cpp index 2be9b9cc4..ccb0c4362 100644 --- a/src/app/tape/tmainwindow.cpp +++ b/src/app/tape/tmainwindow.cpp @@ -473,6 +473,22 @@ void TMainWindow::FileSaveAs() { fileName += "." + suffix; } + + if (QFileInfo(fileName).exists()) + { + VLockGuard tmp(fileName + ".lock"); + + if (not tmp.IsLocked()) + { + if (lock->GetLockError() == QLockFile::LockFailedError) + { + qCCritical(tMainWindow, "%s", tr("Failed to lock. This file already opened in another window.") + .toUtf8().constData()); + return; + } + } + } + QString error; bool result = SaveMeasurements(fileName, error); if (result == false) @@ -484,6 +500,20 @@ void TMainWindow::FileSaveAs() messageBox.setDetailedText(error); messageBox.setStandardButtons(QMessageBox::Ok); messageBox.exec(); + + return; + } + + lock.reset(); + + VlpCreateLock(lock, fileName + ".lock"); + + if (lock->GetLockError() == QLockFile::LockFailedError) + { + qCCritical(tMainWindow, "%s", tr("Failed to lock. This file already opened in another window. " + "Expect collissions when run 2 copies of the program.").toUtf8().constData()); + lock.reset(); + return; } } diff --git a/src/app/valentina/mainwindow.cpp b/src/app/valentina/mainwindow.cpp index 696064c43..891cfe571 100644 --- a/src/app/valentina/mainwindow.cpp +++ b/src/app/valentina/mainwindow.cpp @@ -1837,9 +1837,24 @@ bool MainWindow::SaveAs() { fileName += ".val"; } - const QString oldFileName = curFile; + + if (QFileInfo(fileName).exists()) + { + VLockGuard tmp(fileName + ".lock"); + + if (not tmp.IsLocked()) + { + if (lock->GetLockError() == QLockFile::LockFailedError) + { + qCCritical(vMainWindow, "%s", tr("Failed to lock. This file already opened in another window.") + .toUtf8().constData()); + return false; + } + } + } + QString error; - bool result = SavePattern(fileName, error); + const bool result = SavePattern(fileName, error); if (result == false) { QMessageBox messageBox; @@ -1849,14 +1864,33 @@ bool MainWindow::SaveAs() messageBox.setDetailedText(error); messageBox.setStandardButtons(QMessageBox::Ok); messageBox.exec(); + + return result; } - if (oldFileName != curFile) - {// Now we have new file name after save as. - // But still have previous name in restore list. We should delete them. - QStringList restoreFiles = qApp->ValentinaSettings()->GetRestoreFileList(); - restoreFiles.removeAll(oldFileName); - qApp->ValentinaSettings()->SetRestoreFileList(restoreFiles); + + qCDebug(vMainWindow, "Unlock old file"); + lock.reset(); + + qCDebug(vMainWindow, "Locking file"); + VlpCreateLock(lock, fileName+".lock"); + + if (lock->IsLocked()) + { + qCDebug(vMainWindow, "Pattern file %s was locked.", fileName.toUtf8().constData()); } + else + { + qCDebug(vMainWindow, "Failed to lock %s", fileName.toUtf8().constData()); + qCDebug(vMainWindow, "Error type: %d", lock->GetLockError()); + if (lock->GetLockError() == QLockFile::LockFailedError) + { + qCCritical(vMainWindow, "%s", tr("Failed to lock. This file already opened in another window. " + "Expect collissions when run 2 copies of the program.") + .toUtf8().constData()); + lock.reset(); + } + } + return result; } From 0dc78c818ca0de0c8f2800fecdc676b106924a11 Mon Sep 17 00:00:00 2001 From: Sergey Alyoshin Date: Wed, 16 Sep 2015 15:51:24 +0300 Subject: [PATCH 15/17] Internationalize coordinates format in status line --HG-- branch : develop --- src/app/valentina/mainwindow.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/valentina/mainwindow.cpp b/src/app/valentina/mainwindow.cpp index 891cfe571..851ba4f01 100644 --- a/src/app/valentina/mainwindow.cpp +++ b/src/app/valentina/mainwindow.cpp @@ -1373,9 +1373,10 @@ void MainWindow::currentPPChanged(int index) */ void MainWindow::mouseMove(const QPointF &scenePos) { - QString string = QString("%1, %2 (%3)").arg(static_cast(qApp->fromPixel(scenePos.x()))) + //: Coords in status line: "X, Y (units)" + QString string = QString(tr("%1, %2 (%3)")).arg(static_cast(qApp->fromPixel(scenePos.x()))) .arg(static_cast(qApp->fromPixel(scenePos.y()))) - .arg(doc->UnitsToStr(qApp->patternUnit())); + .arg(doc->UnitsToStr(qApp->patternUnit(), true)); if (mouseCoordinate != nullptr) { mouseCoordinate->setText(string); From 826aac16d3aa7407c5a44b9adee286a9ceb54384 Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Wed, 16 Sep 2015 16:47:32 +0300 Subject: [PATCH 16/17] Run sending a crash report "silently". --HG-- branch : develop --- src/app/valentina/core/vapplication.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/valentina/core/vapplication.cpp b/src/app/valentina/core/vapplication.cpp index fed2815d8..712c8f802 100644 --- a/src/app/valentina/core/vapplication.cpp +++ b/src/app/valentina/core/vapplication.cpp @@ -889,7 +889,9 @@ void VApplication::SendReport(const QString &reportName) const const QString arg = QString("curl.exe -k -H \"Authorization: bearer ")+token.join("")+ QString("\" -H \"Accept: application/json\" -H \"Content-type: application/json\" -X POST " "--data @gist.json https://api.github.com/gists"); - QProcess::startDetached(arg); + QProcess proc; + proc.start(arg); + proc.waitForFinished(10000); // 10 sec reportFile.remove();// Clear after yourself } else From ec31f7268d2e27d83a5d1a7a73e15906e404229a Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Thu, 17 Sep 2015 21:44:15 +0300 Subject: [PATCH 17/17] Test original and translated measurement names. --HG-- branch : develop --- src/app/valentina/valentina.pro | 2 +- src/test/ValentinaTest/ValentinaTest.pro | 55 +++- src/test/ValentinaTest/qttestmainlambda.cpp | 2 + .../ValentinaTest/tst_measurementregexp.cpp | 234 ++++++++++++++++++ .../ValentinaTest/tst_measurementregexp.h | 61 +++++ 5 files changed, 344 insertions(+), 10 deletions(-) create mode 100644 src/test/ValentinaTest/tst_measurementregexp.cpp create mode 100644 src/test/ValentinaTest/tst_measurementregexp.h diff --git a/src/app/valentina/valentina.pro b/src/app/valentina/valentina.pro index f64ed64d5..b25a4b603 100644 --- a/src/app/valentina/valentina.pro +++ b/src/app/valentina/valentina.pro @@ -344,7 +344,7 @@ INSTALL_TRANSLATIONS += \ $${TRANSLATIONS_PATH}/measurements_p9_fi_FI.qm \ $${TRANSLATIONS_PATH}/measurements_p9_en_US.qm -TRANSLATIONS += \ +INSTALL_TRANSLATIONS += \ $${TRANSLATIONS_PATH}/measurements_p10_ru_RU.qm \ $${TRANSLATIONS_PATH}/measurements_p10_uk_UA.qm \ $${TRANSLATIONS_PATH}/measurements_p10_de_DE.qm \ diff --git a/src/test/ValentinaTest/ValentinaTest.pro b/src/test/ValentinaTest/ValentinaTest.pro index 2678849d2..3eeac216d 100644 --- a/src/test/ValentinaTest/ValentinaTest.pro +++ b/src/test/ValentinaTest/ValentinaTest.pro @@ -42,7 +42,8 @@ SOURCES += \ tst_nameregexp.cpp \ tst_vlayoutdetail.cpp \ tst_varc.cpp \ - stable.cpp + stable.cpp \ + tst_measurementregexp.cpp HEADERS += \ tst_vposter.h \ @@ -52,7 +53,8 @@ HEADERS += \ tst_nameregexp.h \ tst_vlayoutdetail.h \ tst_varc.h \ - stable.h + stable.h \ + tst_measurementregexp.h # Set using ccache. Function enable_ccache() defined in common.pri. $$enable_ccache() @@ -115,6 +117,42 @@ CONFIG(debug, debug|release){ } } +#VPatternDB static library (depend on vgeometry, vmisc, VLayout) +unix|win32: LIBS += -L$$OUT_PWD/../../libs/vpatterndb/$${DESTDIR} -lvpatterndb + +INCLUDEPATH += $$PWD/../../libs/vpatterndb +DEPENDPATH += $$PWD/../../libs/vpatterndb + +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 + +#VMisc static library +unix|win32: LIBS += -L$$OUT_PWD/../../libs/vmisc/$${DESTDIR}/ -lvmisc + +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 + +# VGeometry static library (depend on ifc) +unix|win32: LIBS += -L$$OUT_PWD/../../libs/vgeometry/$${DESTDIR} -lvgeometry + +INCLUDEPATH += $$PWD/../../libs/vgeometry +DEPENDPATH += $$PWD/../../libs/vgeometry + +win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vgeometry/$${DESTDIR}/vgeometry.lib +else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vgeometry/$${DESTDIR}/libvgeometry.a + +# IFC static library (depend on QMuParser) +unix|win32: LIBS += -L$$OUT_PWD/../../libs/ifc/$${DESTDIR}/ -lifc + +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 + # VLayout static library unix|win32: LIBS += -L$$OUT_PWD/../../libs/vlayout/$${DESTDIR} -lvlayout @@ -124,11 +162,10 @@ DEPENDPATH += $$PWD/../../libs/vlayout win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vlayout/$${DESTDIR}/vlayout.lib else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vlayout/$${DESTDIR}/libvlayout.a -# VGeometry static library -unix|win32: LIBS += -L$$OUT_PWD/../../libs/vgeometry/$${DESTDIR} -lvgeometry +# QMuParser library +win32:CONFIG(release, debug|release): LIBS += -L$${OUT_PWD}/../../libs/qmuparser/$${DESTDIR} -lqmuparser2 +else:win32:CONFIG(debug, debug|release): LIBS += -L$${OUT_PWD}/../../libs/qmuparser/$${DESTDIR} -lqmuparser2 +else:unix: LIBS += -L$${OUT_PWD}/../../libs/qmuparser/$${DESTDIR} -lqmuparser -INCLUDEPATH += $$PWD/../../libs/vgeometry -DEPENDPATH += $$PWD/../../libs/vgeometry - -win32:!win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vgeometry/$${DESTDIR}/vgeometry.lib -else:unix|win32-g++: PRE_TARGETDEPS += $$OUT_PWD/../../libs/vgeometry/$${DESTDIR}/libvgeometry.a +INCLUDEPATH += $${PWD}/../../libs/qmuparser +DEPENDPATH += $${PWD}/../../libs/qmuparser diff --git a/src/test/ValentinaTest/qttestmainlambda.cpp b/src/test/ValentinaTest/qttestmainlambda.cpp index 08a415890..53346a4e0 100644 --- a/src/test/ValentinaTest/qttestmainlambda.cpp +++ b/src/test/ValentinaTest/qttestmainlambda.cpp @@ -34,6 +34,7 @@ #include "tst_nameregexp.h" #include "tst_vlayoutdetail.h" #include "tst_varc.h" +#include "tst_measurementregexp.h" int main(int argc, char** argv) { @@ -52,6 +53,7 @@ int main(int argc, char** argv) ASSERT_TEST(new TST_NameRegExp()); ASSERT_TEST(new TST_VLayoutDetail()); ASSERT_TEST(new TST_VArc()); + ASSERT_TEST(new TST_MeasurementRegExp()); return status; } diff --git a/src/test/ValentinaTest/tst_measurementregexp.cpp b/src/test/ValentinaTest/tst_measurementregexp.cpp new file mode 100644 index 000000000..8d94bc86c --- /dev/null +++ b/src/test/ValentinaTest/tst_measurementregexp.cpp @@ -0,0 +1,234 @@ +/************************************************************************ + ** + ** @file tst_measurementregexp.cpp + ** @author Roman Telezhynskyi + ** @date 16 9, 2015 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentine project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 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 . + ** + *************************************************************************/ + +#include "tst_measurementregexp.h" +#include "../qmuparser/qmudef.h" +#include "../vmisc/def.h" +#include "../vpatterndb/vtranslatemeasurements.h" + +#include +#include + +enum ErrorState {ErrorLoad = 0, ErrorInstall, ErrorSize, NoError}; + +//--------------------------------------------------------------------------------------------------------------------- +TST_MeasurementRegExp::TST_MeasurementRegExp(QObject *parent) + :QObject(parent), + pmsTranslator(nullptr), + trMs(nullptr) +{ +} + +//--------------------------------------------------------------------------------------------------------------------- +TST_MeasurementRegExp::~TST_MeasurementRegExp() +{ + delete pmsTranslator; + delete trMs; +} + +//--------------------------------------------------------------------------------------------------------------------- +// cppcheck-suppress unusedFunction +void TST_MeasurementRegExp::TestOriginalMeasurementNamesRegExp() +{ + const QStringList originalNames = OriginalNames(); + const QRegularExpression re(NameRegExp()); + + foreach(const QString &str, originalNames) + { + QCOMPARE(re.match(str).hasMatch(), true); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +// cppcheck-suppress unusedFunction +void TST_MeasurementRegExp::TestMeasurementRegExp() +{ + const int systemCounts = 55; + const QStringList locales {"ru_RU", "uk_UA", "de_DE", "cs_CZ", "he_IL", "fr_FR", "it_IT", "nl_NL", "id_ID", + "es_ES", "fi_FI", "en_US"}; + + { + const int combinations = systemCounts * locales.size(); // 55*12=660 + + QDir dir(TranslationsPath()); + const QStringList fileNames = dir.entryList(QStringList("measurements_p*_*.qm")); + + QVERIFY2(combinations == fileNames.size(), "Unexpected count of files."); + } + + for(int s = 0; s < systemCounts; ++s) + { + for(int l = 0, sz = locales.size(); l < sz; ++l) + { + const int res = LoadTranslation(QString("p%1").arg(s), locales.at(l)); + + switch(res) + { + case ErrorInstall: + case ErrorSize: + case ErrorLoad: + { + const QString message = QString("Failed to check translation for system = p%1 and locale = %2") + .arg(s) + .arg(locales.at(l)); + QFAIL(message.toUtf8().constData()); + break; + } + case NoError: + { + CheckNames(); + + if (not pmsTranslator.isNull()) + { + const bool result = QCoreApplication::removeTranslator(pmsTranslator); + + if (result == false) + { + const QString message = QString("Can't remove translation for system = p%1 and locale = %2") + .arg(s) + .arg(locales.at(l)); + QWARN(message.toUtf8().constData()); + } + delete pmsTranslator; + } + } + default: + QWARN("Unexpected state"); + } + } + } +} + +//--------------------------------------------------------------------------------------------------------------------- +QString TST_MeasurementRegExp::TranslationsPath() const +{ + return QApplication::applicationDirPath() + QStringLiteral("/../../../app/valentina/bin/translations"); +} + +//--------------------------------------------------------------------------------------------------------------------- +int TST_MeasurementRegExp::LoadTranslation(const QString &checkedSystem, const QString &checkedLocale) +{ + const QString path = TranslationsPath(); + const QString file = QString("measurements_%1_%2.qm").arg(checkedSystem).arg(checkedLocale); + + if (QFileInfo(path+"/"+file).size() <= 34) + { + const QString message = QString("Translation for system = %1 and locale = %2 is empty. \nFull path: %3/%4") + .arg(checkedSystem) + .arg(checkedLocale) + .arg(path) + .arg(file); + QWARN(message.toUtf8().constData()); + + return ErrorSize; + } + + pmsTranslator = new QTranslator(this); + + if (not pmsTranslator->load(file, path)) + { + const QString message = QString("Can't load translation for system = %1 and locale = %2. \nFull path: %3/%4") + .arg(checkedSystem) + .arg(checkedLocale) + .arg(path) + .arg(file); + QWARN(message.toUtf8().constData()); + + delete pmsTranslator; + + return ErrorLoad; + } + + if (not QCoreApplication::installTranslator(pmsTranslator)) + { + const QString message = QString("Can't install translation for system = %1 and locale = %2. \nFull path: %3/%4") + .arg(checkedSystem) + .arg(checkedLocale) + .arg(path) + .arg(file); + QWARN(message.toUtf8().constData()); + + delete pmsTranslator; + + return ErrorInstall; + } + + InitTrMs();//Very important do it after load QM file. + + return NoError; +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_MeasurementRegExp::InitTrMs() +{ + if (trMs != nullptr) + { + trMs->Retranslate(); + } + else + { + trMs = new VTranslateMeasurements(); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void TST_MeasurementRegExp::CheckNames() const +{ + const QStringList originalNames = OriginalNames(); + const QRegularExpression re(NameRegExp()); + + foreach(const QString &str, originalNames) + { + const QString translated = trMs->MToUser(str); + QCOMPARE(re.match(translated).hasMatch(), true); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +QStringList TST_MeasurementRegExp::OriginalNames() const +{ + const QStringList originalNames = QStringList() << ListGroupA() + << ListGroupB() + << ListGroupC() + << ListGroupD() + << ListGroupE() + << ListGroupF() + << ListGroupG() + << ListGroupH() + << ListGroupI() + << ListGroupJ() + << ListGroupK() + << ListGroupL() + << ListGroupM() + << ListGroupN() + << ListGroupO() + << ListGroupP() + << ListGroupQ(); + + return originalNames; +} diff --git a/src/test/ValentinaTest/tst_measurementregexp.h b/src/test/ValentinaTest/tst_measurementregexp.h new file mode 100644 index 000000000..0385bd847 --- /dev/null +++ b/src/test/ValentinaTest/tst_measurementregexp.h @@ -0,0 +1,61 @@ +/************************************************************************ + ** + ** @file tst_measurementregexp.h + ** @author Roman Telezhynskyi + ** @date 16 9, 2015 + ** + ** @brief + ** @copyright + ** This source code is part of the Valentine project, a pattern making + ** program, whose allow create and modeling patterns of clothing. + ** Copyright (C) 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 TST_MEASUREMENTREGEXP_H +#define TST_MEASUREMENTREGEXP_H + +#include + +class QTranslator; +class VTranslateMeasurements; + +class TST_MeasurementRegExp : public QObject +{ + Q_OBJECT +public: + explicit TST_MeasurementRegExp(QObject *parent = 0); + virtual ~TST_MeasurementRegExp() Q_DECL_OVERRIDE; + +private slots: + void TestOriginalMeasurementNamesRegExp(); + void TestMeasurementRegExp(); + +private: + Q_DISABLE_COPY(TST_MeasurementRegExp) + + QPointer pmsTranslator; + VTranslateMeasurements *trMs; + + QString TranslationsPath() const; + int LoadTranslation(const QString &checkedSystem, const QString &checkedLocale); + void InitTrMs(); + void CheckNames() const; + QStringList OriginalNames() const; +}; + +#endif // TST_MEASUREMENTREGEXP_H