diff options
42 files changed, 945 insertions, 715 deletions
diff --git a/Changelog.txt b/Changelog.txt index 19cb9bf4..3ff45e86 100755 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,9 +1,25 @@ +FreeFileSync 9.3 +---------------- +Support multiple connections per FTP folder traversal: N times speed up +Improved folder traversal time by 35% for FTP servers supporting MLSD +Use single CWD when changing FTP working directory +Maximize FTP input/output speed using prefetch/output buffers and async execution +Use larger socket buffer for significant FTP upload speed increase +Fixed out of memory error when copying large files via FTP +New popup dialog option to ignore all errors +Reduced memory peaks by enforcing streaming buffer size limits +Removed custom sync directions from config XML if not needed +Fixed EOPNOTSUPP error on gvfs-mounted FTP (Linux) +Prevent input focus stealing after manual comparison +Flash task bar after comparison if other app has input focus + + FreeFileSync 9.2 [2017-07-03] ----------------------------- +Use direct copy instead of transaction to speed up versioning Replaced file existing handling with use of unique temporary names Support SFTP authentication via Pageant/SSH agent New menu option to restore hidden panels individually -Use direct copy instead of transaction to speed up versioning Fixed GTK button icon being truncated (Linux) Fixed error dialog hiding behind progress dialog (macOS) Round out FTP symlink deletion handling diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng index 14a89170..ef2a8463 100755 --- a/FreeFileSync/Build/Languages/german.lng +++ b/FreeFileSync/Build/Languages/german.lng @@ -88,17 +88,14 @@ <source>Syntax:</source> <target>Syntax:</target> -<source>global config file:</source> -<target>Globale Konfigurationsdatei:</target> - <source>config files:</source> <target>Konfigurationsdateien:</target> <source>directory</source> <target>Verzeichnis</target> -<source>Path to an alternate GlobalSettings.xml file.</source> -<target>Pfad zu alternativer GlobalSettings.xml Datei.</target> +<source>global config file:</source> +<target>Globale Konfigurationsdatei:</target> <source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source> <target>Beliebige Anzahl von FreeFileSync .ffs_gui und/oder .ffs_batch Konfigurationsdateien.</target> @@ -109,6 +106,9 @@ <source>Open configuration for editing without executing it.</source> <target>Konfiguration zum Editieren öffnen ohne sie auszuführen.</target> +<source>Path to an alternate GlobalSettings.xml file.</source> +<target>Pfad zu alternativer GlobalSettings.xml Datei.</target> + <source>Cannot find the following folders:</source> <target>Die folgenden Ordner wurden nicht gefunden:</target> @@ -253,8 +253,8 @@ <source>Update attributes on right</source> <target>Aktualisiere Attribute des rechten Elements</target> -<source>Cannot write file %x.</source> -<target>Die Datei %x kann nicht geschrieben werden.</target> +<source>Cannot read file %x.</source> +<target>Die Datei %x kann nicht gelesen werden.</target> <source> Unexpected size of data stream. @@ -273,12 +273,15 @@ Tatsächlich: %y bytes <source>Operation not supported for different base folder types.</source> <target>Der Vorgang wird für unterschiedliche Basisordner nicht unterstützt.</target> -<source>Cannot copy symbolic link %x to %y.</source> -<target>Die symbolische Verknüpfung %x kann nicht nach %y kopiert werden.</target> +<source>Cannot write file %x.</source> +<target>Die Datei %x kann nicht geschrieben werden.</target> <source>Cannot move file %x to %y.</source> <target>Die Datei %x kann nicht nach %y verschoben werden.</target> +<source>Cannot copy symbolic link %x to %y.</source> +<target>Die symbolische Verknüpfung %x kann nicht nach %y kopiert werden.</target> + <source>Unable to connect to %x.</source> <target>Es kann keine Verbindung zu %x aufgebaut werden.</target> @@ -291,9 +294,6 @@ Tatsächlich: %y bytes <source>Cannot read directory %x.</source> <target>Das Verzeichnis %x kann nicht gelesen werden.</target> -<source>Cannot read file %x.</source> -<target>Die Datei %x kann nicht gelesen werden.</target> - <source>Cannot read file attributes of %x.</source> <target>Die Dateiattribute von %x können nicht gelesen werden.</target> @@ -596,8 +596,8 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Automated Synchronization</source> <target>Automatisierte Synchronisation</target> -<source>The following path does not support directory monitoring:</source> -<target>Der folgende Pfad unterstützt keine Verzeichnisüberwachung:</target> +<source>The %x protocol does not support directory monitoring:</source> +<target>Das %x Protokoll unterstützt keine Verzeichnisüberwachung:</target> <source>Directory monitoring active</source> <target>Verzeichnisüberwachung ist aktiv</target> @@ -776,8 +776,8 @@ Die Befehlszeile wird ausgelöst, wenn: <pluralform>Automatische Wiederholung in %x Sekunden...</pluralform> </target> -<source>&Ignore subsequent errors</source> -<target>&Nachfolgende Fehler ignorieren</target> +<source>Ignore &all</source> +<target>&Alle ignorieren</target> <source>Retrying operation...</source> <target>Wiederhole Vorgang...</target> @@ -884,12 +884,12 @@ Die Befehlszeile wird ausgelöst, wenn: <source>&Find...</source> <target>S&uchen...</target> -<source>&Reset layout</source> -<target>Oberfläche &zurücksetzen</target> - <source>&Export file list...</source> <target>Dateiliste e&xportieren...</target> +<source>&Reset layout</source> +<target>Oberfläche &zurücksetzen</target> + <source>&Tools</source> <target>E&xtras</target> @@ -1117,6 +1117,9 @@ Die Befehlszeile wird ausgelöst, wenn: <source>&Key file</source> <target>&Schlüsseldatei</target> +<source>&SSH agent</source> +<target>&SSH Agent</target> + <source>User name:</source> <target>Benutzername:</target> @@ -1135,8 +1138,8 @@ Die Befehlszeile wird ausgelöst, wenn: <source>How to get best performance?</source> <target>Wie erhält man die beste Leistung?</target> -<source>SSH connections for directory reading:</source> -<target>SSH Verbindungen zum Lesen des Verzeichnisses:</target> +<source>Connections for directory reading:</source> +<target>Verbindungen zum Verzeichnislesen:</target> <source>Suggested range: [1 - 10]</source> <target>Empfohlener Bereich: [1 - 10]</target> @@ -1150,6 +1153,9 @@ Die Befehlszeile wird ausgelöst, wenn: <source>2 connections x 10 channels = 20 times faster directory reading</source> <target>2 Verbindungen x 10 Kanäle = 20 mal schnelleres Lesen des Verzeichnisses</target> +<source>4 connections = 4 times faster directory reading</source> +<target>4 Verbindungen = 4 mal schnelleres Verzeichnislesen</target> + <source>Select a directory on the server:</source> <target>Verzeichnis auf dem Server auswählen:</target> @@ -1363,6 +1369,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Overview</source> <target>Übersicht</target> +<source>Show "%x"</source> +<target>Zeige "%x"</target> + <source>&Show details</source> <target>&Zeige Details</target> @@ -1459,9 +1468,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Select time span...</source> <target>Zeitspanne auswählen...</target> -<source>Show "%x"</source> -<target>Zeige "%x"</target> - <source>Last session</source> <target>Letzte Sitzung</target> @@ -1615,6 +1621,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Key password:</source> <target>Schlüsselpasswort:</target> +<source>Please enter a file path.</source> +<target>Bitte geben Sie einen Dateipfad ein.</target> + <source> <pluralform>Copy the following item to another folder?</pluralform> <pluralform>Copy the following %x items to another folder?</pluralform> diff --git a/FreeFileSync/Build/Languages/hebrew.lng b/FreeFileSync/Build/Languages/hebrew.lng index f5e9ad7b..146e71ca 100755 --- a/FreeFileSync/Build/Languages/hebrew.lng +++ b/FreeFileSync/Build/Languages/hebrew.lng @@ -1283,7 +1283,7 @@ This guarantees a consistent state even in case of a serious error. <target>במידה ו-FreeFileSync מוצאת חן בעינכם:</target> <source>Donate with PayPal</source> -<target>תרום עם פייפל</target> +<target>תרום עם פייפאל</target> <source>Feedback and suggestions are welcome</source> <target>משוב והצעות יתקבלו בברכה</target> diff --git a/FreeFileSync/Source/RealTimeSync/tray_menu.cpp b/FreeFileSync/Source/RealTimeSync/tray_menu.cpp index 9356c4f1..735ffadc 100755 --- a/FreeFileSync/Source/RealTimeSync/tray_menu.cpp +++ b/FreeFileSync/Source/RealTimeSync/tray_menu.cpp @@ -310,8 +310,9 @@ rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& conf switch (showConfirmationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg(). setDetailInstructions(msg), _("&Retry"))) { - case ConfirmationButton::DO_IT: //retry + case ConfirmationButton::ACCEPT: //retry return; + case ConfirmationButton::CANCEL: throw AbortMonitoring(SHOW_GUI); } diff --git a/FreeFileSync/Source/file_hierarchy.h b/FreeFileSync/Source/file_hierarchy.h index a691bdb4..4d12259d 100755 --- a/FreeFileSync/Source/file_hierarchy.h +++ b/FreeFileSync/Source/file_hierarchy.h @@ -410,11 +410,11 @@ class FileSystemObject : public ObjectMgr<FileSystemObject>, public virtual Path public: virtual void accept(FSObjectVisitor& visitor) const = 0; - Zstring getPairItemName () const; //like getItemName() but without bias to which side is returned + Zstring getPairItemName() const; //like getItemName() but without bias to which side is returned bool isPairEmpty() const; //true, if both sides are empty //path getters always return valid values, even if isEmpty<side>()! - template <SelectedSide side> const Zstring& getItemName() const; //case sensitive! + template <SelectedSide side> Zstring getItemName() const; //case sensitive! template <SelectedSide side> bool isEmpty() const; //comparison result @@ -779,7 +779,7 @@ bool FileSystemObject::isPairEmpty() const template <SelectedSide side> inline -const Zstring& FileSystemObject::getItemName() const +Zstring FileSystemObject::getItemName() const { assert(!itemNameL_.empty() || !itemNameR_.empty()); diff --git a/FreeFileSync/Source/fs/abstract.h b/FreeFileSync/Source/fs/abstract.h index 02243a7a..a343fbe9 100755 --- a/FreeFileSync/Source/fs/abstract.h +++ b/FreeFileSync/Source/fs/abstract.h @@ -226,7 +226,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t }; //- client needs to handle duplicate file reports! (FilePlusTraverser fallback, retrying to read directory contents, ...) - static void traverseFolder(const AbstractPath& ap, TraverserCallback& sink) { ap.afs->traverseFolder(ap.afsPath, sink); } + static void traverseFolder(const AbstractPath& ap, TraverserCallback& sink /*throw X*/) { ap.afs->traverseFolder(ap.afsPath, sink); } //throw X //---------------------------------------------------------------------------------------------------------------- static bool supportPermissionCopy(const AbstractPath& apSource, const AbstractPath& apTarget); //throw FileError @@ -346,7 +346,7 @@ private: const uint64_t* streamSize, //optional const IOCallback& notifyUnbufferedIO) const = 0; // //---------------------------------------------------------------------------------------------------------------- - virtual void traverseFolder(const AfsPath& afsPath, TraverserCallback& sink) const = 0; //noexcept + virtual void traverseFolder(const AfsPath& afsPath, TraverserCallback& sink /*throw X*/) const = 0; //throw X //---------------------------------------------------------------------------------------------------------------- virtual bool supportsPermissions(const AfsPath& afsPath) const = 0; //throw FileError @@ -383,7 +383,7 @@ private: //implement "retry" in a generic way: template <class Command> inline //function object expecting to throw FileError if operation fails -bool tryReportingDirError(Command cmd, AbstractFileSystem::TraverserCallback& callback) //return "true" on success, "false" if error was ignored +bool tryReportingDirError(Command cmd, AbstractFileSystem::TraverserCallback& callback) //throw X, return "true" on success, "false" if error was ignored { for (size_t retryNumber = 0;; ++retryNumber) try @@ -405,7 +405,7 @@ bool tryReportingDirError(Command cmd, AbstractFileSystem::TraverserCallback& ca template <class Command> inline //function object expecting to throw FileError if operation fails -bool tryReportingItemError(Command cmd, AbstractFileSystem::TraverserCallback& callback, const Zstring& itemName) //return "true" on success, "false" if error was ignored +bool tryReportingItemError(Command cmd, AbstractFileSystem::TraverserCallback& callback, const Zstring& itemName) //throw X, return "true" on success, "false" if error was ignored { for (size_t retryNumber = 0;; ++retryNumber) try @@ -482,12 +482,12 @@ AbstractFileSystem::OutputStream::OutputStream(std::unique_ptr<OutputStreamImpl> inline AbstractFileSystem::OutputStream::~OutputStream() { - //we delete the file on errors: => must fail if already existing BEFORE creating OutputStream instance!! + //we delete the file on errors: => file should not have existed prior to creating OutputStream instance!! outStream_.reset(); //close file handle *before* remove! if (!finalizeSucceeded_) //transactional output stream! => clean up! try { AbstractFileSystem::removeFilePlain(filePath_); /*throw FileError*/ } - catch (FileError& e) { (void)e; assert(false); } + catch (FileError& e) { (void)e; } } diff --git a/FreeFileSync/Source/fs/native.cpp b/FreeFileSync/Source/fs/native.cpp index e4e04ac2..dfec9180 100755 --- a/FreeFileSync/Source/fs/native.cpp +++ b/FreeFileSync/Source/fs/native.cpp @@ -232,9 +232,9 @@ private: } //---------------------------------------------------------------------------------------------------------------- - void traverseFolder(const AfsPath& afsPath, TraverserCallback& sink) const override //noexcept + void traverseFolder(const AfsPath& afsPath, TraverserCallback& sink /*throw X*/) const override //throw X { - DirTraverser::execute(getNativePath(afsPath), sink); //noexcept + DirTraverser::execute(getNativePath(afsPath), sink); //throw X } //---------------------------------------------------------------------------------------------------------------- diff --git a/FreeFileSync/Source/fs/native_traverser_impl.h b/FreeFileSync/Source/fs/native_traverser_impl.h index 49a16a8e..fe37a729 100755 --- a/FreeFileSync/Source/fs/native_traverser_impl.h +++ b/FreeFileSync/Source/fs/native_traverser_impl.h @@ -61,7 +61,7 @@ private: void traverse(const Zstring& dirPath, AFS::TraverserCallback& sink) //throw X { - tryReportingDirError([&] + tryReportingDirError([&] //throw X { traverseWithException(dirPath, sink); //throw FileError, X }, sink); @@ -101,7 +101,7 @@ private: const Zstring& itemPath = appendSeparator(dirPath) + itemName; struct ::stat statData = {}; - if (!tryReportingItemError([&] + if (!tryReportingItemError([&] //throw X { if (::lstat(itemPath.c_str(), &statData) != 0) //lstat() does not resolve symlinks THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"lstat"); @@ -119,7 +119,7 @@ private: //try to resolve symlink (and report error on failure!!!) struct ::stat statDataTrg = {}; - const bool validLink = tryReportingItemError([&] + const bool validLink = tryReportingItemError([&] //throw X { if (::stat(itemPath.c_str(), &statDataTrg) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(itemPath)), L"stat"); @@ -130,7 +130,7 @@ private: if (S_ISDIR(statDataTrg.st_mode)) //a directory { if (std::unique_ptr<AFS::TraverserCallback> trav = sink.onFolder({ itemName, &linkInfo })) //throw X - traverse(itemPath, *trav); + traverse(itemPath, *trav); //throw X } else //a file or named pipe, ect. { @@ -149,7 +149,7 @@ private: else if (S_ISDIR(statData.st_mode)) //a directory { if (std::unique_ptr<AFS::TraverserCallback> trav = sink.onFolder({ itemName, nullptr })) //throw X - traverse(itemPath, *trav); + traverse(itemPath, *trav); //throw X } else //a file or named pipe, ect. { diff --git a/FreeFileSync/Source/lib/dir_lock.cpp b/FreeFileSync/Source/lib/dir_lock.cpp index 145b80d6..0419a36e 100755 --- a/FreeFileSync/Source/lib/dir_lock.cpp +++ b/FreeFileSync/Source/lib/dir_lock.cpp @@ -45,6 +45,8 @@ public: void operator()() const //throw ThreadInterruption { + setCurrentThreadName("Folder Lock Life Signs"); + try { for (;;) diff --git a/FreeFileSync/Source/lib/icon_buffer.cpp b/FreeFileSync/Source/lib/icon_buffer.cpp index 4c07ed9a..792e5b43 100755 --- a/FreeFileSync/Source/lib/icon_buffer.cpp +++ b/FreeFileSync/Source/lib/icon_buffer.cpp @@ -76,30 +76,18 @@ ImageHolder getDisplayIcon(const AbstractPath& itemPath, IconBuffer::IconSize sz class WorkLoad { public: - //context of worker thread, blocking: - AbstractPath extractNextFile() //throw ThreadInterruption - { - assert(std::this_thread::get_id() != mainThreadId); - std::unique_lock<std::mutex> dummy(lockFiles); - - interruptibleWait(conditionNewWork, dummy, [this] { return !workLoad.empty(); }); //throw ThreadInterruption - - AbstractPath filePath = workLoad.back(); // - workLoad.pop_back(); //yes, no strong exception guarantee (std::bad_alloc) - return filePath; // - } - - void setWorkload(const std::vector<AbstractPath>& newLoad) //context of main thread + //context of main thread + void setWorkload(const std::vector<AbstractPath>& newLoad) { assert(std::this_thread::get_id() == mainThreadId); { - std::lock_guard<std::mutex> dummy(lockFiles); + std::lock_guard<std::mutex> dummy(lockFiles_); - workLoad.clear(); + workLoad_.clear(); for (const AbstractPath& filePath : newLoad) - workLoad.emplace_back(filePath); + workLoad_.emplace_back(filePath); } - conditionNewWork.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 + conditionNewWork_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796 //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref } @@ -107,17 +95,30 @@ public: { assert(std::this_thread::get_id() == mainThreadId); { - std::lock_guard<std::mutex> dummy(lockFiles); - workLoad.emplace_back(filePath); //set as next item to retrieve + std::lock_guard<std::mutex> dummy(lockFiles_); + workLoad_.emplace_back(filePath); //set as next item to retrieve } - conditionNewWork.notify_all(); + conditionNewWork_.notify_all(); + } + + //context of worker thread, blocking: + AbstractPath extractNextFile() //throw ThreadInterruption + { + assert(std::this_thread::get_id() != mainThreadId); + std::unique_lock<std::mutex> dummy(lockFiles_); + + interruptibleWait(conditionNewWork_, dummy, [this] { return !workLoad_.empty(); }); //throw ThreadInterruption + + AbstractPath filePath = workLoad_.back(); // + workLoad_.pop_back(); //yes, no strong exception guarantee (std::bad_alloc) + return filePath; // } private: //AbstractPath is thread-safe like an int! - std::vector<AbstractPath> workLoad; //processes last elements of vector first! - std::mutex lockFiles; - std::condition_variable conditionNewWork; //signal event: data for processing available + std::vector<AbstractPath> workLoad_; //processes last elements of vector first! + std::mutex lockFiles_; + std::condition_variable conditionNewWork_; //signal event: data for processing available }; @@ -127,7 +128,7 @@ public: //called by main and worker thread: bool hasIcon(const AbstractPath& filePath) const { - std::lock_guard<std::mutex> dummy(lockIconList); + std::lock_guard<std::mutex> dummy(lockIconList_); return iconList.find(filePath) != iconList.end(); } @@ -135,7 +136,7 @@ public: Opt<wxBitmap> retrieve(const AbstractPath& filePath) { assert(std::this_thread::get_id() == mainThreadId); - std::lock_guard<std::mutex> dummy(lockIconList); + std::lock_guard<std::mutex> dummy(lockIconList_); auto it = iconList.find(filePath); if (it == iconList.end()) @@ -155,7 +156,7 @@ public: //called by main and worker thread: void insert(const AbstractPath& filePath, ImageHolder&& icon) { - std::lock_guard<std::mutex> dummy(lockIconList); + std::lock_guard<std::mutex> dummy(lockIconList_); //thread safety: moving ImageHolder is free from side effects, but ~wxBitmap() is NOT! => do NOT delete items from iconList here! auto rc = iconList.emplace(filePath, makeValueObject()); @@ -172,11 +173,11 @@ public: void limitSize() { assert(std::this_thread::get_id() == mainThreadId); - std::lock_guard<std::mutex> dummy(lockIconList); + std::lock_guard<std::mutex> dummy(lockIconList_); while (iconList.size() > BUFFER_SIZE_MAX) { - auto itDelPos = firstInsertPos; + auto itDelPos = firstInsertPos_; priorityListPopFront(); iconList.erase(itDelPos); //remove oldest element } @@ -198,30 +199,30 @@ private: //call while holding lock: void priorityListPopFront() { - assert(firstInsertPos!= iconList.end()); - firstInsertPos = refData(firstInsertPos).next_; + assert(firstInsertPos_!= iconList.end()); + firstInsertPos_ = refData(firstInsertPos_).next_; - if (firstInsertPos != iconList.end()) - refData(firstInsertPos).prev_ = iconList.end(); + if (firstInsertPos_ != iconList.end()) + refData(firstInsertPos_).prev_ = iconList.end(); else //priority list size > BUFFER_SIZE_MAX in this context, but still for completeness: - lastInsertPos = iconList.end(); + lastInsertPos_ = iconList.end(); } //call while holding lock: void priorityListPushBack(FileIconMap::iterator it) { - if (lastInsertPos == iconList.end()) + if (lastInsertPos_ == iconList.end()) { - assert(firstInsertPos == iconList.end()); - firstInsertPos = lastInsertPos = it; + assert(firstInsertPos_ == iconList.end()); + firstInsertPos_ = lastInsertPos_ = it; refData(it).prev_ = refData(it).next_ = iconList.end(); } else { refData(it).next_ = iconList.end(); - refData(it).prev_ = lastInsertPos; - refData(lastInsertPos).next_ = it; - lastInsertPos = it; + refData(it).prev_ = lastInsertPos_; + refData(lastInsertPos_).next_ = it; + lastInsertPos_ = it; } } @@ -238,7 +239,7 @@ private: } else { - assert(it == firstInsertPos); + assert(it == firstInsertPos_); priorityListPopFront(); } priorityListPushBack(it); @@ -246,9 +247,9 @@ private: else { if (refData(it).prev_ != iconList.end()) - assert(it == lastInsertPos); //nothing to do + assert(it == lastInsertPos_); //nothing to do else - assert(iconList.size() == 1 && it == firstInsertPos && it == lastInsertPos); //nothing to do + assert(iconList.size() == 1 && it == firstInsertPos_ && it == lastInsertPos_); //nothing to do } } @@ -269,10 +270,10 @@ private: FileIconMap::iterator next_; // }; - mutable std::mutex lockIconList; + mutable std::mutex lockIconList_; FileIconMap iconList; //shared resource; Zstring is thread-safe like an int - FileIconMap::iterator firstInsertPos = iconList.end(); - FileIconMap::iterator lastInsertPos = iconList.end(); + FileIconMap::iterator firstInsertPos_ = iconList.end(); + FileIconMap::iterator lastInsertPos_ = iconList.end(); }; //################################################################################################################################################ @@ -300,9 +301,11 @@ private: void WorkerThread::operator()() const //thread entry { + setCurrentThreadName("Icon Buffer Worker"); for (;;) { interruptionPoint(); //throw ThreadInterruption + //needed? extractNextFile() is already interruptible... //start work: blocks until next icon to load is retrieved: const AbstractPath itemPath = workload_->extractNextFile(); //throw ThreadInterruption @@ -335,7 +338,7 @@ IconBuffer::IconBuffer(IconSize sz) : pimpl(std::make_unique<Impl>()), iconSizeT IconBuffer::~IconBuffer() { - setWorkload({}); //make sure interruption point is always reached! + setWorkload({}); //make sure interruption point is always reached! //needed??? pimpl->worker.interrupt(); pimpl->worker.join(); } diff --git a/FreeFileSync/Source/lib/parallel_scan.cpp b/FreeFileSync/Source/lib/parallel_scan.cpp index 838c4497..b9e80453 100755 --- a/FreeFileSync/Source/lib/parallel_scan.cpp +++ b/FreeFileSync/Source/lib/parallel_scan.cpp @@ -182,7 +182,8 @@ public: return rv; } - void processErrors(FillBufferCallback& callback) //context of main thread, call repreatedly + //context of main thread, call repreatedly + void processErrors(FillBufferCallback& callback) { std::unique_lock<std::mutex> dummy(lockErrorInfo_); if (errorInfo_.get() && !errorResponse_.get()) @@ -391,7 +392,8 @@ std::unique_ptr<AFS::TraverserCallback> DirCallback::onFolder(const FolderInfo& //------------------------------------------------------------------------------------ if (level_ > 100) //Win32 traverser: stack overflow approximately at level 1000 - if (!tryReportingItemError([&] //check after FolderContainer::addSubFolder() + //check after FolderContainer::addSubFolder() + if (!tryReportingItemError([&] //throw ThreadInterruption { throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", AFS::getDisplayPath(AFS::appendRelPath(cfg.baseFolderPath_, folderRelPath))), L"Endless recursion."); }, *this, fi.itemName)) @@ -480,38 +482,39 @@ class WorkerThread public: WorkerThread(int threadID, const std::shared_ptr<AsyncCallback>& acb, - const AbstractPath& baseFolderPath, //always bound! + const AbstractPath& baseFolderPath, //always bound! const HardFilter::FilterRef& filter, // SymLinkHandling handleSymlinks, DirectoryValue& dirOutput) : acb_(acb), - outputContainer(dirOutput.folderCont), - travCfg(threadID, - baseFolderPath, - filter, - handleSymlinks, //shared by all(!) instances of DirCallback while traversing a folder hierarchy - dirOutput.failedFolderReads, - dirOutput.failedItemReads, - *acb_) {} + outputContainer_(dirOutput.folderCont), + travCfg_(threadID, + baseFolderPath, + filter, + handleSymlinks, //shared by all(!) instances of DirCallback while traversing a folder hierarchy + dirOutput.failedFolderReads, + dirOutput.failedItemReads, + *acb_) {} void operator()() //thread entry { + setCurrentThreadName("Folder Traverser"); acb_->incActiveWorker(); ZEN_ON_SCOPE_EXIT(acb_->decActiveWorker()); - if (acb_->mayReportCurrentFile(travCfg.threadID_, travCfg.lastReportTime_)) - acb_->reportCurrentFile(AFS::getDisplayPath(travCfg.baseFolderPath_)); //just in case first directory access is blocking + if (acb_->mayReportCurrentFile(travCfg_.threadID_, travCfg_.lastReportTime_)) + acb_->reportCurrentFile(AFS::getDisplayPath(travCfg_.baseFolderPath_)); //just in case first directory access is blocking - DirCallback cb(travCfg, Zstring(), outputContainer, 0); + DirCallback cb(travCfg_, Zstring(), outputContainer_, 0); - AFS::traverseFolder(travCfg.baseFolderPath_, cb); //throw X + AFS::traverseFolder(travCfg_.baseFolderPath_, cb); //throw ThreadInterruption } private: std::shared_ptr<AsyncCallback> acb_; - FolderContainer& outputContainer; - TraverserConfig travCfg; + FolderContainer& outputContainer_; + TraverserConfig travCfg_; }; } @@ -528,7 +531,7 @@ void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in ZEN_ON_SCOPE_FAIL ( for (InterruptibleThread& wt : worker) - wt.interrupt(); //interrupt all at once first, then join + wt.interrupt(); //interrupt all first, then join for (InterruptibleThread& wt : worker) if (wt.joinable()) //= precondition of thread::join(), which throws an exception if violated! wt.join(); //in this context it is possible a thread is *not* joinable anymore due to the thread::try_join_for() below! diff --git a/FreeFileSync/Source/lib/process_xml.cpp b/FreeFileSync/Source/lib/process_xml.cpp index 762f0064..31cf7120 100755 --- a/FreeFileSync/Source/lib/process_xml.cpp +++ b/FreeFileSync/Source/lib/process_xml.cpp @@ -825,13 +825,18 @@ void readConfig(const XmlIn& in, DirectionConfig& directCfg) { in["Variant"](directCfg.var); - XmlIn inCustDir = in["CustomDirections"]; - inCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly); - inCustDir["RightOnly" ](directCfg.custom.exRightSideOnly); - inCustDir["LeftNewer" ](directCfg.custom.leftNewer); - inCustDir["RightNewer"](directCfg.custom.rightNewer); - inCustDir["Different" ](directCfg.custom.different); - inCustDir["Conflict" ](directCfg.custom.conflict); + if (directCfg.var == DirectionConfig::CUSTOM) + { + XmlIn inCustDir = in["CustomDirections"]; + inCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly); + inCustDir["RightOnly" ](directCfg.custom.exRightSideOnly); + inCustDir["LeftNewer" ](directCfg.custom.leftNewer); + inCustDir["RightNewer"](directCfg.custom.rightNewer); + inCustDir["Different" ](directCfg.custom.different); + inCustDir["Conflict" ](directCfg.custom.conflict); + } + else + directCfg.custom = DirectionSet(); in["DetectMovedFiles"](directCfg.detectMovedFiles); } @@ -1313,13 +1318,16 @@ void writeConfig(const DirectionConfig& directCfg, XmlOut& out) { out["Variant"](directCfg.var); - XmlOut outCustDir = out["CustomDirections"]; - outCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly); - outCustDir["RightOnly" ](directCfg.custom.exRightSideOnly); - outCustDir["LeftNewer" ](directCfg.custom.leftNewer); - outCustDir["RightNewer"](directCfg.custom.rightNewer); - outCustDir["Different" ](directCfg.custom.different); - outCustDir["Conflict" ](directCfg.custom.conflict); + if (directCfg.var == DirectionConfig::CUSTOM) + { + XmlOut outCustDir = out["CustomDirections"]; + outCustDir["LeftOnly" ](directCfg.custom.exLeftSideOnly); + outCustDir["RightOnly" ](directCfg.custom.exRightSideOnly); + outCustDir["LeftNewer" ](directCfg.custom.leftNewer); + outCustDir["RightNewer"](directCfg.custom.rightNewer); + outCustDir["Different" ](directCfg.custom.different); + outCustDir["Conflict" ](directCfg.custom.conflict); + } out["DetectMovedFiles"](directCfg.detectMovedFiles); } diff --git a/FreeFileSync/Source/lib/versioning.cpp b/FreeFileSync/Source/lib/versioning.cpp index 88b3f551..2cda977f 100755 --- a/FreeFileSync/Source/lib/versioning.cpp +++ b/FreeFileSync/Source/lib/versioning.cpp @@ -264,7 +264,7 @@ void FileVersioner::revisionFolderImpl(const AbstractPath& folderPath, const Zst //create target directories only when needed in moveFileToVersioning(): avoid empty directories! FlatTraverserCallback ft(folderPath); //traverse source directory one level deep - AFS::traverseFolder(folderPath, ft); + AFS::traverseFolder(folderPath, ft); //throw FileError const Zstring relPathPf = appendSeparator(relativePath); diff --git a/FreeFileSync/Source/structures.cpp b/FreeFileSync/Source/structures.cpp index 04e46a4d..11dafb3e 100755 --- a/FreeFileSync/Source/structures.cpp +++ b/FreeFileSync/Source/structures.cpp @@ -69,14 +69,20 @@ std::wstring zen::getVariantName(CompareVariant var) std::wstring zen::getVariantName(DirectionConfig::Variant var) { + //const wchar_t arrowLeft [] = L"\u2190"; + //const wchar_t arrowRight[] = L"\u2192"; unicode arrows -> too small + const wchar_t arrowLeft [] = L"\uFF1C\u2013"; //fullwidth less-than + en dash + const wchar_t arrowRight[] = L"\u2013\uFF1E"; //en dash + fullwidth greater-than + const wchar_t angleRight[] = L"\uFF1E"; + switch (var) { case DirectionConfig::TWO_WAY: - return L"<- " + _("Two way") + L" ->"; + return std::wstring(arrowLeft) + L" " + _("Two way") + L" " + arrowRight; case DirectionConfig::MIRROR: - return _("Mirror") + L" ->"; + return _("Mirror") + L" " + arrowRight; case DirectionConfig::UPDATE: - return _("Update") + L" >"; + return _("Update") + L" " + angleRight; case DirectionConfig::CUSTOM: return _("Custom"); } diff --git a/FreeFileSync/Source/structures.h b/FreeFileSync/Source/structures.h index 562b4902..91b7df1c 100755 --- a/FreeFileSync/Source/structures.h +++ b/FreeFileSync/Source/structures.h @@ -140,15 +140,6 @@ struct DirectionConfig //technical representation of sync-config bool detectMovedFiles = false; //dependent from Variant: e.g. always active for DirectionConfig::TWO_WAY! => use functions below for evaluation! }; -inline -bool operator==(const DirectionConfig& lhs, const DirectionConfig& rhs) -{ - return lhs.var == rhs.var && - lhs.custom == rhs.custom && - lhs.detectMovedFiles == rhs.detectMovedFiles; - //adapt effectivelyEqual() on changes, too! -} - bool detectMovedFilesSelectable(const DirectionConfig& cfg); bool detectMovedFilesEnabled (const DirectionConfig& cfg); @@ -157,6 +148,15 @@ DirectionSet extractDirections(const DirectionConfig& cfg); //get sync direction std::wstring getVariantName(DirectionConfig::Variant var); inline +bool operator==(const DirectionConfig& lhs, const DirectionConfig& rhs) +{ + return lhs.var == rhs.var && + (lhs.var != DirectionConfig::CUSTOM || lhs.custom == rhs.custom) && //no need to consider custom directions if var != CUSTOM + lhs.detectMovedFiles == rhs.detectMovedFiles; //useful to remember this setting even if the current sync variant does not need it + //adapt effectivelyEqual() on changes, too! +} + +inline bool effectivelyEqual(const DirectionConfig& lhs, const DirectionConfig& rhs) { return (lhs.var == DirectionConfig::TWO_WAY) == (rhs.var == DirectionConfig::TWO_WAY) && //either both two-way or none diff --git a/FreeFileSync/Source/synchronization.cpp b/FreeFileSync/Source/synchronization.cpp index 9e0f53b8..a22903e1 100755 --- a/FreeFileSync/Source/synchronization.cpp +++ b/FreeFileSync/Source/synchronization.cpp @@ -1627,7 +1627,7 @@ void verifyFiles(const AbstractPath& sourcePath, const AbstractPath& targetPath, // => it seems OS buffered are not invalidated by this: snake oil??? if (Opt<Zstring> nativeTargetPath = AFS::getNativeItemPath(targetPath)) { - const int fileHandle = ::open(nativeTargetPath->c_str(), O_WRONLY); + const int fileHandle = ::open(nativeTargetPath->c_str(), O_WRONLY | O_APPEND); if (fileHandle == -1) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(*nativeTargetPath)), L"open"); ZEN_ON_SCOPE_EXIT(::close(fileHandle)); diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp index 4fa9a855..ba9ae644 100755 --- a/FreeFileSync/Source/ui/batch_status_handler.cpp +++ b/FreeFileSync/Source/ui/batch_status_handler.cpp @@ -360,22 +360,22 @@ void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool& forceUiRefresh(); bool dontWarnAgain = false; - switch (showConfirmationDialog3(progressDlg_->getWindowIfVisible(), DialogInfoType::WARNING, PopupDialogCfg3(). - setDetailInstructions(warningMessage + L"\n\n" + _("You can switch to FreeFileSync's main window to resolve this issue.")). - setCheckBox(dontWarnAgain, _("&Don't show this warning again"), ConfirmationButton3::DONT_DO_IT), - _("&Ignore"), _("&Switch"))) + switch (showQuestionDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::WARNING, PopupDialogCfg(). + setDetailInstructions(warningMessage + L"\n\n" + _("You can switch to FreeFileSync's main window to resolve this issue.")). + setCheckBox(dontWarnAgain, _("&Don't show this warning again"), QuestionButton2::NO), + _("&Ignore"), _("&Switch"))) { - case ConfirmationButton3::DO_IT: //ignore + case QuestionButton2::YES: //ignore warningActive = !dontWarnAgain; break; - case ConfirmationButton3::DONT_DO_IT: //switch + case QuestionButton2::NO: //switch errorLog_.logMsg(_("Switching to FreeFileSync's main window"), TYPE_INFO); switchToGuiRequested_ = true; //treat as a special kind of cancel requestAbortion(); // throw BatchRequestSwitchToMainDialog(); - case ConfirmationButton3::CANCEL: + case QuestionButton2::CANCEL: abortProcessNow(); break; } @@ -422,18 +422,18 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er PauseTimers dummy(*progressDlg_); forceUiRefresh(); - bool ignoreNextErrors = false; - switch (showConfirmationDialog3(progressDlg_->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg3(). - setDetailInstructions(errorMessage). - setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT), - _("&Ignore"), _("&Retry"))) + switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg(). + setDetailInstructions(errorMessage), + _("&Ignore"), _("Ignore &all"), _("&Retry"))) { - case ConfirmationButton3::DO_IT: //ignore - if (ignoreNextErrors) //falsify only - handleError_ = xmlAccess::ON_ERROR_IGNORE; + case ConfirmationButton3::ACCEPT: //ignore + return ProcessCallback::IGNORE_ERROR; + + case ConfirmationButton3::ACCEPT_ALL: //ignore all + handleError_ = xmlAccess::ON_ERROR_IGNORE; return ProcessCallback::IGNORE_ERROR; - case ConfirmationButton3::DONT_DO_IT: //retry + case ConfirmationButton3::DECLINE: //retry guardWriteLog.dismiss(); errorLog_.logMsg(errorMessage + L"\n-> " + _("Retrying operation..."), TYPE_INFO); return ProcessCallback::RETRY; @@ -470,18 +470,19 @@ void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage) PauseTimers dummy(*progressDlg_); forceUiRefresh(); - bool ignoreNextErrors = false; switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg().setTitle(_("Serious Error")). - setDetailInstructions(errorMessage). - setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors")), - _("&Ignore"))) + setDetailInstructions(errorMessage), + _("&Ignore"), _("Ignore &all"))) { - case ConfirmationButton::DO_IT: - if (ignoreNextErrors) //falsify only - handleError_ = xmlAccess::ON_ERROR_IGNORE; + case ConfirmationButton2::ACCEPT: break; - case ConfirmationButton::CANCEL: + + case ConfirmationButton2::ACCEPT_ALL: + handleError_ = xmlAccess::ON_ERROR_IGNORE; + break; + + case ConfirmationButton2::CANCEL: abortProcessNow(); break; } diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp index 8e43d0cb..b6007636 100755 --- a/FreeFileSync/Source/ui/gui_generated.cpp +++ b/FreeFileSync/Source/ui/gui_generated.cpp @@ -2317,7 +2317,7 @@ CloudSetupDlgGenerated::CloudSetupDlgGenerated( wxWindow* parent, wxWindowID id, m_panel41->SetSizer( bSizer185 ); m_panel41->Layout(); bSizer185->Fit( m_panel41 ); - bSizer134->Add( m_panel41, 1, wxEXPAND, 5 ); + bSizer134->Add( m_panel41, 0, wxEXPAND, 5 ); bSizerSftpTweaks = new wxBoxSizer( wxVERTICAL ); @@ -2327,8 +2327,8 @@ CloudSetupDlgGenerated::CloudSetupDlgGenerated( wxWindow* parent, wxWindowID id, wxBoxSizer* bSizer219; bSizer219 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapSpeed = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizer219->Add( m_bitmapSpeed, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); + m_bitmapSpeedSftp = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer219->Add( m_bitmapSpeedSftp, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); m_staticText1361 = new wxStaticText( this, wxID_ANY, _("Performance improvements:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText1361->Wrap( -1 ); @@ -2358,34 +2358,28 @@ CloudSetupDlgGenerated::CloudSetupDlgGenerated( wxWindow* parent, wxWindowID id, fgSizer1611->SetFlexibleDirection( wxBOTH ); fgSizer1611->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - m_staticText12341 = new wxStaticText( m_panel411, wxID_ANY, _("SSH connections for directory reading:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText12341 = new wxStaticText( m_panel411, wxID_ANY, _("Connections for directory reading:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText12341->Wrap( -1 ); fgSizer1611->Add( m_staticText12341, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 ); - m_spinCtrlConnectionCount = new wxSpinCtrl( m_panel411, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); - fgSizer1611->Add( m_spinCtrlConnectionCount, 0, wxALL, 5 ); - - wxBoxSizer* bSizer230; - bSizer230 = new wxBoxSizer( wxHORIZONTAL ); + m_spinCtrlConnectionCountSftp = new wxSpinCtrl( m_panel411, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); + fgSizer1611->Add( m_spinCtrlConnectionCountSftp, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); m_staticText138111 = new wxStaticText( m_panel411, wxID_ANY, _("Suggested range: [1 - 10]"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText138111->Wrap( -1 ); m_staticText138111->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - bSizer230->Add( m_staticText138111, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - - fgSizer1611->Add( bSizer230, 0, 0, 5 ); + fgSizer1611->Add( m_staticText138111, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); m_staticText1231111 = new wxStaticText( m_panel411, wxID_ANY, _("SFTP channels per connection:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText1231111->Wrap( -1 ); fgSizer1611->Add( m_staticText1231111, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxBOTTOM|wxLEFT, 5 ); - m_spinCtrlChannelCount = new wxSpinCtrl( m_panel411, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); - fgSizer1611->Add( m_spinCtrlChannelCount, 0, wxALL, 5 ); + m_spinCtrlChannelCountSftp = new wxSpinCtrl( m_panel411, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); + fgSizer1611->Add( m_spinCtrlChannelCountSftp, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); m_button42 = new wxButton( m_panel411, wxID_ANY, _("Detect server limit"), wxDefaultPosition, wxDefaultSize, 0 ); - fgSizer1611->Add( m_button42, 0, wxALL, 5 ); + fgSizer1611->Add( m_button42, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); bSizer1851->Add( fgSizer1611, 0, wxALL, 5 ); @@ -2415,7 +2409,89 @@ CloudSetupDlgGenerated::CloudSetupDlgGenerated( wxWindow* parent, wxWindowID id, bSizerSftpTweaks->Add( m_panel411, 1, wxEXPAND, 5 ); - bSizer134->Add( bSizerSftpTweaks, 0, wxEXPAND, 5 ); + bSizer134->Add( bSizerSftpTweaks, 1, wxEXPAND, 5 ); + + bSizerFtpTweaks = new wxBoxSizer( wxVERTICAL ); + + m_staticline5711 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerFtpTweaks->Add( m_staticline5711, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer2191; + bSizer2191 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapSpeedFtp = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer2191->Add( m_bitmapSpeedFtp, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); + + m_staticText13611 = new wxStaticText( this, wxID_ANY, _("Performance improvements:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText13611->Wrap( -1 ); + bSizer2191->Add( m_staticText13611, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); + + + bSizer2191->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_hyperlink1711 = new wxHyperlinkCtrl( this, wxID_ANY, _("How to get best performance?"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + bSizer2191->Add( m_hyperlink1711, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); + + + bSizerFtpTweaks->Add( bSizer2191, 0, wxEXPAND, 5 ); + + m_staticline573 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerFtpTweaks->Add( m_staticline573, 0, wxEXPAND, 5 ); + + m_panel4111 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_panel4111->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer18511; + bSizer18511 = new wxBoxSizer( wxVERTICAL ); + + wxFlexGridSizer* fgSizer16111; + fgSizer16111 = new wxFlexGridSizer( 0, 3, 0, 0 ); + fgSizer16111->AddGrowableCol( 1 ); + fgSizer16111->SetFlexibleDirection( wxBOTH ); + fgSizer16111->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_staticText123411 = new wxStaticText( m_panel4111, wxID_ANY, _("Connections for directory reading:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText123411->Wrap( -1 ); + fgSizer16111->Add( m_staticText123411, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 ); + + m_spinCtrlConnectionCountFtp = new wxSpinCtrl( m_panel4111, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); + fgSizer16111->Add( m_spinCtrlConnectionCountFtp, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText1381111 = new wxStaticText( m_panel4111, wxID_ANY, _("Suggested range: [1 - 10]"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText1381111->Wrap( -1 ); + m_staticText1381111->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + fgSizer16111->Add( m_staticText1381111, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer18511->Add( fgSizer16111, 0, wxALL, 5 ); + + wxBoxSizer* bSizer2201; + bSizer2201 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText1381121 = new wxStaticText( m_panel4111, wxID_ANY, _("Example:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText1381121->Wrap( -1 ); + m_staticText1381121->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizer2201->Add( m_staticText1381121, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText138113 = new wxStaticText( m_panel4111, wxID_ANY, _("4 connections = 4 times faster directory reading"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText138113->Wrap( -1 ); + m_staticText138113->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + bSizer2201->Add( m_staticText138113, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer18511->Add( bSizer2201, 0, wxBOTTOM|wxRIGHT|wxLEFT, 10 ); + + + m_panel4111->SetSizer( bSizer18511 ); + m_panel4111->Layout(); + bSizer18511->Fit( m_panel4111 ); + bSizerFtpTweaks->Add( m_panel4111, 1, wxEXPAND, 5 ); + + + bSizer134->Add( bSizerFtpTweaks, 1, wxEXPAND, 5 ); m_staticline12 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bSizer134->Add( m_staticline12, 0, wxEXPAND, 5 ); @@ -2453,6 +2529,7 @@ CloudSetupDlgGenerated::CloudSetupDlgGenerated( wxWindow* parent, wxWindowID id, m_buttonSelectFolder->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CloudSetupDlgGenerated::OnBrowseCloudFolder ), NULL, this ); m_hyperlink171->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( CloudSetupDlgGenerated::OnHelpSftpPerformance ), NULL, this ); m_button42->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CloudSetupDlgGenerated::OnDetectServerChannelLimit ), NULL, this ); + m_hyperlink1711->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( CloudSetupDlgGenerated::OnHelpSftpPerformance ), NULL, this ); m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CloudSetupDlgGenerated::OnOkay ), NULL, this ); m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CloudSetupDlgGenerated::OnCancel ), NULL, this ); } @@ -2946,15 +3023,18 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind m_panelProgress->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); wxBoxSizer* bSizer173; - bSizer173 = new wxBoxSizer( wxVERTICAL ); + bSizer173 = new wxBoxSizer( wxHORIZONTAL ); - bSizer171 = new wxBoxSizer( wxHORIZONTAL ); + wxBoxSizer* bSizer161; + bSizer161 = new wxBoxSizer( wxVERTICAL ); + m_panelGraphBytes = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), 0 ); + m_panelGraphBytes->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - bSizer171->Add( 10, 0, 0, 0, 5 ); + bSizer161->Add( m_panelGraphBytes, 1, wxEXPAND, 15 ); - wxBoxSizer* bSizer164; - bSizer164 = new wxBoxSizer( wxVERTICAL ); + wxBoxSizer* bSizer232; + bSizer232 = new wxBoxSizer( wxHORIZONTAL ); m_panelItemsProcessed = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); m_panelItemsProcessed->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); @@ -2993,7 +3073,7 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind m_panelItemsProcessed->SetSizer( bSizer165 ); m_panelItemsProcessed->Layout(); bSizer165->Fit( m_panelItemsProcessed ); - bSizer164->Add( m_panelItemsProcessed, 0, wxEXPAND|wxTOP, 7 ); + bSizer232->Add( m_panelItemsProcessed, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 10 ); m_panelItemsRemaining = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); m_panelItemsRemaining->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); @@ -3032,7 +3112,7 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind m_panelItemsRemaining->SetSizer( bSizer166 ); m_panelItemsRemaining->Layout(); bSizer166->Fit( m_panelItemsRemaining ); - bSizer164->Add( m_panelItemsRemaining, 0, wxTOP|wxEXPAND, 7 ); + bSizer232->Add( m_panelItemsRemaining, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 10 ); m_panelTimeRemaining = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); m_panelTimeRemaining->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); @@ -3061,7 +3141,7 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind m_panelTimeRemaining->SetSizer( bSizer167 ); m_panelTimeRemaining->Layout(); bSizer167->Fit( m_panelTimeRemaining ); - bSizer164->Add( m_panelTimeRemaining, 0, wxTOP|wxEXPAND, 7 ); + bSizer232->Add( m_panelTimeRemaining, 0, wxLEFT|wxALIGN_CENTER_VERTICAL, 10 ); wxPanel* m_panelTimeElapsed; m_panelTimeElapsed = new wxPanel( m_panelProgress, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); @@ -3091,49 +3171,44 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind m_panelTimeElapsed->SetSizer( bSizer168 ); m_panelTimeElapsed->Layout(); bSizer168->Fit( m_panelTimeElapsed ); - bSizer164->Add( m_panelTimeElapsed, 0, wxTOP|wxEXPAND, 7 ); - - - bSizer171->Add( bSizer164, 0, wxALIGN_CENTER_VERTICAL, 5 ); - + bSizer232->Add( m_panelTimeElapsed, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 10 ); - bSizer171->Add( 10, 0, 0, 0, 5 ); - - wxBoxSizer* bSizer161; - bSizer161 = new wxBoxSizer( wxVERTICAL ); + wxBoxSizer* bSizer233; + bSizer233 = new wxBoxSizer( wxVERTICAL ); wxBoxSizer* bSizer175; bSizer175 = new wxBoxSizer( wxHORIZONTAL ); m_bitmapGraphKeyBytes = new wxStaticBitmap( m_panelProgress, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizer175->Add( m_bitmapGraphKeyBytes, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); + bSizer175->Add( m_bitmapGraphKeyBytes, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); wxStaticText* m_staticText99; m_staticText99 = new wxStaticText( m_panelProgress, wxID_ANY, _("Bytes copied:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText99->Wrap( -1 ); - bSizer175->Add( m_staticText99, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - + bSizer175->Add( m_staticText99, 0, wxALIGN_CENTER_VERTICAL, 5 ); - bSizer161->Add( bSizer175, 0, 0, 5 ); - m_panelGraphBytes = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), 0 ); - m_panelGraphBytes->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - bSizer161->Add( m_panelGraphBytes, 1, wxEXPAND, 15 ); + bSizer233->Add( bSizer175, 0, 0, 5 ); wxBoxSizer* bSizer174; bSizer174 = new wxBoxSizer( wxHORIZONTAL ); m_bitmapGraphKeyItems = new wxStaticBitmap( m_panelProgress, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizer174->Add( m_bitmapGraphKeyItems, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); + bSizer174->Add( m_bitmapGraphKeyItems, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); wxStaticText* m_staticText100; m_staticText100 = new wxStaticText( m_panelProgress, wxID_ANY, _("Items processed:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText100->Wrap( -1 ); - bSizer174->Add( m_staticText100, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + bSizer174->Add( m_staticText100, 0, wxALIGN_CENTER_VERTICAL, 5 ); - bSizer161->Add( bSizer174, 0, 0, 5 ); + bSizer233->Add( bSizer174, 0, wxTOP, 5 ); + + + bSizer232->Add( bSizer233, 0, wxLEFT|wxALIGN_CENTER_VERTICAL, 10 ); + + + bSizer161->Add( bSizer232, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP|wxBOTTOM, 10 ); m_panelGraphItems = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_panelGraphItems->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); @@ -3141,16 +3216,13 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind bSizer161->Add( m_panelGraphItems, 1, wxEXPAND, 15 ); - bSizer161->Add( 450, 0, 0, 0, 5 ); + bSizer161->Add( 550, 0, 0, 0, 5 ); - bSizer171->Add( bSizer161, 1, wxEXPAND, 5 ); + bSizer173->Add( bSizer161, 1, wxEXPAND|wxLEFT, 10 ); - bSizer171->Add( 0, 310, 0, 0, 5 ); - - - bSizer173->Add( bSizer171, 1, wxEXPAND, 5 ); + bSizer173->Add( 0, 340, 0, 0, 5 ); m_panelProgress->SetSizer( bSizer173 ); @@ -4055,9 +4127,9 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer172->Add( m_hyperlink15, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - m_hyperlink12 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Google Test"), wxT("http://code.google.com/p/googletest"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink12 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("Google Test"), wxT("https://github.com/google/googletest"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); m_hyperlink12->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink12->SetToolTip( _("http://code.google.com/p/googletest") ); + m_hyperlink12->SetToolTip( _("https://github.com/google/googletest") ); bSizer172->Add( m_hyperlink12, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); @@ -4067,12 +4139,18 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer172->Add( m_hyperlink13, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); - m_hyperlink10 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("libssh2"), wxT("http://www.libssh2.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink10 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("libssh2"), wxT("https://www.libssh2.org"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); m_hyperlink10->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_hyperlink10->SetToolTip( _("http://www.libssh2.org") ); + m_hyperlink10->SetToolTip( _("https://www.libssh2.org") ); bSizer172->Add( m_hyperlink10, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); + m_hyperlink101 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("libcurl"), wxT("https://curl.haxx.se/libcurl"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink101->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + m_hyperlink101->SetToolTip( _("https://curl.haxx.se/libcurl") ); + + bSizer172->Add( m_hyperlink101, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); + m_hyperlink18 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("NSIS"), wxT("http://nsis.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); m_hyperlink18->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); m_hyperlink18->SetToolTip( _("http://nsis.sourceforge.net") ); diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h index 99adf914..fae782c6 100755 --- a/FreeFileSync/Source/ui/gui_generated.h +++ b/FreeFileSync/Source/ui/gui_generated.h @@ -535,19 +535,31 @@ protected: wxButton* m_buttonSelectFolder; wxBoxSizer* bSizerSftpTweaks; wxStaticLine* m_staticline571; - wxStaticBitmap* m_bitmapSpeed; + wxStaticBitmap* m_bitmapSpeedSftp; wxStaticText* m_staticText1361; wxHyperlinkCtrl* m_hyperlink171; wxStaticLine* m_staticline57; wxPanel* m_panel411; wxStaticText* m_staticText12341; - wxSpinCtrl* m_spinCtrlConnectionCount; + wxSpinCtrl* m_spinCtrlConnectionCountSftp; wxStaticText* m_staticText138111; wxStaticText* m_staticText1231111; - wxSpinCtrl* m_spinCtrlChannelCount; + wxSpinCtrl* m_spinCtrlChannelCountSftp; wxButton* m_button42; wxStaticText* m_staticText138112; wxStaticText* m_staticText13811; + wxBoxSizer* bSizerFtpTweaks; + wxStaticLine* m_staticline5711; + wxStaticBitmap* m_bitmapSpeedFtp; + wxStaticText* m_staticText13611; + wxHyperlinkCtrl* m_hyperlink1711; + wxStaticLine* m_staticline573; + wxPanel* m_panel4111; + wxStaticText* m_staticText123411; + wxSpinCtrl* m_spinCtrlConnectionCountFtp; + wxStaticText* m_staticText1381111; + wxStaticText* m_staticText1381121; + wxStaticText* m_staticText138113; wxStaticLine* m_staticline12; wxBoxSizer* bSizerStdButtons; wxButton* m_buttonOkay; @@ -697,7 +709,6 @@ private: protected: wxBoxSizer* bSizer42; - wxBoxSizer* bSizer171; wxStaticText* m_staticText87; public: @@ -708,6 +719,7 @@ public: wxBoxSizer* bSizerStatusText; wxStaticText* m_staticTextStatus; wxPanel* m_panelProgress; + zen::Graph2D* m_panelGraphBytes; wxPanel* m_panelItemsProcessed; wxStaticText* m_staticTextItemsProcessed; wxStaticText* m_staticTextBytesProcessed; @@ -718,7 +730,6 @@ public: wxStaticText* m_staticTextTimeRemaining; wxStaticText* m_staticTextTimeElapsed; wxStaticBitmap* m_bitmapGraphKeyBytes; - zen::Graph2D* m_panelGraphBytes; wxStaticBitmap* m_bitmapGraphKeyItems; zen::Graph2D* m_panelGraphItems; wxNotebook* m_notebookResult; @@ -1015,6 +1026,7 @@ protected: wxHyperlinkCtrl* m_hyperlink12; wxHyperlinkCtrl* m_hyperlink13; wxHyperlinkCtrl* m_hyperlink10; + wxHyperlinkCtrl* m_hyperlink101; wxHyperlinkCtrl* m_hyperlink18; wxHyperlinkCtrl* m_hyperlink9; wxPanel* m_panelThankYou; diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp index c1f77479..19a76e88 100755 --- a/FreeFileSync/Source/ui/gui_status_handler.cpp +++ b/FreeFileSync/Source/ui/gui_status_handler.cpp @@ -20,14 +20,14 @@ using namespace zen; using namespace xmlAccess; -StatusHandlerTemporaryPanel::StatusHandlerTemporaryPanel(MainDialog& dlg) : mainDlg(dlg) +StatusHandlerTemporaryPanel::StatusHandlerTemporaryPanel(MainDialog& dlg) : mainDlg_(dlg) { { - mainDlg.compareStatus_->init(*this); //clear old values before showing panel + mainDlg_.compareStatus_->init(*this); //clear old values before showing panel //------------------------------------------------------------------ - const wxAuiPaneInfo& topPanel = mainDlg.auiMgr_.GetPane(mainDlg.m_panelTopButtons); - wxAuiPaneInfo& statusPanel = mainDlg.auiMgr_.GetPane(mainDlg.compareStatus_->getAsWindow()); + const wxAuiPaneInfo& topPanel = mainDlg_.auiMgr_.GetPane(mainDlg_.m_panelTopButtons); + wxAuiPaneInfo& statusPanel = mainDlg_.auiMgr_.GetPane(mainDlg_.compareStatus_->getAsWindow()); //determine best status panel row near top panel switch (topPanel.dock_direction) @@ -48,7 +48,7 @@ StatusHandlerTemporaryPanel::StatusHandlerTemporaryPanel(MainDialog& dlg) : main //case wxAUI_DOCK_CENTRE: } - wxAuiPaneInfoArray& paneArray = mainDlg.auiMgr_.GetAllPanes(); + wxAuiPaneInfoArray& paneArray = mainDlg_.auiMgr_.GetAllPanes(); const bool statusRowTaken = [&] { @@ -80,26 +80,26 @@ StatusHandlerTemporaryPanel::StatusHandlerTemporaryPanel(MainDialog& dlg) : main //------------------------------------------------------------------ statusPanel.Show(); - mainDlg.auiMgr_.Update(); + mainDlg_.auiMgr_.Update(); } - mainDlg.Update(); //don't wait until idle event! + mainDlg_.Update(); //don't wait until idle event! //register keys - mainDlg.Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(StatusHandlerTemporaryPanel::OnKeyPressed), nullptr, this); - mainDlg.m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusHandlerTemporaryPanel::OnAbortCompare), nullptr, this); + mainDlg_.Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(StatusHandlerTemporaryPanel::OnKeyPressed), nullptr, this); + mainDlg_.m_buttonCancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusHandlerTemporaryPanel::OnAbortCompare), nullptr, this); } StatusHandlerTemporaryPanel::~StatusHandlerTemporaryPanel() { //unregister keys - mainDlg.Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(StatusHandlerTemporaryPanel::OnKeyPressed), nullptr, this); - mainDlg.m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusHandlerTemporaryPanel::OnAbortCompare), nullptr, this); + mainDlg_.Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(StatusHandlerTemporaryPanel::OnKeyPressed), nullptr, this); + mainDlg_.m_buttonCancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusHandlerTemporaryPanel::OnAbortCompare), nullptr, this); - mainDlg.auiMgr_.GetPane(mainDlg.compareStatus_->getAsWindow()).Hide(); - mainDlg.auiMgr_.Update(); - mainDlg.compareStatus_->teardown(); + mainDlg_.auiMgr_.GetPane(mainDlg_.compareStatus_->getAsWindow()).Hide(); + mainDlg_.auiMgr_.Update(); + mainDlg_.compareStatus_->teardown(); } @@ -120,7 +120,7 @@ void StatusHandlerTemporaryPanel::initNewPhase(int itemsTotal, int64_t bytesTota { StatusHandler::initNewPhase(itemsTotal, bytesTotal, phaseID); - mainDlg.compareStatus_->initNewPhase(); //call after "StatusHandler::initNewPhase" + mainDlg_.compareStatus_->initNewPhase(); //call after "StatusHandler::initNewPhase" forceUiRefresh(); //throw ?; OS X needs a full yield to update GUI and get rid of "dummy" texts } @@ -128,7 +128,7 @@ void StatusHandlerTemporaryPanel::initNewPhase(int itemsTotal, int64_t bytesTota void StatusHandlerTemporaryPanel::reportInfo(const std::wstring& text) { - errorLog.logMsg(text, TYPE_INFO); //log first! + errorLog_.logMsg(text, TYPE_INFO); //log first! StatusHandler::reportInfo(text); //throw X } @@ -139,7 +139,7 @@ ProcessCallback::Response StatusHandlerTemporaryPanel::reportError(const std::ws //=> similar behavior like "ignoreErrors" which is also not used for the comparison phase in GUI mode //always, except for "retry": - auto guardWriteLog = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { errorLog.logMsg(errorMessage, TYPE_ERROR); }); + auto guardWriteLog = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { errorLog_.logMsg(errorMessage, TYPE_ERROR); }); switch (handleError_) { @@ -147,20 +147,20 @@ ProcessCallback::Response StatusHandlerTemporaryPanel::reportError(const std::ws { forceUiRefresh(); - bool ignoreNextErrors = false; - switch (showConfirmationDialog3(&mainDlg, DialogInfoType::ERROR2, PopupDialogCfg3(). - setDetailInstructions(errorMessage). - setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT), - _("&Ignore"), _("&Retry"))) + switch (showConfirmationDialog(&mainDlg_, DialogInfoType::ERROR2, PopupDialogCfg(). + setDetailInstructions(errorMessage), + _("&Ignore"), _("Ignore &all"), _("&Retry"))) { - case ConfirmationButton3::DO_IT: //ignore - if (ignoreNextErrors) //falsify only - handleError_ = ON_GUIERROR_IGNORE; + case ConfirmationButton3::ACCEPT: //ignore + return ProcessCallback::IGNORE_ERROR; + + case ConfirmationButton3::ACCEPT_ALL: //ignore all + handleError_ = ON_GUIERROR_IGNORE; return ProcessCallback::IGNORE_ERROR; - case ConfirmationButton3::DONT_DO_IT: //retry + case ConfirmationButton3::DECLINE: //retry guardWriteLog.dismiss(); - errorLog.logMsg(errorMessage + L"\n-> " + _("Retrying operation..."), TYPE_INFO); //explain why there are duplicate "doing operation X" info messages in the log! + errorLog_.logMsg(errorMessage + L"\n-> " + _("Retrying operation..."), TYPE_INFO); //explain why there are duplicate "doing operation X" info messages in the log! return ProcessCallback::RETRY; case ConfirmationButton3::CANCEL: @@ -181,16 +181,16 @@ ProcessCallback::Response StatusHandlerTemporaryPanel::reportError(const std::ws void StatusHandlerTemporaryPanel::reportFatalError(const std::wstring& errorMessage) { - errorLog.logMsg(errorMessage, TYPE_FATAL_ERROR); + errorLog_.logMsg(errorMessage, TYPE_FATAL_ERROR); forceUiRefresh(); - showNotificationDialog(&mainDlg, DialogInfoType::ERROR2, PopupDialogCfg().setTitle(_("Serious Error")).setDetailInstructions(errorMessage)); + showNotificationDialog(&mainDlg_, DialogInfoType::ERROR2, PopupDialogCfg().setTitle(_("Serious Error")).setDetailInstructions(errorMessage)); } void StatusHandlerTemporaryPanel::reportWarning(const std::wstring& warningMessage, bool& warningActive) { - errorLog.logMsg(warningMessage, TYPE_WARNING); + errorLog_.logMsg(warningMessage, TYPE_WARNING); if (!warningActive) //if errors are ignored, then warnings should also return; @@ -202,12 +202,12 @@ void StatusHandlerTemporaryPanel::reportWarning(const std::wstring& warningMessa forceUiRefresh(); bool dontWarnAgain = false; - switch (showConfirmationDialog(&mainDlg, DialogInfoType::WARNING, + switch (showConfirmationDialog(&mainDlg_, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(warningMessage). setCheckBox(dontWarnAgain, _("&Don't show this warning again")), _("&Ignore"))) { - case ConfirmationButton::DO_IT: + case ConfirmationButton::ACCEPT: warningActive = !dontWarnAgain; break; case ConfirmationButton::CANCEL: @@ -225,7 +225,7 @@ void StatusHandlerTemporaryPanel::reportWarning(const std::wstring& warningMessa void StatusHandlerTemporaryPanel::forceUiRefresh() { - mainDlg.compareStatus_->updateStatusPanelNow(); + mainDlg_.compareStatus_->updateStatusPanelNow(); } @@ -252,12 +252,12 @@ StatusHandlerFloatingDialog::StatusHandlerFloatingDialog(wxFrame* parentDlg, const Zstring& soundFileSyncComplete, const Zstring& onCompletion, std::vector<Zstring>& onCompletionHistory) : - progressDlg(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, parentDlg, true, jobName, soundFileSyncComplete, onCompletion, onCompletionHistory)), - lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax), - handleError_(handleError), - automaticRetryCount_(automaticRetryCount), - automaticRetryDelay_(automaticRetryDelay), - jobName_(jobName), + progressDlg_(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, parentDlg, true, jobName, soundFileSyncComplete, onCompletion, onCompletionHistory)), + lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax), + handleError_(handleError), + automaticRetryCount_(automaticRetryCount), + automaticRetryDelay_(automaticRetryDelay), + jobName_(jobName), startTime_(std::time(nullptr)) {} @@ -268,12 +268,12 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog() //decide whether to stay on status screen or exit immediately... bool showFinalResults = true; - if (progressDlg) + if (progressDlg_) { //execute "on completion" command (even in case of ignored errors) if (!abortIsRequested()) //if aborted (manually), we don't execute the command { - const Zstring finalCommand = progressDlg->getExecWhenFinishedCommand(); //final value (after possible user modification) + const Zstring finalCommand = progressDlg_->getExecWhenFinishedCommand(); //final value (after possible user modification) if (!finalCommand.empty()) { if (isCloseProgressDlgCommand(finalCommand)) @@ -336,27 +336,27 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog() } catch (FileError&) { assert(false); } - if (progressDlg) + if (progressDlg_) { //notify to progressDlg that current process has ended if (showFinalResults) { if (abortIsRequested()) - progressDlg->processHasFinished(SyncProgressDialog::RESULT_ABORTED, errorLog_); //enable okay and close events + progressDlg_->processHasFinished(SyncProgressDialog::RESULT_ABORTED, errorLog_); //enable okay and close events else if (totalErrors > 0) - progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_ERROR, errorLog_); + progressDlg_->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_ERROR, errorLog_); else if (totalWarnings > 0) - progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS, errorLog_); + progressDlg_->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS, errorLog_); else - progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS, errorLog_); + progressDlg_->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS, errorLog_); } else - progressDlg->closeWindowDirectly(); + progressDlg_->closeWindowDirectly(); //wait until progress dialog notified shutdown via onProgressDialogTerminate() //-> required since it has our "this" pointer captured in lambda "notifyWindowTerminate"! //-> nicely manages dialog lifetime - while (progressDlg) + while (progressDlg_) { wxTheApp->Yield(); //*first* refresh GUI (removing flicker) before sleeping! std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS)); @@ -369,8 +369,8 @@ void StatusHandlerFloatingDialog::initNewPhase(int itemsTotal, int64_t bytesTota { assert(phaseID == PHASE_SYNCHRONIZING); StatusHandler::initNewPhase(itemsTotal, bytesTotal, phaseID); - if (progressDlg) - progressDlg->initNewPhase(); //call after "StatusHandler::initNewPhase" + if (progressDlg_) + progressDlg_->initNewPhase(); //call after "StatusHandler::initNewPhase" forceUiRefresh(); //throw ?; OS X needs a full yield to update GUI and get rid of "dummy" texts } @@ -379,8 +379,8 @@ void StatusHandlerFloatingDialog::initNewPhase(int itemsTotal, int64_t bytesTota void StatusHandlerFloatingDialog::updateProcessedData(int itemsDelta, int64_t bytesDelta) { StatusHandler::updateProcessedData(itemsDelta, bytesDelta); - if (progressDlg) - progressDlg->notifyProgressChange(); //noexcept + if (progressDlg_) + progressDlg_->notifyProgressChange(); //noexcept //note: this method should NOT throw in order to properly allow undoing setting of statistics! } @@ -417,22 +417,22 @@ ProcessCallback::Response StatusHandlerFloatingDialog::reportError(const std::ws { case ON_GUIERROR_POPUP: { - if (!progressDlg) abortProcessNow(); - PauseTimers dummy(*progressDlg); + if (!progressDlg_) abortProcessNow(); + PauseTimers dummy(*progressDlg_); forceUiRefresh(); - bool ignoreNextErrors = false; - switch (showConfirmationDialog3(progressDlg->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg3(). - setDetailInstructions(errorMessage). - setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT), - _("&Ignore"), _("&Retry"))) + switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg(). + setDetailInstructions(errorMessage), + _("&Ignore"), _("Ignore &all"), _("&Retry"))) { - case ConfirmationButton3::DO_IT: //ignore - if (ignoreNextErrors) //falsify only - handleError_ = ON_GUIERROR_IGNORE; + case ConfirmationButton3::ACCEPT: //ignore return ProcessCallback::IGNORE_ERROR; - case ConfirmationButton3::DONT_DO_IT: //retry + case ConfirmationButton3::ACCEPT_ALL: //ignore all + handleError_ = ON_GUIERROR_IGNORE; + return ProcessCallback::IGNORE_ERROR; + + case ConfirmationButton3::DECLINE: //retry guardWriteLog.dismiss(); errorLog_.logMsg(errorMessage + L"\n-> " + _("Retrying operation..."), TYPE_INFO); //explain why there are duplicate "doing operation X" info messages in the log! return ProcessCallback::RETRY; @@ -461,22 +461,23 @@ void StatusHandlerFloatingDialog::reportFatalError(const std::wstring& errorMess { case ON_GUIERROR_POPUP: { - if (!progressDlg) abortProcessNow(); - PauseTimers dummy(*progressDlg); + if (!progressDlg_) abortProcessNow(); + PauseTimers dummy(*progressDlg_); forceUiRefresh(); - bool ignoreNextErrors = false; - switch (showConfirmationDialog(progressDlg->getWindowIfVisible(), DialogInfoType::ERROR2, + switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg().setTitle(_("Serious Error")). - setDetailInstructions(errorMessage). - setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors")), - _("&Ignore"))) + setDetailInstructions(errorMessage), + _("&Ignore"), _("Ignore &all"))) { - case ConfirmationButton::DO_IT: - if (ignoreNextErrors) //falsify only - handleError_ = ON_GUIERROR_IGNORE; + case ConfirmationButton2::ACCEPT: break; - case ConfirmationButton::CANCEL: + + case ConfirmationButton2::ACCEPT_ALL: + handleError_ = ON_GUIERROR_IGNORE; + break; + + case ConfirmationButton2::CANCEL: abortProcessNow(); break; } @@ -500,17 +501,17 @@ void StatusHandlerFloatingDialog::reportWarning(const std::wstring& warningMessa { case ON_GUIERROR_POPUP: { - if (!progressDlg) abortProcessNow(); - PauseTimers dummy(*progressDlg); + if (!progressDlg_) abortProcessNow(); + PauseTimers dummy(*progressDlg_); forceUiRefresh(); bool dontWarnAgain = false; - switch (showConfirmationDialog(progressDlg->getWindowIfVisible(), DialogInfoType::WARNING, + switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(warningMessage). setCheckBox(dontWarnAgain, _("&Don't show this warning again")), _("&Ignore"))) { - case ConfirmationButton::DO_IT: + case ConfirmationButton::ACCEPT: warningActive = !dontWarnAgain; break; case ConfirmationButton::CANCEL: @@ -528,8 +529,8 @@ void StatusHandlerFloatingDialog::reportWarning(const std::wstring& warningMessa void StatusHandlerFloatingDialog::forceUiRefresh() { - if (progressDlg) - progressDlg->updateGui(); + if (progressDlg_) + progressDlg_->updateGui(); } @@ -543,5 +544,5 @@ void StatusHandlerFloatingDialog::abortProcessNow() void StatusHandlerFloatingDialog::onProgressDialogTerminate() { //it's responsibility of "progressDlg" to call requestAbortion() when closing dialog - progressDlg = nullptr; + progressDlg_ = nullptr; } diff --git a/FreeFileSync/Source/ui/gui_status_handler.h b/FreeFileSync/Source/ui/gui_status_handler.h index 5a5fcade..7ea2a8c3 100755 --- a/FreeFileSync/Source/ui/gui_status_handler.h +++ b/FreeFileSync/Source/ui/gui_status_handler.h @@ -37,15 +37,15 @@ public: void forceUiRefresh() override; void abortProcessNow() override final; //throw GuiAbortProcess - zen::ErrorLog getErrorLog() const { return errorLog; } + zen::ErrorLog getErrorLog() const { return errorLog_; } private: void OnKeyPressed(wxKeyEvent& event); void OnAbortCompare(wxCommandEvent& event); //handle abort button click - MainDialog& mainDlg; + MainDialog& mainDlg_; xmlAccess::OnGuiError handleError_ = xmlAccess::ON_GUIERROR_POPUP; - zen::ErrorLog errorLog; + zen::ErrorLog errorLog_; }; @@ -78,7 +78,7 @@ public: private: void onProgressDialogTerminate(); - SyncProgressDialog* progressDlg; //managed to have shorter lifetime than this handler! + SyncProgressDialog* progressDlg_; //managed to have shorter lifetime than this handler! const size_t lastSyncsLogFileSizeMax_; xmlAccess::OnGuiError handleError_; zen::ErrorLog errorLog_; diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp index 3a280a34..0200defb 100755 --- a/FreeFileSync/Source/ui/main_dlg.cpp +++ b/FreeFileSync/Source/ui/main_dlg.cpp @@ -87,6 +87,46 @@ bool isComponentOf(const wxWindow* child, const wxWindow* top) } +inline +wxTopLevelWindow* getTopLevelWindow(wxWindow* child) +{ + for (wxWindow* wnd = child; wnd != nullptr; wnd = wnd->GetParent()) + if (auto tlw = dynamic_cast<wxTopLevelWindow*>(wnd)) //why does wxWidgets use wxWindows::IsTopLevel() ?? + return tlw; + return nullptr; +} + + +/* +Preserving input focus has to be more clever than: + wxWindow* oldFocus = wxWindow::FindFocus(); + ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus()); + +=> wxWindow::SetFocus() internally calls Win32 ::SetFocus, which calls ::SetActiveWindow, which - lord knows why - changes the foreground window to the focus window + even if the user is currently busy using a different app! More curiosity: this foreground focus stealing happens only during the *first* SetFocus() after app start! + It also can be avoided by changing focus back and forth with some other app after start => wxWidgets bug or Win32 feature??? +*/ +struct FocusPreserver +{ + ~FocusPreserver() + { + //wxTopLevelWindow::IsActive() does NOT call Win32 ::GetActiveWindow()! + //Instead it checks if ::GetFocus() is set somewhere inside the top level + //Note: Both Win32 active and focus windows are *thread-local* values, while foreground window is global! https://blogs.msdn.microsoft.com/oldnewthing/20131016-00/?p=2913 + if (oldFocus_) + if (wxTopLevelWindow* topWin = getTopLevelWindow(oldFocus_)) + if (topWin->IsActive()) //Linux/macOS: already behaves just like ::GetForegroundWindow() on Windows! + oldFocus_->SetFocus(); + } + + wxWindow* getFocus() const { return oldFocus_; } + void setFocus(wxWindow* win) { oldFocus_ = win; } + +private: + wxWindow* oldFocus_ = wxWindow::FindFocus(); +}; + + bool acceptDialogFileDrop(const std::vector<Zstring>& shellItemPaths) { return std::any_of(shellItemPaths.begin(), shellItemPaths.end(), [](const Zstring& shellItemPath) @@ -470,7 +510,7 @@ MainDialog::MainDialog(const Zstring& globalConfigFile, wxAuiPaneInfo().Name(L"CenterPanel").CenterPane().PaneBorder(false)); { //set comparison button label tentatively for m_panelTopButtons to receive final height: - updateTopButton(*m_buttonCompare, getResourceImage(L"compare"), L"Dummy", false); + updateTopButton(*m_buttonCompare, getResourceImage(L"compare"), L"Dummy", false /*makeGrey*/); m_panelTopButtons->GetSizer()->SetSizeHints(m_panelTopButtons); //~=Fit() + SetMinSize() setBitmapTextLabel(*m_buttonCancel, wxImage(), m_buttonCancel->GetLabel()); //we can't use a wxButton for cancel: it's rendered smaller on OS X than a wxBitmapButton! @@ -1166,8 +1206,7 @@ void MainDialog::copyToAlternateFolder(const std::vector<zen::FileSystemObject*> if (rowsLeftTmp.empty() && rowsRightTmp.empty()) return; - wxWindow* oldFocus = wxWindow::FindFocus(); - ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus()); + FocusPreserver fp; if (zen::showCopyToDialog(this, rowsLeftTmp, rowsRightTmp, @@ -1210,8 +1249,7 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec if (rowsLeftTmp.empty() && rowsRightTmp.empty()) return; - wxWindow* oldFocus = wxWindow::FindFocus(); - ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus()); + FocusPreserver fp; //sigh: do senseless vector<FileSystemObject*> -> vector<const FileSystemObject*> conversion: if (zen::showDeleteDialog(this, { rowsLeftTmp.begin(), rowsLeftTmp.end() }, { rowsRightTmp.begin(), rowsRightTmp.end() }, @@ -1404,7 +1442,7 @@ void MainDialog::openExternalApplication(const Zstring& commandLinePhrase, bool setCheckBox(dontAskAgain, _("&Don't show this warning again")), _("&Execute"))) { - case ConfirmationButton::DO_IT: + case ConfirmationButton::ACCEPT: globalCfg_.optDialogs.confirmExternalCommandMassInvoke = !dontAskAgain; break; case ConfirmationButton::CANCEL: @@ -1427,8 +1465,7 @@ void MainDialog::openExternalApplication(const Zstring& commandLinePhrase, bool //##################### create temporary files for non-native paths ###################### if (!nonNativeFiles.empty()) { - wxWindow* oldFocus = wxWindow::FindFocus(); - ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus()); + FocusPreserver fp; disableAllElements(true); //StatusHandlerTemporaryPanel will internally process Window messages, so avoid unexpected callbacks! auto app = wxTheApp; //fix lambda/wxWigets/VC fuck up @@ -1591,10 +1628,11 @@ void MainDialog::disableAllElements(bool enableAbort) //show abort button m_buttonCancel->Enable(); m_buttonCancel->Show(); - if (m_buttonCancel->IsShownOnScreen()) - m_buttonCancel->SetFocus(); + //if (m_buttonCancel->IsShownOnScreen()) -> needed? + m_buttonCancel->SetFocus(); m_buttonCompare->Disable(); m_buttonCompare->Hide(); + m_panelTopButtons->Layout(); } else @@ -2801,9 +2839,9 @@ void MainDialog::updateUnsavedCfgStatus() title += utfTo<wxString>(activeCfgFilename); else if (activeConfigFiles_.size() > 1) { - const wchar_t* EM_DASH = L" \u2014 "; + const wchar_t* EN_DASH = L" \u2013 "; title += xmlAccess::extractJobName(activeConfigFiles_[0]); - std::for_each(activeConfigFiles_.begin() + 1, activeConfigFiles_.end(), [&](const Zstring& filepath) { title += EM_DASH + xmlAccess::extractJobName(filepath); }); + std::for_each(activeConfigFiles_.begin() + 1, activeConfigFiles_.end(), [&](const Zstring& filepath) { title += EN_DASH + xmlAccess::extractJobName(filepath); }); } else title += L"FreeFileSync - " + _("Folder Comparison and Synchronization"); @@ -3004,14 +3042,14 @@ bool MainDialog::saveOldConfig() //return false on user abort //only if check is active and non-default config file loaded { bool neverSaveChanges = false; - switch (showConfirmationDialog3(this, DialogInfoType::INFO, PopupDialogCfg3(). - setTitle(utfTo<wxString>(activeCfgFilename)). - setMainInstructions(replaceCpy(_("Do you want to save changes to %x?"), L"%x", - fmtPath(afterLast(activeCfgFilename, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)))). - setCheckBox(neverSaveChanges, _("Never save &changes"), ConfirmationButton3::DO_IT), - _("&Save"), _("Do&n't save"))) + switch (showQuestionDialog(this, DialogInfoType::INFO, PopupDialogCfg(). + setTitle(utfTo<wxString>(activeCfgFilename)). + setMainInstructions(replaceCpy(_("Do you want to save changes to %x?"), L"%x", + fmtPath(afterLast(activeCfgFilename, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)))). + setCheckBox(neverSaveChanges, _("Never save &changes"), QuestionButton2::YES), + _("&Save"), _("Do&n't save"))) { - case ConfirmationButton3::DO_IT: //save + case QuestionButton2::YES: //save using namespace xmlAccess; try @@ -3036,11 +3074,11 @@ bool MainDialog::saveOldConfig() //return false on user abort } break; - case ConfirmationButton3::DONT_DO_IT: //don't save + case QuestionButton2::NO: //don't save globalCfg_.optDialogs.popupOnConfigChange = !neverSaveChanges; break; - case ConfirmationButton3::CANCEL: + case QuestionButton2::CANCEL: return false; } } @@ -3675,8 +3713,7 @@ void MainDialog::OnCompare(wxCommandEvent& event) { //wxBusyCursor dummy; -> redundant: progress already shown in progress dialog! - wxWindow* oldFocus = wxWindow::FindFocus(); - ZEN_ON_SCOPE_EXIT(if (oldFocus && oldFocus->IsShownOnScreen()) oldFocus->SetFocus()); //e.g. keep focus on config panel after pressing F5 + FocusPreserver fp; //e.g. keep focus on config panel after pressing F5 int scrollPosX = 0; int scrollPosY = 0; @@ -3734,15 +3771,19 @@ void MainDialog::OnCompare(wxCommandEvent& event) { const Zstring soundFilePath = getResourceDirPf() + globalCfg_.soundFileCompareFinished; if (fileAvailable(soundFilePath)) - wxSound::Play(utfTo<wxString>(soundFilePath), wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as batch! + wxSound::Play(utfTo<wxString>(soundFilePath), wxSOUND_ASYNC); + //warning: this may fail and show a wxWidgets error message! => must not play when running FFS without user interaction! } + if (!IsActive()) + RequestUserAttention(); + //add to folder history after successful comparison only folderHistoryLeft_ ->addItem(utfTo<Zstring>(m_folderPathLeft ->GetValue())); folderHistoryRight_->addItem(utfTo<Zstring>(m_folderPathRight->GetValue())); - if (oldFocus == m_buttonCompare) - oldFocus = m_buttonSync; + if (fp.getFocus() == m_buttonCompare) + fp.setFocus(m_buttonSync); //prepare status information if (allElementsEqual(folderCmp_)) @@ -3752,7 +3793,7 @@ void MainDialog::OnCompare(wxCommandEvent& event) void MainDialog::updateTopButtonImages() { - updateTopButton(*m_buttonCompare, getResourceImage(L"compare"), getConfig().mainCfg.getCompVariantName(), false); + updateTopButton(*m_buttonCompare, getResourceImage(L"compare"), getConfig().mainCfg.getCompVariantName(), false /*makeGrey*/); updateTopButton(*m_buttonSync, getResourceImage(L"sync"), getConfig().mainCfg.getSyncVariantName(), folderCmp_.empty()); m_panelTopButtons->Layout(); diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp index 90ee11d7..48848df2 100755 --- a/FreeFileSync/Source/ui/progress_indicator.cpp +++ b/FreeFileSync/Source/ui/progress_indicator.cpp @@ -152,7 +152,7 @@ public: private: std::pair<double, double> getRangeX() const override { return std::make_pair(0, 1); } - std::vector<CurvePoint> getPoints(double minX, double maxX, int pixelWidth) const override + std::vector<CurvePoint> getPoints(double minX, double maxX, const wxSize& areaSizePx) const override { const double yHigh = drawTop_ ? 3 : 1; //draw partially out of vertical bounds to not render top/bottom borders of the bars const double yLow = drawTop_ ? 1 : -1; // @@ -174,7 +174,7 @@ class CurveDataProgressSeparatorLine : public CurveData { std::pair<double, double> getRangeX() const override { return std::make_pair(0, 1); } - std::vector<CurvePoint> getPoints(double minX, double maxX, int pixelWidth) const override + std::vector<CurvePoint> getPoints(double minX, double maxX, const wxSize& areaSizePx) const override { return { @@ -999,13 +999,13 @@ namespace class CurveDataStatistics : public SparseCurveData { public: - CurveDataStatistics() : SparseCurveData(true) /*true: add steps*/ {} + CurveDataStatistics() : SparseCurveData(true /*addSteps*/) {} - void clear() { samples.clear(); lastSample = std::make_pair(0, 0); } + void clear() { samples_.clear(); lastSample_ = std::make_pair(0, 0); } void addRecord(int64_t timeNowMs, double value) { - assert((!samples.empty() || lastSample == std::pair<int64_t, double>(0, 0))); + assert((!samples_.empty() || lastSample_ == std::pair<int64_t, double>(0, 0))); //samples.clear(); //samples.emplace(-1000, 0); @@ -1014,28 +1014,28 @@ public: //samples.emplace(1000, 0); //return; - lastSample = std::make_pair(timeNowMs, value); + lastSample_ = std::make_pair(timeNowMs, value); //allow for at most one sample per 100ms (handles duplicate inserts, too!) => this is unrelated to UI_UPDATE_INTERVAL_MS! - if (!samples.empty()) //always unconditionally insert first sample! - if (timeNowMs / 100 == samples.rbegin()->first / 100) + if (!samples_.empty()) //always unconditionally insert first sample! + if (timeNowMs / 100 == samples_.rbegin()->first / 100) return; - samples.insert(samples.end(), std::make_pair(timeNowMs, value)); //time is "expected" to be monotonously ascending + samples_.insert(samples_.end(), std::make_pair(timeNowMs, value)); //time is "expected" to be monotonously ascending //documentation differs about whether "hint" should be before or after the to be inserted element! //however "std::map<>::end()" is interpreted correctly by GCC and VS2010 - if (samples.size() > MAX_BUFFER_SIZE) //limit buffer size - samples.erase(samples.begin()); + if (samples_.size() > MAX_BUFFER_SIZE) //limit buffer size + samples_.erase(samples_.begin()); } private: std::pair<double, double> getRangeX() const override { - if (samples.empty()) + if (samples_.empty()) return std::make_pair(0.0, 0.0); - double upperEndMs = std::max(samples.rbegin()->first, lastSample.first); + double upperEndMs = std::max(samples_.rbegin()->first, lastSample_.first); /* //report some additional width by 5% elapsed time to make graph recalibrate before hitting the right border @@ -1044,7 +1044,7 @@ private: upperEndMs += 0.05 *(upperEndMs - samples.begin()->first); */ - return std::make_pair(samples.begin()->first / 1000.0, //need not start with 0, e.g. "binary comparison, graph reset, followed by sync" + return std::make_pair(samples_.begin()->first / 1000.0, //need not start with 0, e.g. "binary comparison, graph reset, followed by sync" upperEndMs / 1000.0); } @@ -1052,14 +1052,14 @@ private: { const int64_t timex = std::floor(x * 1000); //------ add artifical last sample value ------- - if (!samples.empty() && samples.rbegin()->first < lastSample.first) - if (lastSample.first <= timex) - return CurvePoint(lastSample.first / 1000.0, lastSample.second); + if (!samples_.empty() && samples_.rbegin()->first < lastSample_.first) + if (lastSample_.first <= timex) + return CurvePoint(lastSample_.first / 1000.0, lastSample_.second); //-------------------------------------------------- //find first key > x, then go one step back: => samples must be a std::map, NOT std::multimap!!! - auto it = samples.upper_bound(timex); - if (it == samples.begin()) + auto it = samples_.upper_bound(timex); + if (it == samples_.begin()) return NoValue(); //=> samples not empty in this context --it; @@ -1070,21 +1070,21 @@ private: { const int64_t timex = std::ceil(x * 1000); //------ add artifical last sample value ------- - if (!samples.empty() && samples.rbegin()->first < lastSample.first) - if (samples.rbegin()->first < timex && timex <= lastSample.first) - return CurvePoint(lastSample.first / 1000.0, lastSample.second); + if (!samples_.empty() && samples_.rbegin()->first < lastSample_.first) + if (samples_.rbegin()->first < timex && timex <= lastSample_.first) + return CurvePoint(lastSample_.first / 1000.0, lastSample_.second); //-------------------------------------------------- - auto it = samples.lower_bound(timex); - if (it == samples.end()) + auto it = samples_.lower_bound(timex); + if (it == samples_.end()) return NoValue(); return CurvePoint(it->first / 1000.0, it->second); } static const size_t MAX_BUFFER_SIZE = 2500000; //sizeof(single node) worst case ~ 3 * 8 byte ptr + 16 byte key/value = 40 byte - std::map <int64_t, double> samples; //time, unit: [ms] !don't use std::multimap, see getLessEq() - std::pair<int64_t, double> lastSample; //artificial most current record at the end of samples to visualize current time! + std::map <int64_t, double> samples_; //time, unit: [ms] !don't use std::multimap, see getLessEq() + std::pair<int64_t, double> lastSample_; //artificial most current record at the end of samples to visualize current time! }; @@ -1098,7 +1098,7 @@ public: private: std::pair<double, double> getRangeX() const override { return std::make_pair(x_, x_); } //conceptually just a vertical line! - std::vector<CurvePoint> getPoints(double minX, double maxX, int pixelWidth) const override + std::vector<CurvePoint> getPoints(double minX, double maxX, const wxSize& areaSizePx) const override { return { @@ -1228,7 +1228,7 @@ public: void initNewPhase () override; void notifyProgressChange() override; - void updateGui () override { updateGuiInt(true); } + void updateGui () override { updateGuiInt(true /*allowYield*/); } Zstring getExecWhenFinishedCommand() const override { return pnl_.m_comboBoxOnCompletion->getValue(); } @@ -1373,13 +1373,13 @@ SyncProgressDialogImpl<TopLevelDialog>::SyncProgressDialogImpl(long style, //wxF const int xLabelHeight = this->GetCharHeight() + 2 * 1 /*border*/; //use same height for both graphs to make sure they stretch evenly const int yLabelWidth = 70; pnl_.m_panelGraphBytes->setAttributes(Graph2D::MainAttributes(). - setLabelX(Graph2D::LABEL_X_BOTTOM, xLabelHeight, std::make_shared<LabelFormatterTimeElapsed>(true)). + setLabelX(Graph2D::LABEL_X_TOP, xLabelHeight, std::make_shared<LabelFormatterTimeElapsed>(true)). setLabelY(Graph2D::LABEL_Y_RIGHT, yLabelWidth, std::make_shared<LabelFormatterBytes>()). setBackgroundColor(wxColor(208, 208, 208)). //light grey setSelectionMode(Graph2D::SELECT_NONE)); pnl_.m_panelGraphItems->setAttributes(Graph2D::MainAttributes(). - setLabelX(Graph2D::LABEL_X_BOTTOM, xLabelHeight, std::make_shared<LabelFormatterTimeElapsed>(false)). + setLabelX(Graph2D::LABEL_X_BOTTOM, xLabelHeight, std::make_shared<LabelFormatterTimeElapsed>(true)). setLabelY(Graph2D::LABEL_Y_RIGHT, yLabelWidth, std::make_shared<LabelFormatterItemCount>()). setBackgroundColor(wxColor(208, 208, 208)). //light grey setSelectionMode(Graph2D::SELECT_NONE)); @@ -1424,7 +1424,7 @@ SyncProgressDialogImpl<TopLevelDialog>::SyncProgressDialogImpl(long style, //wxF pnl_.m_buttonStop->SetFocus(); //don't steal focus when starting in sys-tray! //clear gui flicker, remove dummy texts: window must be visible to make this work! - updateGuiInt(true); //at least on OS X a real Yield() is required to flush pending GUI updates; Update() is not enough + updateGuiInt(true /*allowYield*/); //at least on OS X a real Yield() is required to flush pending GUI updates; Update() is not enough } else minimizeToTray(); @@ -1863,7 +1863,7 @@ void SyncProgressDialogImpl<TopLevelDialog>::processHasFinished(SyncResult resul //update numbers one last time (as if sync were still running) notifyProgressChange(); //make one last graph entry at the *current* time - updateGuiInt(false); + updateGuiInt(false /*allowYield*/); switch (syncStat_->currentPhase()) //no matter if paused or not { @@ -1984,8 +1984,11 @@ void SyncProgressDialogImpl<TopLevelDialog>::processHasFinished(SyncResult resul { const Zstring soundFilePath = getResourceDirPf() + soundFileSyncComplete_; if (fileAvailable(soundFilePath)) - wxSound::Play(utfTo<wxString>(soundFilePath), wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as batch! + wxSound::Play(utfTo<wxString>(soundFilePath), wxSOUND_ASYNC); + //warning: this may fail and show a wxWidgets error message! => must not play when running FFS without user interaction! } + //if (::GetForegroundWindow() != GetHWND()) + // RequestUserAttention(); -> probably too much since task bar already alreay is colorized with Taskbar::STATUS_ERROR or STATUS_NORMAL break; } @@ -2082,7 +2085,7 @@ void SyncProgressDialogImpl<TopLevelDialog>::minimizeToTray() trayIcon_ = std::make_unique<FfsTrayIcon>([this] { this->resumeFromSystray(); }); //FfsTrayIcon lifetime is a subset of "this"'s lifetime! //we may destroy FfsTrayIcon even while in the FfsTrayIcon callback!!!! - updateGuiInt(false); //set tray tooltip + progress: e.g. no updates while paused + updateGuiInt(false /*allowYield*/); //set tray tooltip + progress: e.g. no updates while paused this->Hide(); if (parentFrame_) @@ -2112,8 +2115,8 @@ void SyncProgressDialogImpl<TopLevelDialog>::resumeFromSystray() this->Raise(); this->SetFocus(); - updateDialogStatus(); //restore Windows 7 task bar status (e.g. required in pause mode) - updateGuiInt(false); //restore Windows 7 task bar progress (e.g. required in pause mode) + updateDialogStatus(); //restore Windows 7 task bar status (e.g. required in pause mode) + updateGuiInt(false) /*allowYield*/; //restore Windows 7 task bar progress (e.g. required in pause mode) } } diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp index 1f947671..550f7749 100755 --- a/FreeFileSync/Source/ui/small_dlgs.cpp +++ b/FreeFileSync/Source/ui/small_dlgs.cpp @@ -619,7 +619,7 @@ void OptionsDlg::OnResetDialogs(wxCommandEvent& event) PopupDialogCfg().setMainInstructions(_("Show hidden dialogs and warning messages again?")), _("&Show"))) { - case ConfirmationButton::DO_IT: + case ConfirmationButton::ACCEPT: globalSettingsOut.optDialogs = xmlAccess::OptionalDialogs(); break; case ConfirmationButton::CANCEL: diff --git a/FreeFileSync/Source/ui/tree_view.cpp b/FreeFileSync/Source/ui/tree_view.cpp index 2d0e91cd..6ccd58f0 100755 --- a/FreeFileSync/Source/ui/tree_view.cpp +++ b/FreeFileSync/Source/ui/tree_view.cpp @@ -23,6 +23,7 @@ using namespace zen; namespace { const int WIDTH_PERCENTAGE_BAR = 60; +const wchar_t* EN_DASH = L"\u2013"; } @@ -192,7 +193,7 @@ Zstring zen::getShortDisplayNameForFolderPair(const AbstractPath& itemPathL, con else if (AFS::isNullPath(itemPathR)) return getLastComponent(itemPathL); else - return getLastComponent(itemPathL) + utfTo<Zstring>(L" \u2212 ") + //= unicode minus + return getLastComponent(itemPathL) + Zstr(" ") + utfTo<Zstring>(EN_DASH) + Zstr(" ") + getLastComponent(itemPathR); } @@ -794,7 +795,7 @@ private: return dirRight; else if (dirRight.empty()) return dirLeft; - return dirLeft + L" \u2212 \n" + dirRight; //\u2212 = unicode minus + return dirLeft + L" " + EN_DASH + L"\n" + dirRight; } break; diff --git a/FreeFileSync/Source/ui/version_check.cpp b/FreeFileSync/Source/ui/version_check.cpp index ddd9bc43..45f166bb 100755 --- a/FreeFileSync/Source/ui/version_check.cpp +++ b/FreeFileSync/Source/ui/version_check.cpp @@ -123,7 +123,7 @@ void showUpdateAvailableDialog(wxWindow* parent, const std::string& onlineVersio setDetailInstructions(updateDetailsMsg), _("&Download"))) { - case ConfirmationButton::DO_IT: + case ConfirmationButton::ACCEPT: wxLaunchDefaultBrowser(L"http://www.freefilesync.org/get_latest.php"); break; case ConfirmationButton::CANCEL: @@ -203,7 +203,7 @@ void zen::checkForUpdateNow(wxWindow* parent, std::string& lastOnlineVersion) setMainInstructions(_("Cannot find current FreeFileSync version number online. A newer version is likely available. Check manually now?")). setDetailInstructions(e.toString()), _("&Check"))) { - case ConfirmationButton::DO_IT: + case ConfirmationButton::ACCEPT: wxLaunchDefaultBrowser(L"http://www.freefilesync.org/get_latest.php"); break; case ConfirmationButton::CANCEL: @@ -285,7 +285,7 @@ void zen::periodicUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, std setDetailInstructions(result.error->toString()), _("&Check"))) { - case ConfirmationButton::DO_IT: + case ConfirmationButton::ACCEPT: wxLaunchDefaultBrowser(L"http://www.freefilesync.org/get_latest.php"); break; case ConfirmationButton::CANCEL: diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h index a32b3e5d..88471d98 100755 --- a/FreeFileSync/Source/version/version.h +++ b/FreeFileSync/Source/version/version.h @@ -3,7 +3,7 @@ namespace zen { -const char ffsVersion[] = "9.2"; //internal linkage! +const char ffsVersion[] = "9.3"; //internal linkage! const char FFS_VERSION_SEPARATOR = '.'; } diff --git a/wx+/context_menu.h b/wx+/context_menu.h index 41d2bf71..27e794b1 100755 --- a/wx+/context_menu.h +++ b/wx+/context_menu.h @@ -82,8 +82,8 @@ public: } private: - ContextMenu (const ContextMenu&) = delete; - ContextMenu& operator=(const ContextMenu&) = delete; + ContextMenu (const ContextMenu&) = delete; + ContextMenu& operator=(const ContextMenu&) = delete; void onSelection(wxCommandEvent& event) { @@ -97,7 +97,7 @@ private: std::function<void()> fun_; }; - std::unique_ptr<wxMenu> menu_ = std::make_unique<wxMenu>(); + std::unique_ptr<wxMenu> menu_ = std::make_unique<wxMenu>(); std::map<int, std::function<void()>> commandList_; //(item id, command) }; } diff --git a/wx+/graph.cpp b/wx+/graph.cpp index d45a0e85..78399c3f 100755 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -75,33 +75,33 @@ class ConvertCoord //convert between screen and input data coordinates public: ConvertCoord(double valMin, double valMax, size_t screenSize) : min_(valMin), - scaleToReal(screenSize == 0 ? 0 : (valMax - valMin) / screenSize), - scaleToScr(numeric::isNull((valMax - valMin)) ? 0 : screenSize / (valMax - valMin)), - outOfBoundsLow (-1 * scaleToReal + valMin), - outOfBoundsHigh((screenSize + 1) * scaleToReal + valMin) { if (outOfBoundsLow > outOfBoundsHigh) std::swap(outOfBoundsLow, outOfBoundsHigh); } + scaleToReal_(screenSize == 0 ? 0 : (valMax - valMin) / screenSize), + scaleToScr_(numeric::isNull((valMax - valMin)) ? 0 : screenSize / (valMax - valMin)), + outOfBoundsLow_ (-1 * scaleToReal_ + valMin), + outOfBoundsHigh_((screenSize + 1) * scaleToReal_ + valMin) { if (outOfBoundsLow_ > outOfBoundsHigh_) std::swap(outOfBoundsLow_, outOfBoundsHigh_); } double screenToReal(double screenPos) const //map [0, screenSize] -> [valMin, valMax] { - return screenPos * scaleToReal + min_; + return screenPos * scaleToReal_ + min_; } double realToScreen(double realPos) const //return screen position in pixel (but with double precision!) { - return (realPos - min_) * scaleToScr; + return (realPos - min_) * scaleToScr_; } int realToScreenRound(double realPos) const //returns -1 and screenSize + 1 if out of bounds! { //catch large double values: if double is larger than what int can represent => undefined behavior! - numeric::clamp(realPos, outOfBoundsLow, outOfBoundsHigh); + numeric::clamp(realPos, outOfBoundsLow_, outOfBoundsHigh_); return numeric::round(realToScreen(realPos)); } private: - double min_; - double scaleToReal; - double scaleToScr; + const double min_; + const double scaleToReal_; + const double scaleToScr_; - double outOfBoundsLow; - double outOfBoundsHigh; + double outOfBoundsLow_; + double outOfBoundsHigh_; }; @@ -323,10 +323,11 @@ void cutPointsOutsideY(std::vector<CurvePoint>& curvePoints, std::vector<char>& } -std::vector<CurvePoint> ContinuousCurveData::getPoints(double minX, double maxX, int pixelWidth) const +std::vector<CurvePoint> ContinuousCurveData::getPoints(double minX, double maxX, const wxSize& areaSizePx) const { std::vector<CurvePoint> points; + const int pixelWidth = areaSizePx.GetWidth(); if (pixelWidth <= 1) return points; const ConvertCoord cvrtX(minX, maxX, pixelWidth - 1); //map [minX, maxX] to [0, pixelWidth - 1] @@ -352,9 +353,11 @@ std::vector<CurvePoint> ContinuousCurveData::getPoints(double minX, double maxX, } -std::vector<CurvePoint> SparseCurveData::getPoints(double minX, double maxX, int pixelWidth) const +std::vector<CurvePoint> SparseCurveData::getPoints(double minX, double maxX, const wxSize& areaSizePx) const { std::vector<CurvePoint> points; + + const int pixelWidth = areaSizePx.GetWidth(); if (pixelWidth <= 1) return points; const ConvertCoord cvrtX(minX, maxX, pixelWidth - 1); //map [minX, maxX] to [0, pixelWidth - 1] const std::pair<double, double> rangeX = getRangeX(); @@ -640,7 +643,7 @@ void Graph2D::render(wxDC& dc) const std::vector<CurvePoint>& points = curvePoints[index]; auto& marker = oobMarker [index]; - points = curve->getPoints(minX, maxX, graphArea.width); + points = curve->getPoints(minX, maxX, graphArea.GetSize()); marker.resize(points.size()); //default value: false if (!points.empty()) { diff --git a/wx+/graph.h b/wx+/graph.h index 84927b6d..89475611 100755 --- a/wx+/graph.h +++ b/wx+/graph.h @@ -47,7 +47,7 @@ struct CurveData virtual ~CurveData() {} virtual std::pair<double, double> getRangeX() const = 0; - virtual std::vector<CurvePoint> getPoints(double minX, double maxX, int pixelWidth) const = 0; //points outside the draw area are automatically trimmed! + virtual std::vector<CurvePoint> getPoints(double minX, double maxX, const wxSize& areaSizePx) const = 0; //points outside the draw area are automatically trimmed! }; //special curve types: @@ -56,7 +56,7 @@ struct ContinuousCurveData : public CurveData virtual double getValue(double x) const = 0; private: - std::vector<CurvePoint> getPoints(double minX, double maxX, int pixelWidth) const override; + std::vector<CurvePoint> getPoints(double minX, double maxX, const wxSize& areaSizePx) const override; }; struct SparseCurveData : public CurveData @@ -67,7 +67,7 @@ struct SparseCurveData : public CurveData virtual Opt<CurvePoint> getGreaterEq(double x) const = 0; private: - std::vector<CurvePoint> getPoints(double minX, double maxX, int pixelWidth) const override; + std::vector<CurvePoint> getPoints(double minX, double maxX, const wxSize& areaSizePx) const override; const bool addSteps_; }; diff --git a/wx+/http.cpp b/wx+/http.cpp index a826d751..d4fe1547 100755 --- a/wx+/http.cpp +++ b/wx+/http.cpp @@ -77,22 +77,29 @@ public: size_t read(void* buffer, size_t bytesToRead) //throw SysError, X; return "bytesToRead" bytes unless end of stream! { const size_t blockSize = getBlockSize(); - - while (memBuf_.size() < bytesToRead) + assert(memBuf_.size() <= blockSize); + char* it = static_cast<char*>(buffer); + char* const itEnd = it + bytesToRead; + for (;;) { - memBuf_.resize(memBuf_.size() + blockSize); - const size_t bytesRead = tryRead(&*(memBuf_.end() - blockSize), blockSize); //throw SysError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0 - memBuf_.resize(memBuf_.size() - blockSize + bytesRead); //caveat: unsigned arithmetics + const size_t junkSize = std::min(static_cast<size_t>(itEnd - it), memBuf_.size()); + std::copy (memBuf_.begin(), memBuf_.begin() + junkSize, it); + memBuf_.erase(memBuf_.begin(), memBuf_.begin() + junkSize); + it += junkSize; + + if (it == itEnd) + break; + //-------------------------------------------------------------------- + memBuf_.resize(blockSize); + const size_t bytesRead = tryRead(&memBuf_[0], blockSize); //throw SysError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0 + memBuf_.resize(bytesRead); if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesRead); //throw X if (bytesRead == 0) //end of file - bytesToRead = std::min(bytesToRead, memBuf_.size()); + break; } - - std::copy(memBuf_.begin(), memBuf_.begin() + bytesToRead, static_cast<char*>(buffer)); - memBuf_.erase(memBuf_.begin(), memBuf_.begin() + bytesToRead); - return bytesToRead; + return it - static_cast<char*>(buffer); } size_t getBlockSize() const { return 64 * 1024; } diff --git a/wx+/popup_dlg.cpp b/wx+/popup_dlg.cpp index 5eeebf5e..19c721ad 100755 --- a/wx+/popup_dlg.cpp +++ b/wx+/popup_dlg.cpp @@ -72,12 +72,15 @@ void setBestInitialSize(wxTextCtrl& ctrl, const wxString& text, wxSize maxSize) class zen::StandardPopupDialog : public PopupDialogGenerated { public: - StandardPopupDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg) : + StandardPopupDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, + const wxString& labelAccept, // + const wxString& labelAcceptAll, //optional, except: if "decline" or "acceptAll" is passed, so must be "accept" + const wxString& labelDecline) : // PopupDialogGenerated(parent), - checkBoxValue_(cfg.checkBoxValue) + checkBoxValue_(cfg.checkBoxValue), + buttonToDisableWhenChecked_(cfg.buttonToDisableWhenChecked) { - wxBitmap iconTmp; wxString titleTmp; switch (type) @@ -153,152 +156,153 @@ public: m_checkBoxCustom->Hide(); Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(StandardPopupDialog::OnKeyPressed), nullptr, this); //dialog-specific local key events - } - -private: - void OnClose (wxCloseEvent& event) override { EndModal(static_cast<int>(ConfirmationButton3::CANCEL)); } - void OnCancel(wxCommandEvent& event) override { EndModal(static_cast<int>(ConfirmationButton3::CANCEL)); } - void OnKeyPressed(wxKeyEvent& event) - { - switch (event.GetKeyCode()) + //------------------------------------------------------------------------------ + StdButtons stdBtns; + stdBtns.setAffirmative(m_buttonAccept); + if (labelAccept.empty()) //notification dialog { - case WXK_RETURN: - case WXK_NUMPAD_ENTER: + assert(labelAcceptAll.empty() && labelDecline.empty()); + m_buttonAccept->SetLabel(_("Close")); //UX Guide: use "Close" for errors, warnings and windows in which users can't make changes (no ampersand!) + m_buttonAcceptAll->Hide(); + m_buttonDecline->Hide(); + m_buttonCancel->Hide(); + } + else + { + assert(contains(labelAccept, L"&")); + m_buttonAccept->SetLabel(labelAccept); + stdBtns.setCancel(m_buttonCancel); + + if (labelDecline.empty()) //confirmation dialog(YES/CANCEL) + m_buttonDecline->Hide(); + else //confirmation dialog(YES/NO/CANCEL) { - wxCommandEvent dummy(wxEVT_COMMAND_BUTTON_CLICKED); - OnButtonAffirmative(dummy); - return; + assert(contains(labelDecline, L"&")); + m_buttonDecline->SetLabel(labelDecline); + stdBtns.setNegative(m_buttonDecline); + + //m_buttonConfirm->SetId(wxID_IGNORE); -> setting id after button creation breaks "mouse snap to" functionality + //m_buttonDecline->SetId(wxID_RETRY); -> also wxWidgets docs seem to hide some info: "Normally, the identifier should be provided on creation and should not be modified subsequently." } - case WXK_ESCAPE: //handle case where cancel button is hidden! - EndModal(static_cast<int>(ConfirmationButton3::CANCEL)); - return; + if (labelAcceptAll.empty()) + m_buttonAcceptAll->Hide(); + else + { + assert(contains(labelAcceptAll, L"&")); + m_buttonAcceptAll->SetLabel(labelAcceptAll); + stdBtns.setAffirmativeAll(m_buttonAcceptAll); + } } - event.Skip(); + updateGui(); + + //set std order after button visibility was set + setStandardButtonLayout(*bSizerStdButtons, stdBtns); + + setAsStandard(*m_buttonAccept); + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + Center(); //needs to be re-applied after a dialog size change! } - void OnButtonAffirmative(wxCommandEvent& event) override +private: + void OnClose (wxCloseEvent& event) override { EndModal(static_cast<int>(ConfirmationButton3::CANCEL)); } + void OnCancel(wxCommandEvent& event) override { EndModal(static_cast<int>(ConfirmationButton3::CANCEL)); } + + void OnButtonAccept(wxCommandEvent& event) override { if (checkBoxValue_) *checkBoxValue_ = m_checkBoxCustom->GetValue(); - EndModal(static_cast<int>(ConfirmationButton3::DO_IT)); + EndModal(static_cast<int>(ConfirmationButton3::ACCEPT)); } - void OnButtonNegative(wxCommandEvent& event) override + void OnButtonAcceptAll(wxCommandEvent& event) override { if (checkBoxValue_) *checkBoxValue_ = m_checkBoxCustom->GetValue(); - EndModal(static_cast<int>(ConfirmationButton3::DONT_DO_IT)); - } - - bool* checkBoxValue_; -}; - - -namespace -{ -class NotificationDialog : public StandardPopupDialog -{ -public: - NotificationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg) : - StandardPopupDialog(parent, type, cfg) - { - m_buttonAffirmative->SetLabel(_("Close")); //UX Guide: use "Close" for errors, warnings and windows in which users can't make changes (no ampersand!) - m_buttonNegative->Hide(); - m_buttonCancel->Hide(); - - //set std order after button visibility was set - setStandardButtonLayout(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonAffirmative)); - setAsStandard(*m_buttonAffirmative); - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - Center(); //needs to be re-applied after a dialog size change! + EndModal(static_cast<int>(ConfirmationButton3::ACCEPT_ALL)); } -}; - -class ConfirmationDialog : public StandardPopupDialog -{ -public: - ConfirmationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelDoIt) : - StandardPopupDialog(parent, type, cfg) + void OnButtonDecline(wxCommandEvent& event) override { - assert(contains(labelDoIt, L"&")); - m_buttonAffirmative->SetLabel(labelDoIt); - m_buttonNegative->Hide(); - - //set std order after button visibility was set - setStandardButtonLayout(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonAffirmative).setCancel(m_buttonCancel)); - setAsStandard(*m_buttonAffirmative); - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - Center(); //needs to be re-applied after a dialog size change! + if (checkBoxValue_) + *checkBoxValue_ = m_checkBoxCustom->GetValue(); + EndModal(static_cast<int>(ConfirmationButton3::DECLINE)); } -}; -} -class zen::ConfirmationDialog3 : public StandardPopupDialog -{ -public: - ConfirmationDialog3(wxWindow* parent, DialogInfoType type, const PopupDialogCfg3& cfg, const wxString& labelDoIt, const wxString& labelDontDoIt) : - StandardPopupDialog(parent, type, cfg.pdCfg_), - buttonToDisableWhenChecked_(cfg.buttonToDisableWhenChecked_) + void OnKeyPressed(wxKeyEvent& event) { - assert(contains(labelDoIt, L"&")); - assert(contains(labelDontDoIt, L"&")); - m_buttonAffirmative->SetLabel(labelDoIt); - m_buttonNegative ->SetLabel(labelDontDoIt); - - //m_buttonAffirmative->SetId(wxID_IGNORE); -> setting id after button creation breaks "mouse snap to" functionality - //m_buttonNegative ->SetId(wxID_RETRY); -> also wxWidgets docs seem to hide some info: "Normally, the identifier should be provided on creation and should not be modified subsequently." - - updateGui(); + switch (event.GetKeyCode()) + { + case WXK_RETURN: + case WXK_NUMPAD_ENTER: + { + wxCommandEvent dummy(wxEVT_COMMAND_BUTTON_CLICKED); + OnButtonAccept(dummy); + return; + } - //set std order after button visibility was set - setStandardButtonLayout(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonAffirmative).setNegative(m_buttonNegative).setCancel(m_buttonCancel)); - setAsStandard(*m_buttonAffirmative); - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() - Center(); //needs to be re-applied after a dialog size change! + case WXK_ESCAPE: //handle case where cancel button is hidden! + EndModal(static_cast<int>(ConfirmationButton3::CANCEL)); + return; + } + event.Skip(); } -private: void OnCheckBoxClick(wxCommandEvent& event) override { updateGui(); event.Skip(); } void updateGui() { switch (buttonToDisableWhenChecked_) { - case ConfirmationButton3::DO_IT: - m_buttonAffirmative->Enable(!m_checkBoxCustom->GetValue()); + case QuestionButton2::YES: + m_buttonAccept ->Enable(!m_checkBoxCustom->GetValue()); + m_buttonAcceptAll->Enable(!m_checkBoxCustom->GetValue()); break; - case ConfirmationButton3::DONT_DO_IT: - m_buttonNegative->Enable(!m_checkBoxCustom->GetValue()); + case QuestionButton2::NO: + m_buttonDecline->Enable(!m_checkBoxCustom->GetValue()); break; - case ConfirmationButton3::CANCEL: + case QuestionButton2::CANCEL: break; } } - const ConfirmationButton3 buttonToDisableWhenChecked_; + bool* checkBoxValue_; + const QuestionButton2 buttonToDisableWhenChecked_; }; //######################################################################################## void zen::showNotificationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg) { - NotificationDialog dlg(parent, type, cfg); + StandardPopupDialog dlg(parent, type, cfg, wxString() /*labelAccept*/, wxString() /*labelAcceptAll*/, wxString() /*labelDecline*/); dlg.ShowModal(); } -ConfirmationButton zen::showConfirmationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelDoIt) +ConfirmationButton zen::showConfirmationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelAccept) { - ConfirmationDialog dlg(parent, type, cfg, labelDoIt); + StandardPopupDialog dlg(parent, type, cfg, labelAccept, wxString() /*labelAcceptAll*/, wxString() /*labelDecline*/); return static_cast<ConfirmationButton>(dlg.ShowModal()); } -ConfirmationButton3 zen::showConfirmationDialog3(wxWindow* parent, DialogInfoType type, const PopupDialogCfg3& cfg, const wxString& labelDoIt, const wxString& labelDontDoIt) +ConfirmationButton2 zen::showConfirmationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelAccept, const wxString& labelAcceptAll) { - ConfirmationDialog3 dlg(parent, type, cfg, labelDoIt, labelDontDoIt); + StandardPopupDialog dlg(parent, type, cfg, labelAccept, labelAcceptAll, wxString() /*labelDecline*/); + return static_cast<ConfirmationButton2>(dlg.ShowModal()); +} + + +ConfirmationButton3 zen::showConfirmationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelAccept, const wxString& labelAcceptAll, const wxString& labelDecline) +{ + StandardPopupDialog dlg(parent, type, cfg, labelAccept, labelAcceptAll, labelDecline); return static_cast<ConfirmationButton3>(dlg.ShowModal()); } + + +QuestionButton2 zen::showQuestionDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelYes, const wxString& labelNo) +{ + StandardPopupDialog dlg(parent, type, cfg, labelYes, wxString() /*labelAcceptAll*/, labelNo); + return static_cast<QuestionButton2>(dlg.ShowModal()); +} diff --git a/wx+/popup_dlg.h b/wx+/popup_dlg.h index cc6ffee8..771f5cb0 100755 --- a/wx+/popup_dlg.h +++ b/wx+/popup_dlg.h @@ -18,7 +18,6 @@ namespace zen //this module requires error, warning and info image files in resources.zip, see <wx+/image_resources.h> struct PopupDialogCfg; -struct PopupDialogCfg3; enum class DialogInfoType { @@ -29,24 +28,37 @@ enum class DialogInfoType enum class ConfirmationButton3 { - DO_IT, - DONT_DO_IT, - CANCEL + ACCEPT, + ACCEPT_ALL, + DECLINE, + CANCEL, }; - enum class ConfirmationButton { - DO_IT = static_cast<int>(ConfirmationButton3::DO_IT ), //[!] - CANCEL = static_cast<int>(ConfirmationButton3::CANCEL), //Clang requires a "static_cast" + ACCEPT = static_cast<int>(ConfirmationButton3::ACCEPT), //[!] Clang requires a "static_cast" + CANCEL = static_cast<int>(ConfirmationButton3::CANCEL), // +}; +enum class ConfirmationButton2 +{ + ACCEPT = static_cast<int>(ConfirmationButton3::ACCEPT), + ACCEPT_ALL = static_cast<int>(ConfirmationButton3::ACCEPT_ALL), + CANCEL = static_cast<int>(ConfirmationButton3::CANCEL), +}; +enum class QuestionButton2 +{ + YES = static_cast<int>(ConfirmationButton3::ACCEPT), + NO = static_cast<int>(ConfirmationButton3::DECLINE), + CANCEL = static_cast<int>(ConfirmationButton3::CANCEL), }; -void showNotificationDialog (wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg); -ConfirmationButton showConfirmationDialog (wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelDoIt); -ConfirmationButton3 showConfirmationDialog3(wxWindow* parent, DialogInfoType type, const PopupDialogCfg3& cfg, const wxString& labelDoIt, const wxString& labelDontDoIt); +void showNotificationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg); +ConfirmationButton showConfirmationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelAccept); +ConfirmationButton2 showConfirmationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelAccept, const wxString& labelAcceptAll); +ConfirmationButton3 showConfirmationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelAccept, const wxString& labelAcceptAll, const wxString& labelDecline); +QuestionButton2 showQuestionDialog (wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelYes, const wxString& labelNo); //---------------------------------------------------------------------------------------------------------------- class StandardPopupDialog; -class ConfirmationDialog3; struct PopupDialogCfg { @@ -54,7 +66,13 @@ struct PopupDialogCfg PopupDialogCfg& setTitle (const wxString& label) { title = label; return *this; } PopupDialogCfg& setMainInstructions (const wxString& label) { textMain = label; return *this; } //set at least one of these! PopupDialogCfg& setDetailInstructions(const wxString& label) { textDetail = label; return *this; } // - PopupDialogCfg& setCheckBox(bool& value, const wxString& label) { checkBoxValue = &value; checkBoxLabel = label; return *this; } + PopupDialogCfg& setCheckBox(bool& value, const wxString& label, QuestionButton2 disableWhenChecked = QuestionButton2::CANCEL) + { + checkBoxValue = &value; + checkBoxLabel = label; + buttonToDisableWhenChecked = disableWhenChecked; + return *this; + } private: friend class StandardPopupDialog; @@ -65,29 +83,7 @@ private: wxString textDetail; bool* checkBoxValue = nullptr; //in/out wxString checkBoxLabel; -}; - - -struct PopupDialogCfg3 -{ - PopupDialogCfg3& setIcon (const wxBitmap& bmp ) { pdCfg_.setIcon (bmp); return *this; } - PopupDialogCfg3& setTitle (const wxString& label) { pdCfg_.setTitle (label); return *this; } - PopupDialogCfg3& setMainInstructions (const wxString& label) { pdCfg_.setMainInstructions (label); return *this; } //set at least one of these! - PopupDialogCfg3& setDetailInstructions(const wxString& label) { pdCfg_.setDetailInstructions(label); return *this; } // - PopupDialogCfg3& setCheckBox(bool& value, const wxString& label) { pdCfg_.setCheckBox(value, label); return *this; } - PopupDialogCfg3& setCheckBox(bool& value, const wxString& label, ConfirmationButton3 disableWhenChecked) - { - assert(disableWhenChecked != ConfirmationButton3::CANCEL); - setCheckBox(value, label); - buttonToDisableWhenChecked_ = disableWhenChecked; - return *this; - } - -private: - friend class ConfirmationDialog3; - - PopupDialogCfg pdCfg_; - ConfirmationButton3 buttonToDisableWhenChecked_ = ConfirmationButton3::CANCEL; + QuestionButton2 buttonToDisableWhenChecked = QuestionButton2::CANCEL; }; } diff --git a/wx+/popup_dlg_generated.cpp b/wx+/popup_dlg_generated.cpp index 2c2da65c..c8c504ae 100755 --- a/wx+/popup_dlg_generated.cpp +++ b/wx+/popup_dlg_generated.cpp @@ -11,85 +11,89 @@ PopupDialogGenerated::PopupDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer24; - bSizer24 = new wxBoxSizer( wxVERTICAL ); - - m_panel33 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel33->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer165; - bSizer165 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapMsgType = new wxStaticBitmap( m_panel33, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer165->Add( m_bitmapMsgType, 0, wxALL, 10 ); - - wxBoxSizer* bSizer16; - bSizer16 = new wxBoxSizer( wxVERTICAL ); - - - bSizer16->Add( 0, 10, 0, 0, 5 ); - - m_staticTextMain = new wxStaticText( m_panel33, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextMain->Wrap( -1 ); - bSizer16->Add( m_staticTextMain, 0, wxRIGHT, 10 ); - - - bSizer16->Add( 0, 5, 0, 0, 5 ); - - m_textCtrlTextDetail = new wxTextCtrl( m_panel33, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); - bSizer16->Add( m_textCtrlTextDetail, 1, wxEXPAND, 5 ); - - - bSizer165->Add( bSizer16, 1, wxEXPAND, 5 ); - - - m_panel33->SetSizer( bSizer165 ); - m_panel33->Layout(); - bSizer165->Fit( m_panel33 ); - bSizer24->Add( m_panel33, 1, wxEXPAND, 5 ); - - m_staticline6 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer24->Add( m_staticline6, 0, wxEXPAND, 5 ); - - wxBoxSizer* bSizer25; - bSizer25 = new wxBoxSizer( wxVERTICAL ); - - m_checkBoxCustom = new wxCheckBox( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer25->Add( m_checkBoxCustom, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonAffirmative = new wxButton( this, wxID_YES, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonAffirmative, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - m_buttonNegative = new wxButton( this, wxID_NO, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonNegative, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer25->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); - - - bSizer24->Add( bSizer25, 0, wxEXPAND, 5 ); - - - this->SetSizer( bSizer24 ); - this->Layout(); - bSizer24->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( PopupDialogGenerated::OnClose ) ); - m_checkBoxCustom->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCheckBoxClick ), NULL, this ); - m_buttonAffirmative->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonAffirmative ), NULL, this ); - m_buttonNegative->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonNegative ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCancel ), NULL, this ); + this->SetSizeHints( wxSize( -1, -1 ), wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer24; + bSizer24 = new wxBoxSizer( wxVERTICAL ); + + m_panel33 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel33->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer165; + bSizer165 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapMsgType = new wxStaticBitmap( m_panel33, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 ); + bSizer165->Add( m_bitmapMsgType, 0, wxALL, 10 ); + + wxBoxSizer* bSizer16; + bSizer16 = new wxBoxSizer( wxVERTICAL ); + + + bSizer16->Add( 0, 10, 0, 0, 5 ); + + m_staticTextMain = new wxStaticText( m_panel33, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextMain->Wrap( -1 ); + bSizer16->Add( m_staticTextMain, 0, wxRIGHT, 10 ); + + + bSizer16->Add( 0, 5, 0, 0, 5 ); + + m_textCtrlTextDetail = new wxTextCtrl( m_panel33, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); + bSizer16->Add( m_textCtrlTextDetail, 1, wxEXPAND, 5 ); + + + bSizer165->Add( bSizer16, 1, wxEXPAND, 5 ); + + + m_panel33->SetSizer( bSizer165 ); + m_panel33->Layout(); + bSizer165->Fit( m_panel33 ); + bSizer24->Add( m_panel33, 1, wxEXPAND, 5 ); + + m_staticline6 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer24->Add( m_staticline6, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer25; + bSizer25 = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxCustom = new wxCheckBox( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer25->Add( m_checkBoxCustom, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonAccept = new wxButton( this, wxID_YES, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); + bSizerStdButtons->Add( m_buttonAccept, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_buttonAcceptAll = new wxButton( this, wxID_YESTOALL, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); + bSizerStdButtons->Add( m_buttonAcceptAll, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 ); + + m_buttonDecline = new wxButton( this, wxID_NO, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); + bSizerStdButtons->Add( m_buttonDecline, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer25->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + + + bSizer24->Add( bSizer25, 0, wxEXPAND, 5 ); + + + this->SetSizer( bSizer24 ); + this->Layout(); + bSizer24->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( PopupDialogGenerated::OnClose ) ); + m_checkBoxCustom->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCheckBoxClick ), NULL, this ); + m_buttonAccept->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonAccept ), NULL, this ); + m_buttonAcceptAll->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonAcceptAll ), NULL, this ); + m_buttonDecline->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonDecline ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCancel ), NULL, this ); } PopupDialogGenerated::~PopupDialogGenerated() diff --git a/wx+/popup_dlg_generated.h b/wx+/popup_dlg_generated.h index 51a045ad..896f8d0c 100755 --- a/wx+/popup_dlg_generated.h +++ b/wx+/popup_dlg_generated.h @@ -37,35 +37,37 @@ /////////////////////////////////////////////////////////////////////////////// /// Class PopupDialogGenerated /////////////////////////////////////////////////////////////////////////////// -class PopupDialogGenerated : public wxDialog +class PopupDialogGenerated : public wxDialog { - private: - - protected: - wxPanel* m_panel33; - wxStaticBitmap* m_bitmapMsgType; - wxStaticText* m_staticTextMain; - wxTextCtrl* m_textCtrlTextDetail; - wxStaticLine* m_staticline6; - wxCheckBox* m_checkBoxCustom; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonAffirmative; - wxButton* m_buttonNegative; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnCheckBoxClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnButtonAffirmative( wxCommandEvent& event ) { event.Skip(); } - virtual void OnButtonNegative( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - - public: - - PopupDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~PopupDialogGenerated(); - +private: + +protected: + wxPanel* m_panel33; + wxStaticBitmap* m_bitmapMsgType; + wxStaticText* m_staticTextMain; + wxTextCtrl* m_textCtrlTextDetail; + wxStaticLine* m_staticline6; + wxCheckBox* m_checkBoxCustom; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonAccept; + wxButton* m_buttonAcceptAll; + wxButton* m_buttonDecline; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnCheckBoxClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnButtonAccept( wxCommandEvent& event ) { event.Skip(); } + virtual void OnButtonAcceptAll( wxCommandEvent& event ) { event.Skip(); } + virtual void OnButtonDecline( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + +public: + + PopupDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~PopupDialogGenerated(); + }; #endif //__POPUP_DLG_GENERATED_H__ diff --git a/wx+/std_button_layout.h b/wx+/std_button_layout.h index 47eea3c6..fa4a269e 100755 --- a/wx+/std_button_layout.h +++ b/wx+/std_button_layout.h @@ -16,11 +16,13 @@ namespace zen { struct StdButtons { - StdButtons& setAffirmative (wxButton* btn) { btnYes = btn; return *this; } - StdButtons& setNegative (wxButton* btn) { btnNo = btn; return *this; } - StdButtons& setCancel (wxButton* btn) { btnCancel = btn; return *this; } + StdButtons& setAffirmative (wxButton* btn) { btnYes = btn; return *this; } + StdButtons& setAffirmativeAll(wxButton* btn) { btnYesAll = btn; return *this; } + StdButtons& setNegative (wxButton* btn) { btnNo = btn; return *this; } + StdButtons& setCancel (wxButton* btn) { btnCancel = btn; return *this; } wxButton* btnYes = nullptr; + wxButton* btnYesAll = nullptr; wxButton* btnNo = nullptr; wxButton* btnCancel = nullptr; }; @@ -64,6 +66,7 @@ void setStandardButtonLayout(wxBoxSizer& sizer, const StdButtons& buttons) }; detach(buttonsTmp.btnYes); + detach(buttonsTmp.btnYesAll); detach(buttonsTmp.btnNo); detach(buttonsTmp.btnCancel); @@ -106,11 +109,13 @@ void setStandardButtonLayout(wxBoxSizer& sizer, const StdButtons& buttons) sizer.Add(spaceRimH, 0); attach(buttonsTmp.btnNo); attach(buttonsTmp.btnCancel); + attach(buttonsTmp.btnYesAll); attach(buttonsTmp.btnYes); sizer.Add(spaceRimH, 0); - assert(buttonsTmp.btnCancel || buttonsTmp.btnYes); //OS X: there should be at least one button following the gap after the "dangerous" no-button + //OS X: there should be at least one button following the gap after the "dangerous" no-button + assert(buttonsTmp.btnYes); } } diff --git a/zen/file_access.cpp b/zen/file_access.cpp index 7d57045d..69b6c388 100755 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -345,7 +345,7 @@ void zen::renameFile(const Zstring& pathSource, const Zstring& pathTarget) //thr const Zstring fileNameTrg = afterLast (pathTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); const Zstring parentPathSrc = beforeLast(pathSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); const Zstring parentPathTrg = beforeLast(pathTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); - //some (broken) devices may fail to rename case directly: + //some (broken) devices may fail to rename case directly: if (equalFilePath(parentPathSrc, parentPathTrg)) { if (fileNameSrc == fileNameTrg) @@ -385,12 +385,12 @@ void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTim if (procSl == ProcSymlink::FOLLOW) { //hell knows why files on gvfs-mounted Samba shares fail to open(O_WRONLY) returning EOPNOTSUPP: - //http://www.freefilesync.org/forum/viewtopic.php?t=2803 => utimensat() works + //http://www.freefilesync.org/forum/viewtopic.php?t=2803 => utimensat() works (but not for gvfs SFTP) if (::utimensat(AT_FDCWD, itemPath.c_str(), newTimes, 0) == 0) return; //in other cases utimensat() returns EINVAL for CIFS/NTFS drives, but open+futimens works: http://www.freefilesync.org/forum/viewtopic.php?t=387 - const int fdFile = ::open(itemPath.c_str(), O_WRONLY); + const int fdFile = ::open(itemPath.c_str(), O_WRONLY | O_APPEND); //2017-07-04: O_WRONLY | O_APPEND seems to avoid EOPNOTSUPP on gvfs SFTP! if (fdFile == -1) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"open"); ZEN_ON_SCOPE_EXIT(::close(fdFile)); @@ -515,7 +515,7 @@ void zen::createDirectory(const Zstring& dirPath) //throw FileError, ErrorTarget { const mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO; //0777, default for newly created directories - if (::mkdir(dirPath.c_str(), mode) != 0) + if (::mkdir(dirPath.c_str(), mode) != 0) { const int lastError = errno; //copy before directly or indirectly making other system calls! const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(dirPath)); @@ -537,7 +537,7 @@ void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw File try { - createDirectory(dirPath); //throw FileError, ErrorTargetExisting + createDirectory(dirPath); //throw FileError, ErrorTargetExisting } catch (FileError&) { @@ -628,7 +628,10 @@ FileCopyResult copyFileOsSpecific(const Zstring& sourceFile, //throw FileError, FileOutput fileOut(fdTarget, targetFile, IOCallbackDivider(notifyUnbufferedIO, totalUnbufferedIO)); //pass ownership bufferedStreamCopy(fileIn, fileOut); //throw FileError, X + + //flush intermediate buffers before fiddling with the raw file handle fileOut.flushBuffers(); //throw FileError, X + struct ::stat targetInfo = {}; if (::fstat(fileOut.getHandle(), &targetInfo) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), L"fstat"); diff --git a/zen/file_io.cpp b/zen/file_io.cpp index d291e741..60023849 100755 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -130,22 +130,29 @@ size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError; m size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, X; return "bytesToRead" bytes unless end of stream! { const size_t blockSize = getBlockSize(); - - while (memBuf_.size() < bytesToRead) + assert(memBuf_.size() <= blockSize); + char* it = static_cast<char*>(buffer); + char* const itEnd = it + bytesToRead; + for (;;) { - memBuf_.resize(memBuf_.size() + blockSize); - const size_t bytesRead = tryRead(&*(memBuf_.end() - blockSize), blockSize); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0 - memBuf_.resize(memBuf_.size() - blockSize + bytesRead); //caveat: unsigned arithmetics + const size_t junkSize = std::min(static_cast<size_t>(itEnd - it), memBuf_.size()); + std::copy (memBuf_.begin(), memBuf_.begin() + junkSize, it); + memBuf_.erase(memBuf_.begin(), memBuf_.begin() + junkSize); + it += junkSize; + + if (it == itEnd) + break; + //-------------------------------------------------------------------- + memBuf_.resize(blockSize); + const size_t bytesRead = tryRead(&memBuf_[0], blockSize); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0 + memBuf_.resize(bytesRead); if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesRead); //throw X if (bytesRead == 0) //end of file - bytesToRead = std::min(bytesToRead, memBuf_.size()); + break; } - - std::copy(memBuf_.begin(), memBuf_.begin() + bytesToRead, static_cast<char*>(buffer)); - memBuf_.erase(memBuf_.begin(), memBuf_.begin() + bytesToRead); - return bytesToRead; + return it - static_cast<char*>(buffer); } //---------------------------------------------------------------------------------------------------- @@ -224,16 +231,21 @@ size_t FileOutput::tryWrite(const void* buffer, size_t bytesToWrite) //throw Fil void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileError, X { - auto bufFirst = static_cast<const char*>(buffer); - memBuf_.insert(memBuf_.end(), bufFirst, bufFirst + bytesToWrite); - const size_t blockSize = getBlockSize(); - size_t bytesRemaining = memBuf_.size(); - ZEN_ON_SCOPE_EXIT(memBuf_.erase(memBuf_.begin(), memBuf_.end() - bytesRemaining)); - while (bytesRemaining >= blockSize) + assert(memBuf_.size() <= blockSize); + const char* it = static_cast<const char*>(buffer); + const char* const itEnd = it + bytesToWrite; + for (;;) { - const size_t bytesWritten = tryWrite(&*(memBuf_.end() - bytesRemaining), blockSize); //throw FileError; may return short! CONTRACT: bytesToWrite > 0 - bytesRemaining -= bytesWritten; + const size_t junkSize = std::min(static_cast<size_t>(itEnd - it), blockSize - memBuf_.size()); + memBuf_.insert(memBuf_.end(), it, it + junkSize); + it += junkSize; + + if (it == itEnd) + return; + //-------------------------------------------------------------------- + const size_t bytesWritten = tryWrite(&memBuf_[0], blockSize); //throw FileError; may return short! CONTRACT: bytesToWrite > 0 + memBuf_.erase(memBuf_.begin(), memBuf_.begin() + bytesWritten); if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesWritten); //throw X! } } @@ -241,12 +253,11 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro void FileOutput::flushBuffers() //throw FileError, X { - size_t bytesRemaining = memBuf_.size(); - ZEN_ON_SCOPE_EXIT(memBuf_.erase(memBuf_.begin(), memBuf_.end() - bytesRemaining)); - while (bytesRemaining > 0) + assert(memBuf_.size() <= getBlockSize()); + while (!memBuf_.empty()) { - const size_t bytesWritten = tryWrite(&*(memBuf_.end() - bytesRemaining), bytesRemaining); //throw FileError; may return short! CONTRACT: bytesToWrite > 0 - bytesRemaining -= bytesWritten; + const size_t bytesWritten = tryWrite(&memBuf_[0], memBuf_.size()); //throw FileError; may return short! CONTRACT: bytesToWrite > 0 + memBuf_.erase(memBuf_.begin(), memBuf_.begin() + bytesWritten); if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesWritten); //throw X! } } @@ -20,11 +20,11 @@ namespace zen inline std::string generateGUID() //creates a 16-byte GUID { - //perf: generator: 0.38ms per creation; - // retrieve GUID: 0.13s per call + //perf: generator: 0.38ms per creation; + // retrieve GUID: 0.13s per call //generator is only thread-safe like an int => keep thread-local thread_local boost::uuids::random_generator gen; - const boost::uuids::uuid nativeRep = gen(); + const boost::uuids::uuid nativeRep = gen(); return std::string(nativeRep.begin(), nativeRep.end()); } } diff --git a/zen/scope_guard.h b/zen/scope_guard.h index b336ad53..f4ffc92b 100755 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -13,7 +13,7 @@ //std::uncaught_exceptions() currently unsupported on GCC and Clang => clean up ASAP - static_assert(__GNUC__ < 6 || (__GNUC__ == 6 && (__GNUC_MINOR__ < 3 || (__GNUC_MINOR__ == 3 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support"); + static_assert(__GNUC__ < 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ < 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support"); namespace __cxxabiv1 { @@ -21,7 +21,8 @@ struct __cxa_eh_globals; extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept; } -inline int getUncaughtExceptionCount() +inline +int getUncaughtExceptionCount() { return *(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*))); } diff --git a/zen/serialize.h b/zen/serialize.h index 81d2d1ef..8f7e813c 100755 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -36,20 +36,20 @@ public: using iterator = std::vector<char>::iterator; using const_iterator = std::vector<char>::const_iterator; - iterator begin() { return buffer->begin(); } - iterator end () { return buffer->end (); } + iterator begin() { return buffer_->begin(); } + iterator end () { return buffer_->end (); } - const_iterator begin() const { return buffer->begin(); } - const_iterator end () const { return buffer->end (); } + const_iterator begin() const { return buffer_->begin(); } + const_iterator end () const { return buffer_->end (); } - void resize(size_t len) { buffer->resize(len); } - size_t size() const { return buffer->size(); } - bool empty() const { return buffer->empty(); } + void resize(size_t len) { buffer_->resize(len); } + size_t size() const { return buffer_->size(); } + bool empty() const { return buffer_->empty(); } - inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return *lhs.buffer == *rhs.buffer; } + inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return *lhs.buffer_ == *rhs.buffer_; } private: - std::shared_ptr<std::vector<char>> buffer { std::make_shared<std::vector<char>>() }; //always bound! + std::shared_ptr<std::vector<char>> buffer_ { std::make_shared<std::vector<char>>() }; //always bound! //perf: shared_ptr indirection irrelevant: less than 1% slower! }; @@ -148,9 +148,8 @@ struct MemoryStreamOut void write(const void* buffer, size_t bytesToWrite) { static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes - const size_t oldSize = buffer_.size(); - buffer_.resize(oldSize + bytesToWrite); - std::copy(static_cast<const char*>(buffer), static_cast<const char*>(buffer) + bytesToWrite, buffer_.begin() + oldSize); + buffer_.resize(buffer_.size() + bytesToWrite); + std::copy(static_cast<const char*>(buffer), static_cast<const char*>(buffer) + bytesToWrite, buffer_.end() - bytesToWrite); } const BinContainer& ref() const { return buffer_; } diff --git a/zen/thread.h b/zen/thread.h index 41261f1e..85e64493 100755 --- a/zen/thread.h +++ b/zen/thread.h @@ -12,6 +12,7 @@ #include "scope_guard.h" #include "type_traits.h" #include "optional.h" + #include <sys/prctl.h> namespace zen @@ -46,7 +47,7 @@ public: private: std::thread stdThread_; - std::shared_ptr<InterruptionStatus> intStatus_; + std::shared_ptr<InterruptionStatus> intStatus_{ std::make_shared<InterruptionStatus>() }; std::future<void> threadCompleted_; }; @@ -59,6 +60,7 @@ void interruptibleWait(std::condition_variable& cv, std::unique_lock<std::mutex> template <class Rep, class Period> void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime); //throw ThreadInterruption +void setCurrentThreadName(const char* threadName); uint64_t getThreadId(); //simple integer thread id, unlike boost::thread::id: https://svn.boost.org/trac/boost/ticket/5754 @@ -121,7 +123,7 @@ public: template <class Function> void access(Function fun) { - std::lock_guard<std::mutex> dummy(lockValue); + std::lock_guard<std::mutex> dummy(lockValue_); fun(value_); } @@ -129,7 +131,7 @@ private: Protected (const Protected&) = delete; Protected& operator=(const Protected&) = delete; - std::mutex lockValue; + std::mutex lockValue_; T value_{}; }; @@ -267,24 +269,24 @@ public: //context of InterruptibleThread instance: void interrupt() { - interrupted = true; + interrupted_ = true; { - std::lock_guard<std::mutex> dummy(lockSleep); //needed! makes sure the following signal is not lost! + std::lock_guard<std::mutex> dummy(lockSleep_); //needed! makes sure the following signal is not lost! //usually we'd make "interrupted" non-atomic, but this is already given due to interruptibleWait() handling } - conditionSleepInterruption.notify_all(); + conditionSleepInterruption_.notify_all(); - std::lock_guard<std::mutex> dummy(lockConditionPtr); - if (activeCondition) - activeCondition->notify_all(); //signal may get lost! + std::lock_guard<std::mutex> dummy(lockConditionPtr_); + if (activeCondition_) + activeCondition_->notify_all(); //signal may get lost! //alternative design locking the cv's mutex here could be dangerous: potential for dead lock! } //context of worker thread: void checkInterruption() //throw ThreadInterruption { - if (interrupted) + if (interrupted_) throw ThreadInterruption(); } @@ -296,7 +298,7 @@ public: ZEN_ON_SCOPE_EXIT(setConditionVar(nullptr)); //"interrupted" is not protected by cv's mutex => signal may get lost!!! => add artifical time out to mitigate! CPU: 0.25% vs 0% for longer time out! - while (!cv.wait_for(lock, std::chrono::milliseconds(1), [&] { return this->interrupted || pred(); })) + while (!cv.wait_for(lock, std::chrono::milliseconds(1), [&] { return this->interrupted_ || pred(); })) ; checkInterruption(); //throw ThreadInterruption @@ -306,26 +308,26 @@ public: template <class Rep, class Period> void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime) //throw ThreadInterruption { - std::unique_lock<std::mutex> lock(lockSleep); - if (conditionSleepInterruption.wait_for(lock, relTime, [this] { return static_cast<bool>(this->interrupted); })) + std::unique_lock<std::mutex> lock(lockSleep_); + if (conditionSleepInterruption_.wait_for(lock, relTime, [this] { return static_cast<bool>(this->interrupted_); })) throw ThreadInterruption(); } private: void setConditionVar(std::condition_variable* cv) { - std::lock_guard<std::mutex> dummy(lockConditionPtr); - activeCondition = cv; + std::lock_guard<std::mutex> dummy(lockConditionPtr_); + activeCondition_ = cv; } - std::atomic<bool> interrupted{ false }; //std:atomic is uninitialized by default!!! + std::atomic<bool> interrupted_{ false }; //std:atomic is uninitialized by default!!! //"The default constructor is trivial: no initialization takes place other than zero initialization of static and thread-local objects." - std::condition_variable* activeCondition = nullptr; - std::mutex lockConditionPtr; //serialize pointer access (only!) + std::condition_variable* activeCondition_ = nullptr; + std::mutex lockConditionPtr_; //serialize pointer access (only!) - std::condition_variable conditionSleepInterruption; - std::mutex lockSleep; + std::condition_variable conditionSleepInterruption_; + std::mutex lockSleep_; }; @@ -373,7 +375,7 @@ void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime) //thr template <class Function> inline -InterruptibleThread::InterruptibleThread(Function&& f) : intStatus_(std::make_shared<InterruptionStatus>()) +InterruptibleThread::InterruptibleThread(Function&& f) { std::promise<void> pFinished; threadCompleted_ = pFinished.get_future(); @@ -403,6 +405,14 @@ void InterruptibleThread::interrupt() { intStatus_->interrupt(); } inline +void setCurrentThreadName(const char* threadName) +{ + ::prctl(PR_SET_NAME, threadName, 0, 0, 0); + +} + + +inline uint64_t getThreadId() { //obviously "gettid()" is not available on Ubuntu/Debian/Suse => use the OpenSSL approach: |