diff options
author | Daniel Wilhelm <shieldwed@outlook.com> | 2018-05-09 00:09:55 +0200 |
---|---|---|
committer | Daniel Wilhelm <shieldwed@outlook.com> | 2018-05-09 00:09:55 +0200 |
commit | 9b623ea3943165fe7efb5e47a0b5b9452c1599e6 (patch) | |
tree | dde40e07e907ac6e0ca9ea32524f2cd4810d4be6 | |
parent | 9.7 (diff) | |
download | FreeFileSync-9b623ea3943165fe7efb5e47a0b5b9452c1599e6.tar.gz FreeFileSync-9b623ea3943165fe7efb5e47a0b5b9452c1599e6.tar.bz2 FreeFileSync-9b623ea3943165fe7efb5e47a0b5b9452c1599e6.zip |
9.8
178 files changed, 3205 insertions, 2725 deletions
diff --git a/Changelog.txt b/Changelog.txt index 60be1b62..b5f6d52c 100755 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,21 @@ +FreeFileSync 9.8 [2018-02-06] +----------------------------- +New option to auto-close progress dialog +Update last sync time if no differences found +Added 5 seconds countdown before shutdown/sleep +Preserve XML attribute creation order +Support HTTPS web accesses without redirect +Connect network share upon logon type not granted +Fixed invalid pointer error when reading MTP +Fixed temporary db file triggering RealTimeSync +Fixed runtime error during uninstallation +Continue status updates during sync cancellation +Log number of items found during comparison +Warn about outdated nviewH64.dll instead of crashing +Show default log file path when saving a batch job +Consider only full days for time since last sync + + FreeFileSync 9.7 [2018-01-12] ----------------------------- New configuration management panel diff --git a/FreeFileSync/Build/Help/html/schedule-a-batch-job.html b/FreeFileSync/Build/Help/html/schedule-a-batch-job.html index 200df6e0..fa9a1e2d 100755 --- a/FreeFileSync/Build/Help/html/schedule-a-batch-job.html +++ b/FreeFileSync/Build/Help/html/schedule-a-batch-job.html @@ -18,7 +18,7 @@ <li>By default, FreeFileSync will show a progress dialog during synchronization and will wait while the summary dialog is shown. If the progress dialog is not needed, enable checkbox <b>Run minimized</b> and - also set <i>When finished</i> to <b>Exit</b> if you want to skip the summary dialog at the end. + also set <b>Auto-Close</b> if you want to skip the summary dialog at the end. <br><br> <div class="bluebox"> diff --git a/FreeFileSync/Build/Help/images/setup-batch-job.png b/FreeFileSync/Build/Help/images/setup-batch-job.png Binary files differindex 76a27a12..1982a66b 100755 --- a/FreeFileSync/Build/Help/images/setup-batch-job.png +++ b/FreeFileSync/Build/Help/images/setup-batch-job.png diff --git a/FreeFileSync/Build/Languages/bulgarian.lng b/FreeFileSync/Build/Languages/bulgarian.lng index e82f9a12..2f34bbf0 100755 --- a/FreeFileSync/Build/Languages/bulgarian.lng +++ b/FreeFileSync/Build/Languages/bulgarian.lng @@ -7,6 +7,9 @@ <plural_definition>n == 1 ? 0 : 1</plural_definition> </header> +<source>Cannot set directory locks for the following folders:</source> +<target></target> + <source>Both sides have changed since last synchronization.</source> <target>Двете страни са променени след последната синхронизация.</target> @@ -53,17 +56,20 @@ <target>Проверява достъпността на кошчето за папка %x...</target> <source>The recycle bin is not supported by the following folders. Deleted or overwritten files will not be able to be restored:</source> -<target>Кошчето не се поддържа от следните папки. Изтритите или презаписани файлове няма да могат да се възстановяват:</target> +<target>Кошчето не се поддържа за следните папки. Изтритите или презаписани файлове няма да могат да се възстановяват:</target> <source>An exception occurred</source> <target>Възникна изключение</target> <source>A directory path is expected after %x.</source> -<target>Очаква път до директорията след %x.</target> +<target>Очаква се път до директория след %x.</target> <source>Syntax error</source> <target>Синтактична грешка</target> +<source>A left and a right directory path are expected after %x.</source> +<target>Очаква се път до лява и дясна директория след %x.</target> + <source>Cannot find file %x.</source> <target>Не е намерен файл %x.</target> @@ -456,9 +462,6 @@ Actual: %y bytes <source>Error parsing file %x, row %y, column %z.</source> <target>Грешка при анализ на файл %x, ред %y, колона %z.</target> -<source>Cannot set directory lock for %x.</source> -<target>Не може да заключи директорията за %x.</target> - <source> <pluralform>1 thread</pluralform> <pluralform>%x threads</pluralform> @@ -797,6 +800,27 @@ The command is triggered if: <source>Serious Error</source> <target>Сериозна грешка</target> +<source>Last session</source> +<target>Последна сесия</target> + +<source>Today</source> +<target>Днес</target> + +<source> +<pluralform>1 day</pluralform> +<pluralform>%x days</pluralform> +</source> +<target> +<pluralform>1 ден</pluralform> +<pluralform>%x дена</pluralform> +</target> + +<source>Name</source> +<target>Име</target> + +<source>Last sync</source> +<target>Последна синхронизация</target> + <source>Folder</source> <target>Папка</target> @@ -1324,6 +1348,9 @@ This guarantees a consistent state even in case of a serious error. <source>Activate offline</source> <target>Активирайте офлайн</target> +<source>Highlight configurations that have not been run for more than the following number of days:</source> +<target>Маркирай конфигурациите, които не са изпълнявани в течение на повече от следния брой дни:</target> + <source>Save as a Batch Job</source> <target>Запази като пакетна задача</target> @@ -1342,6 +1369,9 @@ This guarantees a consistent state even in case of a serious error. <source>FreeFileSync Donation Edition</source> <target>FreeFileSync-Дарителско-Издание</target> +<source>Highlight Configurations</source> +<target>Маркирай конфигурациите</target> + <source>&Options</source> <target>&Опции</target> @@ -1465,9 +1495,6 @@ This guarantees a consistent state even in case of a serious error. <source>Select time span...</source> <target>Избор на времеви интервал...</target> -<source>Last session</source> -<target>Последна сесия</target> - <source>Folder Comparison and Synchronization</source> <target>Сравняване и синхронизация на папки</target> @@ -1486,8 +1513,11 @@ This guarantees a consistent state even in case of a serious error. <source>Do&n't save</source> <target>&Не запазвай</target> -<source>Remove entry from list</source> -<target>Премахни позицията от списъка</target> +<source>Hide configuration</source> +<target>Скрий конфигурацията</target> + +<source>Highlight...</source> +<target>Маркирай...</target> <source>Clear filter</source> <target>Изчисти филтъра</target> @@ -1499,16 +1529,16 @@ This guarantees a consistent state even in case of a serious error. <target>Покажи файловете, съществуващи само отдясно</target> <source>Show files that are newer on left</source> -<target>Покажи по-новите файлове отляво</target> +<target>Покажи файловете от двете страни; файлът отляво е по-нов</target> <source>Show files that are newer on right</source> -<target>Покажи по-новите файлове отдясно</target> +<target>Покажи файловете от двете страни; файлът отдясно е по-нов</target> <source>Show files that are equal</source> <target>Покажи еднаквите файлове</target> <source>Show files that are different</source> -<target>Покажи различаващите се файлове</target> +<target>Покажи нееднаквите файлове</target> <source>Show conflicts</source> <target>Покажи конфликтите</target> @@ -1562,7 +1592,7 @@ This guarantees a consistent state even in case of a serious error. <target>Файловия списък е експортиран</target> <source>Searching for program updates...</source> -<target>Търсене на обновления на програмата...</target> +<target>Търси обновления на програмата...</target> <source>Paused</source> <target>Пауза</target> @@ -1571,7 +1601,7 @@ This guarantees a consistent state even in case of a serious error. <target>Инициализация...</target> <source>Scanning...</source> -<target>Търсене на файлове...</target> +<target>Търси файлове...</target> <source>Comparing content...</source> <target>Сравнява съдържанието на файлове...</target> @@ -1699,9 +1729,6 @@ This guarantees a consistent state even in case of a serious error. <source>Synchronization</source> <target>Синхронизиране</target> -<source>Today</source> -<target>Днес</target> - <source>This week</source> <target>Тази седмица</target> @@ -1771,9 +1798,6 @@ This guarantees a consistent state even in case of a serious error. <source>Files</source> <target>Файлове</target> -<source>Name</source> -<target>Име</target> - <source>Percentage</source> <target>Процент</target> @@ -1885,15 +1909,6 @@ This guarantees a consistent state even in case of a serious error. <pluralform>%x часа</pluralform> </target> -<source> -<pluralform>1 day</pluralform> -<pluralform>%x days</pluralform> -</source> -<target> -<pluralform>1 ден</pluralform> -<pluralform>%x дена</pluralform> -</target> - <source>Cannot set privilege %x.</source> <target>Не може да зададе привилегията %x.</target> @@ -1960,6 +1975,9 @@ This guarantees a consistent state even in case of a serious error. <source>Start menu</source> <target>В стартовото меню</target> +<source>Send To</source> +<target>Изпрати до</target> + <source>Registering FreeFileSync file extensions</source> <target>Регистриране на файлови разширения за FreeFileSync</target> @@ -1987,6 +2005,6 @@ This guarantees a consistent state even in case of a serious error. <source>Please choose the local installation type or select a different folder for installation.</source> <target>Моля, изберете локален тип инсталация или пък друга папка за инсталация.</target> -<source>The silent installation mode is only available in the FreeFileSync Donation Edition.</source> -<target>Режим на мълчаливо инсталиране е възможен само за FreeFileSync-Дарителско-Издание.</target> +<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source> +<target>Опцията %x инсталиране е възможна само за FreeFileSync-ДарителскоИздание.</target> diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng index acc4da40..1a0b13f9 100755 --- a/FreeFileSync/Build/Languages/german.lng +++ b/FreeFileSync/Build/Languages/german.lng @@ -118,6 +118,18 @@ <source>If this error is ignored the folders will be considered empty. Missing folders are created automatically when needed.</source> <target>Wird dieser Fehler ignoriert, werden die Ordner als leer angesehen. Fehlende Ordner werden bei Bedarf automatisch erstellt.</target> +<source>Comparison finished:</source> +<target>Vergleich abgeschlossen:</target> + +<source> +<pluralform>1 item found</pluralform> +<pluralform>%x items found</pluralform> +</source> +<target> +<pluralform>1 Element gefunden</pluralform> +<pluralform>%x Elemente gefunden</pluralform> +</target> + <source>File %x has an invalid date.</source> <target>Die Datei %x hat ein ungültiges Datum.</target> @@ -178,9 +190,6 @@ <source>Using non-default global settings:</source> <target>Nicht dem Standard entsprechende globale Einstellungen:</target> -<source>Starting comparison</source> -<target>Starte Vergleich</target> - <source>A folder input field is empty.</source> <target>Ein Ordnereingabefeld ist leer.</target> @@ -321,15 +330,15 @@ Tatsächlich: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>%x kann nicht in den Papierkorb verschoben werden.</target> +<source>Cannot find %x.</source> +<target>%x wurde nicht gefunden.</target> + <source>Cannot open file %x.</source> <target>Die Datei %x kann nicht geöffnet werden.</target> <source>Cannot find device %x.</source> <target>Das Gerät %x wurde nicht gefunden.</target> -<source>Cannot find %x.</source> -<target>%x wurde nicht gefunden.</target> - <source>Type of item %x is not supported:</source> <target>Der Typ des Elements %x wird nicht unterstützt:</target> @@ -435,6 +444,9 @@ Tatsächlich: %y bytes <source>Lock owner:</source> <target>Sperreigentümer:</target> +<source>Detecting abandoned lock...</source> +<target>Ermittle aufgegebene Sperre...</target> + <source> <pluralform>1 sec</pluralform> <pluralform>%x sec</pluralform> @@ -444,9 +456,6 @@ Tatsächlich: %y bytes <pluralform>%x Sek.</pluralform> </target> -<source>Detecting abandoned lock...</source> -<target>Ermittle aufgegebene Sperre...</target> - <source>Items processed:</source> <target>Verarbeitete Elemente:</target> @@ -501,9 +510,6 @@ Tatsächlich: %y bytes <source>Volume name %x is not part of file path %y.</source> <target>Laufwerksname %x ist kein Teil des Dateipfades %y.</target> -<source>Stop requested: Waiting for current operation to finish...</source> -<target>Unterbrechung wurde eingeleitet: Warte bis der aktuelle Vorgang beendet ist...</target> - <source>Unable to create time stamp for versioning:</source> <target>Der Zeitstempel für die Versionierung kann nicht erstellt werden:</target> @@ -516,6 +522,9 @@ Tatsächlich: %y bytes <source>Select a folder</source> <target>Ordner auswählen</target> +<source>&New</source> +<target>&Neu</target> + <source>&Open...</source> <target>Ö&ffnen...</target> @@ -731,26 +740,23 @@ Die Befehlszeile wird ausgelöst, wenn: <source>job name</source> <target>Auftragsname</target> -<source>Show summary</source> -<target>Zusammenfassung zeigen</target> +<source>System: Sleep</source> +<target>System: Energie sparen</target> -<source>Sleep</source> -<target>Energie sparen</target> +<source>System: Shut down</source> +<target>System: Herunterfahren</target> -<source>Shut down</source> -<target>Herunterfahren</target> - -<source>Synchronization stopped</source> -<target>Synchronisation unterbrochen</target> +<source>Cleaning up old log files...</source> +<target>Bereinige alte Protokolldateien...</target> <source>Stopped</source> -<target>Unterbrochen</target> +<target>Gestoppt</target> -<source>Synchronization completed with errors</source> -<target>Synchronisation mit Fehlern abgeschlossen</target> +<source>Completed with errors</source> +<target>Mit Fehlern abgeschlossen</target> -<source>Synchronization completed with warnings</source> -<target>Synchronisation mit Warnungen abgeschlossen</target> +<source>Completed with warnings</source> +<target>Mit Warnungen abgeschlossen</target> <source>Warning</source> <target>Warnung</target> @@ -758,15 +764,12 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Nothing to synchronize</source> <target>Es gibt nichts zu synchronisieren</target> -<source>Synchronization completed successfully</source> -<target>Synchronisation erfolgreich abgeschlossen</target> +<source>Completed</source> +<target>Fertig</target> <source>Executing command %x</source> <target>Führe Befehl aus: %x</target> -<source>Cleaning up old log files...</source> -<target>Bereinige alte Protokolldateien...</target> - <source>You can switch to FreeFileSync's main window to resolve this issue.</source> <target>Sie können auf FreeFileSyncs Hauptfenster wechseln, um das Problem zu beheben.</target> @@ -782,14 +785,8 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Switching to FreeFileSync's main window</source> <target>Wechsle auf FreeFileSyncs Hauptfenster</target> -<source> -<pluralform>Automatic retry in 1 second...</pluralform> -<pluralform>Automatic retry in %x seconds...</pluralform> -</source> -<target> -<pluralform>Automatische Wiederholung in 1 Sekunde...</pluralform> -<pluralform>Automatische Wiederholung in %x Sekunden...</pluralform> -</target> +<source>Automatic retry</source> +<target>Automatische Wiederholung</target> <source>Ignore &all</source> <target>&Alle ignorieren</target> @@ -884,9 +881,6 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Please select a folder on a local file system, network or an MTP device.</source> <target>Bitte wählen Sie einen Ordner auf einem lokalen Dateisystem, Netzwerk oder MTP Gerät.</target> -<source>&New</source> -<target>&Neu</target> - <source>&Save</source> <target>&Speichern</target> @@ -1207,6 +1201,9 @@ Die Befehlszeile wird ausgelöst, wenn: <source>When finished:</source> <target>Am Ende:</target> +<source>Auto-Close</source> +<target>Automatisch schließen</target> + <source>Close</source> <target>Schließen</target> @@ -1219,6 +1216,9 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x</source> <target>Erstellt eine Batchdatei für die unbeaufsichtigte Synchronisation. Zum Starten die Datei doppelklicken oder in einen Taskplaner eintragen: %x</target> +<source>Progress dialog:</source> +<target>Fortschrittsdialog:</target> + <source>Run minimized</source> <target>Minimiert ausführen</target> @@ -1229,7 +1229,7 @@ Die Befehlszeile wird ausgelöst, wenn: <target>&Abbrechen</target> <source>Stop synchronization at first error</source> -<target>Synchronisation beim ersten Fehler unterbrechen</target> +<target>Synchronisation beim ersten Fehler stoppen</target> <source>Save log:</source> <target>Protokoll speichern:</target> @@ -1597,6 +1597,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Paused</source> <target>Angehalten</target> +<source>Stop requested...</source> +<target>Wird gestoppt...</target> + <source>Initializing...</source> <target>Initialisiere...</target> @@ -1606,9 +1609,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Comparing content...</source> <target>Vergleiche Dateiinhalt...</target> -<source>Completed</source> -<target>Fertig</target> - <source>Info</source> <target>Info</target> @@ -1619,7 +1619,7 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <target>&Fortfahren</target> <source>Progress</source> -<target>Verlauf</target> +<target>Fortschritt</target> <source>Log</source> <target>Protokoll</target> @@ -1690,12 +1690,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Parameters for opposite side</source> <target>Parameter für gegenüberliegende Seite</target> -<source>Show hidden dialogs and warning messages again?</source> -<target>Sollen versteckte Fenster und Warnmeldungen wieder gezeigt werden?</target> - -<source>&Show</source> -<target>&Zeigen</target> - <source>Downloading update...</source> <target>Lade Aktualisierung...</target> diff --git a/FreeFileSync/Source/RealTimeSync/application.cpp b/FreeFileSync/Source/RealTimeSync/application.cpp index e4cfc530..5559fcf9 100755 --- a/FreeFileSync/Source/RealTimeSync/application.cpp +++ b/FreeFileSync/Source/RealTimeSync/application.cpp @@ -24,6 +24,7 @@ #include <gtk/gtk.h> using namespace zen; +using namespace rts; IMPLEMENT_APP(Application); @@ -40,7 +41,7 @@ bool Application::OnInit() { //do not call wxApp::OnInit() to avoid using wxWidgets command line parser - ::gtk_rc_parse((zen::getResourceDirPf() + "styles.gtk_rc").c_str()); //remove inner border from bitmap buttons + ::gtk_rc_parse((fff::getResourceDirPf() + "styles.gtk_rc").c_str()); //remove inner border from bitmap buttons //Windows User Experience Interaction Guidelines: tool tips should have 5s timeout, info tips no timeout => compromise: wxToolTip::Enable(true); //yawn, a wxWidgets screw-up: wxToolTip::SetAutoPop is no-op if global tooltip window is not yet constructed: wxToolTip::Enable creates it @@ -48,11 +49,11 @@ bool Application::OnInit() SetAppName(L"RealTimeSync"); - initResourceImages(getResourceDirPf() + Zstr("Resources.zip")); + initResourceImages(fff::getResourceDirPf() + Zstr("Resources.zip")); try { - setLanguage(xmlAccess::getProgramLanguage()); //throw FileError + fff::setLanguage(getProgramLanguage()); //throw FileError } catch (const FileError& e) { @@ -75,8 +76,8 @@ bool Application::OnInit() int Application::OnExit() { - uninitializeHelp(); - releaseWxLocale(); + fff::uninitializeHelp(); + fff::releaseWxLocale(); cleanupResourceImages(); return wxApp::OnExit(); } @@ -90,7 +91,7 @@ void Application::onEnterEventLoop(wxEvent& event) std::vector<Zstring> commandArgs; for (int i = 1; i < argc; ++i) { - Zstring filePath = getResolvedFilePath(utfTo<Zstring>(argv[i])); + Zstring filePath = fff::getResolvedFilePath(utfTo<Zstring>(argv[i])); if (!fileAvailable(filePath)) //be a little tolerant { @@ -123,15 +124,15 @@ int Application::OnRun() } catch (const std::bad_alloc& e) //the only kind of exception we don't want crash dumps for { - logFatalError(e.what()); //it's not always possible to display a message box, e.g. corrupted stack, however low-level file output works! + fff::logFatalError(e.what()); //it's not always possible to display a message box, e.g. corrupted stack, however low-level file output works! const auto title = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred"); wxSafeShowMessage(title, e.what()); - return FFS_RC_EXCEPTION; + return fff::FFS_RC_EXCEPTION; } //catch (...) -> let it crash and create mini dump!!! - return FFS_RC_SUCCESS; //program's return code + return fff::FFS_RC_SUCCESS; //program's return code } diff --git a/FreeFileSync/Source/RealTimeSync/application.h b/FreeFileSync/Source/RealTimeSync/application.h index b4228c50..338a15e1 100755 --- a/FreeFileSync/Source/RealTimeSync/application.h +++ b/FreeFileSync/Source/RealTimeSync/application.h @@ -10,6 +10,8 @@ #include <wx/app.h> +namespace rts +{ class Application : public wxApp { public: @@ -24,5 +26,6 @@ private: void onEnterEventLoop(wxEvent& event); //wxLayoutDirection GetLayoutDirection() const override { return wxLayout_LeftToRight; } }; +} #endif //APPLICATION_H_18506781708176342677 diff --git a/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp b/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp index fce01000..f83eebb7 100755 --- a/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp +++ b/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp @@ -16,6 +16,7 @@ using namespace zen; +using namespace rts; namespace @@ -25,7 +26,7 @@ void setFolderPath(const Zstring& dirpath, wxTextCtrl* txtCtrl, wxWindow& toolti if (txtCtrl) txtCtrl->ChangeValue(utfTo<wxString>(dirpath)); - const Zstring folderPathFmt = getResolvedFilePath(dirpath); //may block when resolving [<volume name>] + const Zstring folderPathFmt = fff::getResolvedFilePath(dirpath); //may block when resolving [<volume name>] tooltipWnd.SetToolTip(nullptr); //workaround wxComboBox bug http://trac.wxwidgets.org/ticket/10512 / http://trac.wxwidgets.org/ticket/12659 tooltipWnd.SetToolTip(utfTo<wxString>(folderPathFmt)); //who knows when the real bugfix reaches mere mortals via an official release... @@ -124,7 +125,7 @@ void FolderSelector2::onSelectDir(wxCommandEvent& event) //IFileDialog requirements for default path: 1. accepts native paths only!!! 2. path must exist! Zstring defaultFolderPath; { - const Zstring folderPath = getResolvedFilePath(getPath()); + const Zstring folderPath = fff::getResolvedFilePath(getPath()); if (!folderPath.empty()) { auto ft = runAsync([folderPath] { return dirAvailable(folderPath); }); diff --git a/FreeFileSync/Source/RealTimeSync/folder_selector2.h b/FreeFileSync/Source/RealTimeSync/folder_selector2.h index a11567a8..e6af3940 100755 --- a/FreeFileSync/Source/RealTimeSync/folder_selector2.h +++ b/FreeFileSync/Source/RealTimeSync/folder_selector2.h @@ -13,7 +13,7 @@ #include <wx/textctrl.h> #include <wx+/file_drop.h> -namespace zen +namespace rts { //handle drag and drop, tooltip, label and manual input, coordinating a wxWindow, wxButton, and wxTextCtrl @@ -32,7 +32,7 @@ public: private: void onMouseWheel (wxMouseEvent& event); - void onFilesDropped (FileDropEvent& event); + void onFilesDropped (zen::FileDropEvent& event); void onEditFolderPath(wxCommandEvent& event); void onSelectDir (wxCommandEvent& event); diff --git a/FreeFileSync/Source/RealTimeSync/gui_generated.cpp b/FreeFileSync/Source/RealTimeSync/gui_generated.cpp index cc5dbb99..0f62f821 100755 --- a/FreeFileSync/Source/RealTimeSync/gui_generated.cpp +++ b/FreeFileSync/Source/RealTimeSync/gui_generated.cpp @@ -1,8 +1,8 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Jun 17 2015) +// C++ code generated with wxFormBuilder (version Nov 6 2017) // http://www.wxformbuilder.org/ // -// PLEASE DO "NOT" EDIT THIS FILE! +// PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #include "wx+/bitmap_button.h" @@ -18,6 +18,10 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr m_menubar1 = new wxMenuBar( 0 ); m_menuFile = new wxMenu(); + wxMenuItem* m_menuItem6; + m_menuItem6 = new wxMenuItem( m_menuFile, wxID_NEW, wxString( _("&New") ) + wxT('\t') + wxT("Ctrl+N"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem6 ); + wxMenuItem* m_menuItem13; m_menuItem13 = new wxMenuItem( m_menuFile, wxID_OPEN, wxString( _("&Open...") ) + wxT('\t') + wxT("CTRL+O"), wxEmptyString, wxITEM_NORMAL ); m_menuFile->Append( m_menuItem13 ); @@ -58,7 +62,7 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr m_staticText9 = new wxStaticText( this, wxID_ANY, _("Usage:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText9->Wrap( -1 ); - m_staticText9->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + m_staticText9->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) ); bSizer16->Add( m_staticText9, 0, wxALL, 5 ); @@ -144,7 +148,6 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr bSizer19 = new wxBoxSizer( wxHORIZONTAL ); m_txtCtrlDirectoryMain = new wxTextCtrl( m_panelMainFolder, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 300, -1 ), 0 ); - m_txtCtrlDirectoryMain->SetMaxLength( 0 ); bSizer19->Add( m_txtCtrlDirectoryMain, 1, wxALIGN_CENTER_VERTICAL, 5 ); m_buttonSelectFolderMain = new wxButton( m_panelMainFolder, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); @@ -205,7 +208,6 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr bSizer141->Add( m_staticText6, 0, wxALL, 5 ); m_textCtrlCommand = new wxTextCtrl( m_panelMain, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - m_textCtrlCommand->SetMaxLength( 0 ); m_textCtrlCommand->SetToolTip( _("The command is triggered if:\n- files or subfolders change\n- new folders arrive (e.g. USB stick insert)") ); bSizer141->Add( m_textCtrlCommand, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 ); @@ -224,7 +226,7 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr m_buttonStart = new zen::BitmapTextButton( this, wxID_OK, _("Start"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonStart->SetDefault(); - m_buttonStart->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); + m_buttonStart->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) ); bSizerMain->Add( m_buttonStart, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); @@ -237,6 +239,7 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr // Connect Events this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( MainDlgGenerated::OnClose ) ); + this->Connect( m_menuItem6->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnConfigNew ) ); this->Connect( m_menuItem13->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnConfigLoad ) ); this->Connect( m_menuItem14->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnConfigSave ) ); this->Connect( m_menuItem4->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnMenuQuit ) ); @@ -264,7 +267,6 @@ FolderGenerated::FolderGenerated( wxWindow* parent, wxWindowID id, const wxPoint bSizer114->Add( m_bpButtonRemoveFolder, 0, wxALIGN_CENTER_VERTICAL, 5 ); m_txtCtrlDirectory = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - m_txtCtrlDirectory->SetMaxLength( 0 ); bSizer114->Add( m_txtCtrlDirectory, 1, wxALIGN_CENTER_VERTICAL, 5 ); m_buttonSelectFolder = new wxButton( this, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); diff --git a/FreeFileSync/Source/RealTimeSync/gui_generated.h b/FreeFileSync/Source/RealTimeSync/gui_generated.h index b16f1a97..773d8c5a 100755 --- a/FreeFileSync/Source/RealTimeSync/gui_generated.h +++ b/FreeFileSync/Source/RealTimeSync/gui_generated.h @@ -1,8 +1,8 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Jun 17 2015) +// C++ code generated with wxFormBuilder (version Nov 6 2017) // http://www.wxformbuilder.org/ // -// PLEASE DO "NOT" EDIT THIS FILE! +// PLEASE DO *NOT* EDIT THIS FILE! /////////////////////////////////////////////////////////////////////////// #ifndef __GUI_GENERATED_H__ @@ -77,6 +77,7 @@ protected: // Virtual event handlers, overide them in your derived class virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnConfigNew( wxCommandEvent& event ) { event.Skip(); } virtual void OnConfigLoad( wxCommandEvent& event ) { event.Skip(); } virtual void OnConfigSave( wxCommandEvent& event ) { event.Skip(); } virtual void OnMenuQuit( wxCommandEvent& event ) { event.Skip(); } diff --git a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp index f665c783..c6093389 100755 --- a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp +++ b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp @@ -25,6 +25,7 @@ #include <gtk/gtk.h> using namespace zen; +using namespace rts; namespace @@ -32,7 +33,7 @@ namespace } -class DirectoryPanel : public FolderGenerated +class rts::DirectoryPanel : public FolderGenerated { public: DirectoryPanel(wxWindow* parent) : @@ -43,7 +44,7 @@ public: Zstring getPath() const { return folderSelector_.getPath(); } private: - zen::FolderSelector2 folderSelector_; + FolderSelector2 folderSelector_; }; @@ -55,7 +56,7 @@ void MainDialog::create(const Zstring& cfgFile) MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName) : MainDlgGenerated(dlg), - lastRunConfigPath_(zen::getConfigDirPathPf() + Zstr("LastRun.ffs_real")) + lastRunConfigPath_(fff::getConfigDirPathPf() + Zstr("LastRun.ffs_real")) { SetIcon(getRtsIcon()); //set application icon @@ -76,7 +77,7 @@ MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName) dirpathFirst = std::make_unique<FolderSelector2>(*m_panelMainFolder, *m_buttonSelectFolderMain, *m_txtCtrlDirectoryMain, m_staticTextFinalPath); //--------------------------- load config values ------------------------------------ - xmlAccess::XmlRealConfig newConfig; + XmlRealConfig newConfig; Zstring currentConfigFile = cfgFileName; if (currentConfigFile.empty()) @@ -90,7 +91,7 @@ MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName) try { std::wstring warningMsg; - xmlAccess::readRealOrBatchConfig(currentConfigFile, newConfig, warningMsg); //throw FileError + readRealOrBatchConfig(currentConfigFile, newConfig, warningMsg); //throw FileError if (!warningMsg.empty()) showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(warningMsg)); @@ -131,7 +132,7 @@ MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName) MainDialog::~MainDialog() { //save current configuration - const xmlAccess::XmlRealConfig currentCfg = getConfiguration(); + const XmlRealConfig currentCfg = getConfiguration(); try //write config to XML { @@ -153,7 +154,7 @@ void MainDialog::onQueryEndSession() void MainDialog::OnShowHelp(wxCommandEvent& event) { - zen::displayHelpEntry(L"realtimesync", this); + fff::displayHelpEntry(L"realtimesync", this); } @@ -193,9 +194,10 @@ void MainDialog::OnStart(wxCommandEvent& event) { Hide(); - xmlAccess::XmlRealConfig currentCfg = getConfiguration(); + XmlRealConfig currentCfg = getConfiguration(); + const Zstring activeCfgFilePath = !equalFilePath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring(); - switch (rts::startDirectoryMonitor(currentCfg, xmlAccess::extractJobName(utfTo<Zstring>(currentConfigFileName_)))) + switch (rts::startDirectoryMonitor(currentCfg, fff::extractJobName(activeCfgFilePath))) { case rts::EXIT_APP: Close(); @@ -212,29 +214,29 @@ void MainDialog::OnStart(wxCommandEvent& event) void MainDialog::OnConfigSave(wxCommandEvent& event) { - Zstring defaultFileName = currentConfigFileName_.empty() ? Zstr("Realtime.ffs_real") : currentConfigFileName_; + Zstring defaultFilePath = !activeConfigFile_.empty() && !equalFilePath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstr("Realtime.ffs_real"); //attention: currentConfigFileName may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config! - if (endsWith(defaultFileName, Zstr(".ffs_batch"), CmpFilePath())) - defaultFileName = beforeLast(defaultFileName, Zstr("."), IF_MISSING_RETURN_NONE) + Zstr(".ffs_real"); + if (endsWith(defaultFilePath, Zstr(".ffs_batch"), CmpFilePath())) + defaultFilePath = beforeLast(defaultFilePath, Zstr("."), IF_MISSING_RETURN_NONE) + Zstr(".ffs_real"); wxFileDialog filePicker(this, wxString(), //OS X really needs dir/file separated like this: - utfTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir - utfTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file + utfTo<wxString>(beforeLast(defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir + utfTo<wxString>(afterLast (defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file wxString(L"RealTimeSync (*.ffs_real)|*.ffs_real") + L"|" +_("All files") + L" (*.*)|*", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (filePicker.ShowModal() != wxID_OK) return; - const Zstring newFileName = utfTo<Zstring>(filePicker.GetPath()); + const Zstring targetFilePath = utfTo<Zstring>(filePicker.GetPath()); //write config to XML - const xmlAccess::XmlRealConfig currentCfg = getConfiguration(); + const XmlRealConfig currentCfg = getConfiguration(); try { - writeConfig(currentCfg, newFileName); //throw FileError - setLastUsedConfig(newFileName); + writeConfig(currentCfg, targetFilePath); //throw FileError + setLastUsedConfig(targetFilePath); } catch (const FileError& e) { @@ -245,21 +247,22 @@ void MainDialog::OnConfigSave(wxCommandEvent& event) void MainDialog::loadConfig(const Zstring& filepath) { - xmlAccess::XmlRealConfig newConfig; + XmlRealConfig newConfig; - try - { - std::wstring warningMsg; - xmlAccess::readRealOrBatchConfig(filepath, newConfig, warningMsg); //throw FileError + if (!filepath.empty()) + try + { + std::wstring warningMsg; + readRealOrBatchConfig(filepath, newConfig, warningMsg); //throw FileError - if (!warningMsg.empty()) - showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(warningMsg)); - } - catch (const FileError& e) - { - showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); - return; - } + if (!warningMsg.empty()) + showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(warningMsg)); + } + catch (const FileError& e) + { + showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); + return; + } setConfiguration(newConfig); setLastUsedConfig(filepath); @@ -268,25 +271,31 @@ void MainDialog::loadConfig(const Zstring& filepath) void MainDialog::setLastUsedConfig(const Zstring& filepath) { - //set title - if (filepath == lastRunConfigPath_) - { - SetTitle(wxString(L"RealTimeSync ") + zen::ffsVersion + SPACED_DASH + _("Automated Synchronization")); - currentConfigFileName_.clear(); - } + activeConfigFile_ = filepath; + + const Zstring activeCfgFilePath = !equalFilePath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring(); + + if (!activeCfgFilePath.empty()) + SetTitle(utfTo<wxString>(activeCfgFilePath)); else - { - SetTitle(utfTo<wxString>(filepath)); - currentConfigFileName_ = filepath; - } + SetTitle(wxString(L"RealTimeSync ") + fff::ffsVersion + SPACED_DASH + _("Automated Synchronization")); + +} + + +void MainDialog::OnConfigNew(wxCommandEvent& event) +{ + loadConfig({}); } void MainDialog::OnConfigLoad(wxCommandEvent& event) { + const Zstring activeCfgFilePath = !equalFilePath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring(); + wxFileDialog filePicker(this, wxString(), - utfTo<wxString>(beforeLast(currentConfigFileName_, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir + utfTo<wxString>(beforeLast(activeCfgFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir wxString(), //default file wxString(L"RealTimeSync (*.ffs_real; *.ffs_batch)|*.ffs_real;*.ffs_batch") + L"|" +_("All files") + L" (*.*)|*", wxFD_OPEN); @@ -303,7 +312,7 @@ void MainDialog::onFilesDropped(FileDropEvent& event) } -void MainDialog::setConfiguration(const xmlAccess::XmlRealConfig& cfg) +void MainDialog::setConfiguration(const XmlRealConfig& cfg) { //clear existing folders dirpathFirst->setPath(Zstring()); @@ -326,9 +335,9 @@ void MainDialog::setConfiguration(const xmlAccess::XmlRealConfig& cfg) } -xmlAccess::XmlRealConfig MainDialog::getConfiguration() +XmlRealConfig MainDialog::getConfiguration() { - xmlAccess::XmlRealConfig output; + XmlRealConfig output; output.directories.push_back(utfTo<Zstring>(dirpathFirst->getPath())); for (const DirectoryPanel* dne : dirpathsExtra) diff --git a/FreeFileSync/Source/RealTimeSync/main_dlg.h b/FreeFileSync/Source/RealTimeSync/main_dlg.h index 21decc40..cecf26c8 100755 --- a/FreeFileSync/Source/RealTimeSync/main_dlg.h +++ b/FreeFileSync/Source/RealTimeSync/main_dlg.h @@ -17,10 +17,9 @@ #include "folder_selector2.h" -namespace xmlAccess +namespace rts { struct XmlRealConfig; -} class DirectoryPanel; @@ -45,27 +44,29 @@ private: void OnRemoveTopFolder(wxCommandEvent& event) override; void OnKeyPressed (wxKeyEvent& event); void OnStart (wxCommandEvent& event) override; + void OnConfigNew (wxCommandEvent& event) override; void OnConfigSave (wxCommandEvent& event) override; void OnConfigLoad (wxCommandEvent& event) override; void OnMenuQuit (wxCommandEvent& event) override { Close(); } void onFilesDropped(zen::FileDropEvent& event); - void setConfiguration(const xmlAccess::XmlRealConfig& cfg); - xmlAccess::XmlRealConfig getConfiguration(); + void setConfiguration(const XmlRealConfig& cfg); + XmlRealConfig getConfiguration(); void setLastUsedConfig(const Zstring& filepath); void addFolder(const std::vector<Zstring>& newFolders, bool addFront = false); void removeAddFolder(size_t pos); void clearAddFolders(); - std::unique_ptr<zen::FolderSelector2> dirpathFirst; + std::unique_ptr<FolderSelector2> dirpathFirst; std::vector<DirectoryPanel*> dirpathsExtra; //additional pairs to the standard pair const Zstring lastRunConfigPath_; - Zstring currentConfigFileName_; + Zstring activeConfigFile_; zen::AsyncGuiQueue guiQueue_; //schedule and run long-running tasks asynchronously, but process results on GUI queue }; +} #endif //MAIN_DLG_H_2384790842252445 diff --git a/FreeFileSync/Source/RealTimeSync/monitor.cpp b/FreeFileSync/Source/RealTimeSync/monitor.cpp index 54f5ae65..8f0f5c18 100755 --- a/FreeFileSync/Source/RealTimeSync/monitor.cpp +++ b/FreeFileSync/Source/RealTimeSync/monitor.cpp @@ -10,7 +10,6 @@ #include <zen/file_access.h> #include <zen/dir_watcher.h> #include <zen/thread.h> -//#include <zen/tick_count.h> #include <zen/basic_math.h> #include <wx/utils.h> #include "../lib/resolve_path.h" @@ -23,7 +22,7 @@ using namespace zen; namespace { -const int FOLDER_EXISTENCE_CHECK_INTERVAL_SEC = 1; //unit: [s] +const std::chrono::seconds FOLDER_EXISTENCE_CHECK_INTERVAL(1); std::vector<Zstring> getFormattedDirs(const std::vector<Zstring>& folderPathPhrases) //throw FileError @@ -42,7 +41,7 @@ std::vector<Zstring> getFormattedDirs(const std::vector<Zstring>& folderPathPhra checkProtocol(Zstr("MTP")); // //make unique: no need to resolve duplicate phrases more than once! (consider "[volume name]" syntax) -> shouldn't this be already buffered by OS? - folderPaths.insert(getResolvedFilePath(phrase)); + folderPaths.insert(fff::getResolvedFilePath(phrase)); } return { folderPaths.begin(), folderPaths.end() }; @@ -68,7 +67,7 @@ struct WaitResult WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw FileError - const std::function<void(bool readyForSync)>& onRefreshGui) + const std::function<void(bool readyForSync)>& requestUiRefresh, std::chrono::milliseconds cbInterval) { const std::vector<Zstring> folderPaths = getFormattedDirs(folderPathPhrases); //throw FileError if (folderPaths.empty()) //pathological case, but we have to check else this function will wait endlessly @@ -84,8 +83,8 @@ WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw //a non-existent network path may block, so check existence asynchronously! auto ftDirAvailable = runAsync([=] { return dirAvailable(folderPath); }); - while (ftDirAvailable.wait_for(std::chrono::milliseconds(rts::UI_UPDATE_INTERVAL_MS / 2)) != std::future_status::ready) - onRefreshGui(false /*readyForSync*/); //may throw! + while (ftDirAvailable.wait_for(cbInterval) != std::future_status::ready) + if (requestUiRefresh) requestUiRefresh(false /*readyForSync*/); //throw X if (!ftDirAvailable.get()) //folder not existing or can't access return WaitResult(folderPath); @@ -107,7 +106,7 @@ WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw { const auto now = std::chrono::steady_clock::now(); - if (numeric::dist(now, lastCheckTime) > std::chrono::seconds(FOLDER_EXISTENCE_CHECK_INTERVAL_SEC)) //handle potential chrono wrap-around! + if (numeric::dist(now, lastCheckTime) > FOLDER_EXISTENCE_CHECK_INTERVAL) //handle potential chrono wrap-around! { lastCheckTime = now; return true; @@ -127,15 +126,16 @@ WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw return WaitResult(folderPath); try { - std::vector<DirWatcher::Entry> changedItems = watcher.getChanges([&] { onRefreshGui(false /*readyForSync*/); /*may throw!*/ }); //throw FileError + std::vector<DirWatcher::Entry> changedItems = watcher.getChanges([&] { requestUiRefresh(false /*readyForSync*/); /*throw X*/ }, + cbInterval); //throw FileError //remove to be ignored changes erase_if(changedItems, [](const DirWatcher::Entry& e) { return - //endsWith(e.filepath_, Zstr(".ffs_tmp")) || - endsWith(e.filepath_, Zstr(".ffs_lock")) || //sync.ffs_lock, sync.Del.ffs_lock - endsWith(e.filepath_, Zstr(".ffs_db")); //sync.ffs_db, .sync.tmp.ffs_db + endsWith(e.filePath, Zstr(".ffs_tmp")) || //sync.8ea2.ffs_tmp + endsWith(e.filePath, Zstr(".ffs_lock")) || //sync.ffs_lock, sync.Del.ffs_lock + endsWith(e.filePath, Zstr(".ffs_db")); //sync.ffs_db //no need to ignore temporary recycle bin directory: this must be caused by a file deletion anyway }); @@ -150,15 +150,15 @@ WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw } } - std::this_thread::sleep_for(std::chrono::milliseconds(rts::UI_UPDATE_INTERVAL_MS / 2)); - onRefreshGui(true /*readyForSync*/); //throw ?: may start sync at this presumably idle time + std::this_thread::sleep_for(cbInterval); + requestUiRefresh(true /*readyForSync*/); //throw X: may start sync at this presumably idle time } } //wait until all directories become available (again) + logs in network share void waitForMissingDirs(const std::vector<Zstring>& folderPathPhrases, //throw FileError - const std::function<void(const Zstring& folderPath)>& onRefreshGui) + const std::function<void(const Zstring& folderPath)>& requestUiRefresh, std::chrono::milliseconds cbInterval) { for (;;) { @@ -171,19 +171,18 @@ void waitForMissingDirs(const std::vector<Zstring>& folderPathPhrases, //throw F //2. check dir availability return dirAvailable(folderPath); }); - while (ftDirAvailable.wait_for(std::chrono::milliseconds(rts::UI_UPDATE_INTERVAL_MS / 2)) != std::future_status::ready) - onRefreshGui(folderPath); //may throw! + while (ftDirAvailable.wait_for(cbInterval) != std::future_status::ready) + if (requestUiRefresh) requestUiRefresh(folderPath); //throw X if (!ftDirAvailable.get()) { allAvailable = false; //wait some time... - const int refreshInterval = rts::UI_UPDATE_INTERVAL_MS / 2; - static_assert(FOLDER_EXISTENCE_CHECK_INTERVAL_SEC * 1000 % refreshInterval == 0, ""); - for (int i = 0; i < FOLDER_EXISTENCE_CHECK_INTERVAL_SEC * 1000 / refreshInterval; ++i) + const auto delayUntil = std::chrono::steady_clock::now() + FOLDER_EXISTENCE_CHECK_INTERVAL; + for (auto now = std::chrono::steady_clock::now(); now < delayUntil; now = std::chrono::steady_clock::now()) { - onRefreshGui(folderPath); //may throw! - std::this_thread::sleep_for(std::chrono::milliseconds(refreshInterval)); + if (requestUiRefresh) requestUiRefresh(folderPath); //throw X + std::this_thread::sleep_for(cbInterval); } break; } @@ -213,7 +212,7 @@ struct ExecCommandNowException {}; } -void rts::monitorDirectories(const std::vector<Zstring>& folderPathPhrases, unsigned int delay, rts::MonitorCallback& callback) +void rts::monitorDirectories(const std::vector<Zstring>& folderPathPhrases, size_t delay, MonitorCallback& cb, std::chrono::milliseconds cbInterval) { if (folderPathPhrases.empty()) { @@ -223,12 +222,12 @@ void rts::monitorDirectories(const std::vector<Zstring>& folderPathPhrases, unsi auto execMonitoring = [&] //throw FileError { - callback.setPhase(MonitorCallback::MONITOR_PHASE_WAITING); - waitForMissingDirs(folderPathPhrases, [&](const Zstring& folderPath) { callback.requestUiRefresh(); }); //throw FileError - callback.setPhase(MonitorCallback::MONITOR_PHASE_ACTIVE); + cb.setPhase(MonitorCallback::MONITOR_PHASE_WAITING); + waitForMissingDirs(folderPathPhrases, [&](const Zstring& folderPath) { cb.requestUiRefresh(); }, cbInterval); //throw FileError + cb.setPhase(MonitorCallback::MONITOR_PHASE_ACTIVE); //schedule initial execution (*after* all directories have arrived, which could take some time which we don't want to include) - time_t nextExecDate = std::time(nullptr) + delay; + time_t nextExecTime = std::time(nullptr) + delay; for (;;) //loop over command invocations { @@ -241,33 +240,33 @@ void rts::monitorDirectories(const std::vector<Zstring>& folderPathPhrases, unsi WaitResult res = waitForChanges(folderPathPhrases, [&](bool readyForSync) //throw FileError, ExecCommandNowException { if (readyForSync) - if (nextExecDate <= std::time(nullptr)) + if (nextExecTime <= std::time(nullptr)) throw ExecCommandNowException(); //abort wait and start sync - callback.requestUiRefresh(); - }); + cb.requestUiRefresh(); + }, cbInterval); switch (res.type) { case WaitResult::CHANGE_DIR_UNAVAILABLE: //don't execute the command before all directories are available! - callback.setPhase(MonitorCallback::MONITOR_PHASE_WAITING); - waitForMissingDirs(folderPathPhrases, [&](const Zstring& folderPath) { callback.requestUiRefresh(); }); //throw FileError - callback.setPhase(MonitorCallback::MONITOR_PHASE_ACTIVE); + cb.setPhase(MonitorCallback::MONITOR_PHASE_WAITING); + waitForMissingDirs(folderPathPhrases, [&](const Zstring& folderPath) { cb.requestUiRefresh(); }, cbInterval); //throw FileError + cb.setPhase(MonitorCallback::MONITOR_PHASE_ACTIVE); break; case WaitResult::CHANGE_DETECTED: lastChangeDetected = res.changedItem_; break; } - nextExecDate = std::time(nullptr) + delay; + nextExecTime = std::time(nullptr) + delay; } } catch (ExecCommandNowException&) {} - ::wxSetEnv(L"change_path", utfTo<wxString>(lastChangeDetected.filepath_)); //some way to output what file changed to the user - ::wxSetEnv(L"change_action", toString(lastChangeDetected.action_)); // + ::wxSetEnv(L"change_path", utfTo<wxString>(lastChangeDetected.filePath)); //some way to output what file changed to the user + ::wxSetEnv(L"change_action", toString(lastChangeDetected.action)); // //execute command - callback.executeExternalCommand(); - nextExecDate = std::numeric_limits<time_t>::max(); + cb.executeExternalCommand(); + nextExecTime = std::numeric_limits<time_t>::max(); } }; @@ -278,6 +277,6 @@ void rts::monitorDirectories(const std::vector<Zstring>& folderPathPhrases, unsi } catch (const FileError& e) { - callback.reportError(e.toString()); + cb.reportError(e.toString()); } } diff --git a/FreeFileSync/Source/RealTimeSync/monitor.h b/FreeFileSync/Source/RealTimeSync/monitor.h index c8809998..70b6ff84 100755 --- a/FreeFileSync/Source/RealTimeSync/monitor.h +++ b/FreeFileSync/Source/RealTimeSync/monitor.h @@ -7,15 +7,13 @@ #ifndef MONITOR_H_345087425834253425 #define MONITOR_H_345087425834253425 +#include <chrono> #include <functional> #include <zen/zstring.h> namespace rts { -const int UI_UPDATE_INTERVAL_MS = 100; //unit: [ms]; perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss - - struct MonitorCallback { virtual ~MonitorCallback() {} @@ -32,8 +30,8 @@ struct MonitorCallback }; void monitorDirectories(const std::vector<Zstring>& folderPathPhrases, //non-formatted dirnames that yet require call to getFormattedDirectoryName(); empty directories must be checked by caller! - unsigned int delay, - MonitorCallback& callback); + size_t delay, + MonitorCallback& cb, std::chrono::milliseconds cbInterval); } #endif //MONITOR_H_345087425834253425 diff --git a/FreeFileSync/Source/RealTimeSync/tray_menu.cpp b/FreeFileSync/Source/RealTimeSync/tray_menu.cpp index ec2185d3..dd697d1f 100755 --- a/FreeFileSync/Source/RealTimeSync/tray_menu.cpp +++ b/FreeFileSync/Source/RealTimeSync/tray_menu.cpp @@ -19,13 +19,15 @@ #include "monitor.h" #include "../lib/resolve_path.h" -using namespace rts; using namespace zen; +using namespace rts; namespace { -const int RETRY_AFTER_ERROR_INTERVAL_SEC = 15; //unit: [s] +const std::chrono::seconds RETRY_AFTER_ERROR_INTERVAL(15); +const std::chrono::milliseconds UI_UPDATE_INTERVAL(100); //perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss + std::chrono::steady_clock::time_point lastExec; @@ -34,7 +36,7 @@ bool updateUiIsAllowed() { const auto now = std::chrono::steady_clock::now(); - if (numeric::dist(now, lastExec) > std::chrono::milliseconds(rts::UI_UPDATE_INTERVAL_MS)) //handle potential chrono wrap-around! + if (numeric::dist(now, lastExec) > UI_UPDATE_INTERVAL) //handle potential chrono wrap-around! { lastExec = now; return true; @@ -239,7 +241,7 @@ private: } -rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& config, const wxString& jobname) +rts::AbortReason rts::startDirectoryMonitor(const XmlRealConfig& config, const wxString& jobname) { std::vector<Zstring> dirNamesNonFmt = config.directories; erase_if(dirNamesNonFmt, [](const Zstring& str) { return trimCpy(str).empty(); }); //remove empty entries WITHOUT formatting paths yet! @@ -278,10 +280,10 @@ rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& conf void executeExternalCommand() override { - auto cmdLineExp = expandMacros(cmdLine_); + auto cmdLineExp = fff::expandMacros(cmdLine_); try { - shellExecute(cmdLineExp, EXEC_TYPE_SYNC); //throw FileError + shellExecute(cmdLineExp, ExecutionType::SYNC); //throw FileError } catch (const FileError& e) { @@ -301,8 +303,8 @@ rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& conf trayIcon.clearShowErrorRequested(); //wait for some time, then return to retry - static_assert(RETRY_AFTER_ERROR_INTERVAL_SEC * 1000 % UI_UPDATE_INTERVAL_MS == 0, ""); - for (int i = 0; i < RETRY_AFTER_ERROR_INTERVAL_SEC * 1000 / UI_UPDATE_INTERVAL_MS; ++i) + const auto delayUntil = std::chrono::steady_clock::now() + RETRY_AFTER_ERROR_INTERVAL; + for (auto now = std::chrono::steady_clock::now(); now < delayUntil; now = std::chrono::steady_clock::now()) { trayIcon.doUiRefreshNow(); //throw AbortMonitoring @@ -316,7 +318,7 @@ rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& conf case ConfirmationButton::CANCEL: throw AbortMonitoring(SHOW_GUI); } - std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS)); + std::this_thread::sleep_for(UI_UPDATE_INTERVAL); } } @@ -326,7 +328,7 @@ rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& conf try { - monitorDirectories(dirNamesNonFmt, config.delay, cb); //cb: throw AbortMonitoring + monitorDirectories(dirNamesNonFmt, config.delay, cb, UI_UPDATE_INTERVAL / 2); //cb: throw AbortMonitoring assert(false); return SHOW_GUI; } diff --git a/FreeFileSync/Source/RealTimeSync/tray_menu.h b/FreeFileSync/Source/RealTimeSync/tray_menu.h index c0833784..ad2ee456 100755 --- a/FreeFileSync/Source/RealTimeSync/tray_menu.h +++ b/FreeFileSync/Source/RealTimeSync/tray_menu.h @@ -18,7 +18,7 @@ enum AbortReason SHOW_GUI, EXIT_APP }; -AbortReason startDirectoryMonitor(const xmlAccess::XmlRealConfig& config, const wxString& jobname); //jobname may be empty +AbortReason startDirectoryMonitor(const XmlRealConfig& config, const wxString& jobname); //jobname may be empty } #endif //TRAY_MENU_H_3967857420987534253245 diff --git a/FreeFileSync/Source/RealTimeSync/xml_proc.cpp b/FreeFileSync/Source/RealTimeSync/xml_proc.cpp index 8b631d6c..0cf23407 100755 --- a/FreeFileSync/Source/RealTimeSync/xml_proc.cpp +++ b/FreeFileSync/Source/RealTimeSync/xml_proc.cpp @@ -11,7 +11,7 @@ #include "../lib/ffs_paths.h" using namespace zen; -using namespace xmlAccess; +using namespace rts; namespace @@ -37,7 +37,7 @@ bool isXmlTypeRTS(const XmlDoc& doc) //throw() } -void xmlAccess::readConfig(const Zstring& filepath, XmlRealConfig& config, std::wstring& warningMsg) //throw FileError +void rts::readConfig(const Zstring& filepath, XmlRealConfig& config, std::wstring& warningMsg) //throw FileError { XmlDoc doc = loadXmlDocument(filepath); //throw FileError @@ -69,7 +69,7 @@ void writeConfig(const XmlRealConfig& config, XmlOut& out) } -void xmlAccess::writeConfig(const XmlRealConfig& config, const Zstring& filepath) //throw FileError +void rts::writeConfig(const XmlRealConfig& config, const Zstring& filepath) //throw FileError { XmlDoc doc("FreeFileSync"); doc.root().setAttribute("XmlType", "REAL"); @@ -83,7 +83,7 @@ void xmlAccess::writeConfig(const XmlRealConfig& config, const Zstring& filepath namespace { -xmlAccess::XmlRealConfig convertBatchToReal(const xmlAccess::XmlBatchConfig& batchCfg, const Zstring& batchFilePath) +XmlRealConfig convertBatchToReal(const fff::XmlBatchConfig& batchCfg, const Zstring& batchFilePath) { std::set<Zstring, LessFilePath> uniqueFolders; @@ -92,7 +92,7 @@ xmlAccess::XmlRealConfig convertBatchToReal(const xmlAccess::XmlBatchConfig& bat uniqueFolders.insert(batchCfg.mainCfg.firstPair.folderPathPhraseRight_); //additional folders - for (const FolderPairEnh& fp : batchCfg.mainCfg.additionalPairs) + for (const fff::FolderPairEnh& fp : batchCfg.mainCfg.additionalPairs) { uniqueFolders.insert(fp.folderPathPhraseLeft_); uniqueFolders.insert(fp.folderPathPhraseRight_); @@ -100,23 +100,21 @@ xmlAccess::XmlRealConfig convertBatchToReal(const xmlAccess::XmlBatchConfig& bat erase_if(uniqueFolders, [](const Zstring& str) { return trimCpy(str).empty(); }); - xmlAccess::XmlRealConfig output; + XmlRealConfig output; output.directories.assign(uniqueFolders.begin(), uniqueFolders.end()); - output.commandline = Zstr("\"") + zen::getFreeFileSyncLauncherPath() + Zstr("\" \"") + batchFilePath + Zstr("\""); + output.commandline = Zstr("\"") + fff::getFreeFileSyncLauncherPath() + Zstr("\" \"") + batchFilePath + Zstr("\""); return output; } } -void xmlAccess::readRealOrBatchConfig(const Zstring& filepath, xmlAccess::XmlRealConfig& config, std::wstring& warningMsg) //throw FileError +void rts::readRealOrBatchConfig(const Zstring& filepath, XmlRealConfig& config, std::wstring& warningMsg) //throw FileError { - using namespace xmlAccess; - - if (getXmlType(filepath) != XML_TYPE_BATCH) //throw FileError + if (fff::getXmlType(filepath) != fff::XML_TYPE_BATCH) //throw FileError return readConfig(filepath, config, warningMsg); //throw FileError //convert batch config to RealTimeSync config - XmlBatchConfig batchCfg; + fff::XmlBatchConfig batchCfg; readConfig(filepath, batchCfg, warningMsg); //throw FileError //<- redirect batch config warnings @@ -124,13 +122,13 @@ void xmlAccess::readRealOrBatchConfig(const Zstring& filepath, xmlAccess::XmlRea } -wxLanguage xmlAccess::getProgramLanguage() +wxLanguage rts::getProgramLanguage() { - xmlAccess::XmlGlobalSettings settings; + fff::XmlGlobalSettings settings; std::wstring warningMsg; try { - xmlAccess::readConfig(getGlobalConfigFile(), settings, warningMsg); //throw FileError + fff::readConfig(fff::getGlobalConfigFile(), settings, warningMsg); //throw FileError } catch (const FileError&) {} //use default language if error occurred diff --git a/FreeFileSync/Source/RealTimeSync/xml_proc.h b/FreeFileSync/Source/RealTimeSync/xml_proc.h index 3969ba23..afbd010c 100755 --- a/FreeFileSync/Source/RealTimeSync/xml_proc.h +++ b/FreeFileSync/Source/RealTimeSync/xml_proc.h @@ -12,7 +12,7 @@ #include <zen/zstring.h> #include <wx/language.h> -namespace xmlAccess +namespace rts { struct XmlRealConfig { @@ -26,7 +26,7 @@ void writeConfig(const XmlRealConfig& config, const Zstring& filepath); //throw //reuse (some of) FreeFileSync's xml files -void readRealOrBatchConfig(const Zstring& filepath, xmlAccess::XmlRealConfig& config, std::wstring& warningMsg); //throw FileError +void readRealOrBatchConfig(const Zstring& filepath, XmlRealConfig& config, std::wstring& warningMsg); //throw FileError wxLanguage getProgramLanguage(); } diff --git a/FreeFileSync/Source/algorithm.cpp b/FreeFileSync/Source/algorithm.cpp index 861ca36d..2316cd27 100755 --- a/FreeFileSync/Source/algorithm.cpp +++ b/FreeFileSync/Source/algorithm.cpp @@ -21,10 +21,10 @@ using namespace zen; -//using namespace std::rel_ops; +using namespace fff; -void zen::swapGrids(const MainConfiguration& config, FolderComparison& folderCmp) //throw FileError +void fff::swapGrids(const MainConfiguration& config, FolderComparison& folderCmp) //throw FileError { std::for_each(begin(folderCmp), end(folderCmp), [](BaseFolderPair& baseFolder) { baseFolder.flip(); }); @@ -182,7 +182,7 @@ bool allItemsCategoryEqual(const ContainerObject& hierObj) } } -bool zen::allElementsEqual(const FolderComparison& folderCmp) +bool fff::allElementsEqual(const FolderComparison& folderCmp) { return std::all_of(begin(folderCmp), end(folderCmp), [](const BaseFolderPair& baseFolder) { return allItemsCategoryEqual(baseFolder); }); } @@ -655,7 +655,7 @@ private: //--------------------------------------------------------------------------------------------------------------- -std::vector<DirectionConfig> zen::extractDirectionCfg(const MainConfiguration& mainCfg) +std::vector<DirectionConfig> fff::extractDirectionCfg(const MainConfiguration& mainCfg) { //merge first and additional pairs std::vector<FolderPairEnh> allPairs; @@ -672,7 +672,7 @@ std::vector<DirectionConfig> zen::extractDirectionCfg(const MainConfiguration& m } -void zen::redetermineSyncDirection(const DirectionConfig& dirCfg, //throw FileError +void fff::redetermineSyncDirection(const DirectionConfig& dirCfg, //throw FileError BaseFolderPair& baseFolder, const std::function<void(const std::wstring& msg)>& notifyStatus) { @@ -718,7 +718,7 @@ void zen::redetermineSyncDirection(const DirectionConfig& dirCfg, //throw FileEr } -void zen::redetermineSyncDirection(const MainConfiguration& mainCfg, //throw FileError +void fff::redetermineSyncDirection(const MainConfiguration& mainCfg, //throw FileError FolderComparison& folderCmp, const std::function<void(const std::wstring& msg)>& notifyStatus) { @@ -779,7 +779,7 @@ struct SetNewDirection }; -void zen::setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsObj) +void fff::setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsObj) { //process subdirectories also! visitFSObject(fsObj, [&](const FolderPair& folder) @@ -818,7 +818,7 @@ void inOrExcludeAllRows(ContainerObject& hierObj) } -void zen::setActiveStatus(bool newStatus, FolderComparison& folderCmp) +void fff::setActiveStatus(bool newStatus, FolderComparison& folderCmp) { if (newStatus) std::for_each(begin(folderCmp), end(folderCmp), [](BaseFolderPair& baseFolder) { inOrExcludeAllRows<true>(baseFolder); }); //include all rows @@ -827,7 +827,7 @@ void zen::setActiveStatus(bool newStatus, FolderComparison& folderCmp) } -void zen::setActiveStatus(bool newStatus, FileSystemObject& fsObj) +void fff::setActiveStatus(bool newStatus, FileSystemObject& fsObj) { fsObj.setActive(newStatus); @@ -929,7 +929,7 @@ public: private: ApplySoftFilter(ContainerObject& hierObj, const SoftFilter& timeSizeFilter) : timeSizeFilter_(timeSizeFilter) { recurse(hierObj); } - void recurse(zen::ContainerObject& hierObj) const + void recurse(fff::ContainerObject& hierObj) const { for (FilePair& file : hierObj.refSubFiles()) processFile(file); @@ -1013,20 +1013,20 @@ private: } -void zen::addHardFiltering(BaseFolderPair& baseFolder, const Zstring& excludeFilter) +void fff::addHardFiltering(BaseFolderPair& baseFolder, const Zstring& excludeFilter) { ApplyHardFilter<STRATEGY_AND>::execute(baseFolder, NameFilter(FilterConfig().includeFilter, excludeFilter)); } -void zen::addSoftFiltering(BaseFolderPair& baseFolder, const SoftFilter& timeSizeFilter) +void fff::addSoftFiltering(BaseFolderPair& baseFolder, const SoftFilter& timeSizeFilter) { if (!timeSizeFilter.isNull()) //since we use STRATEGY_AND, we may skip a "null" filter ApplySoftFilter<STRATEGY_AND>::execute(baseFolder, timeSizeFilter); } -void zen::applyFiltering(FolderComparison& folderCmp, const MainConfiguration& mainCfg) +void fff::applyFiltering(FolderComparison& folderCmp, const MainConfiguration& mainCfg) { if (folderCmp.empty()) return; @@ -1117,13 +1117,13 @@ private: }; -void zen::applyTimeSpanFilter(FolderComparison& folderCmp, time_t timeFrom, time_t timeTo) +void fff::applyTimeSpanFilter(FolderComparison& folderCmp, time_t timeFrom, time_t timeTo) { std::for_each(begin(folderCmp), end(folderCmp), [&](BaseFolderPair& baseFolder) { FilterByTimeSpan::execute(baseFolder, timeFrom, timeTo); }); } -Opt<PathDependency> zen::getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL, +Opt<PathDependency> fff::getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL, const AbstractPath& basePathR, const HardFilter& filterR) { if (!AFS::isNullPath(basePathL) && !AFS::isNullPath(basePathR)) @@ -1163,7 +1163,7 @@ Opt<PathDependency> zen::getPathDependency(const AbstractPath& basePathL, const //############################################################################################################ -std::pair<std::wstring, int> zen::getSelectedItemsAsString(const std::vector<const FileSystemObject*>& selectionLeft, +std::pair<std::wstring, int> fff::getSelectedItemsAsString(const std::vector<const FileSystemObject*>& selectionLeft, const std::vector<const FileSystemObject*>& selectionRight) { //don't use wxString! its rather dumb linear allocation strategy brings perf down to a crawl! @@ -1184,7 +1184,7 @@ std::pair<std::wstring, int> zen::getSelectedItemsAsString(const std::vector<con ++totalDelCount; } - return std::make_pair(fileList, totalDelCount); + return { fileList, totalDelCount }; } @@ -1330,12 +1330,12 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT } -void zen::copyToAlternateFolder(const std::vector<const FileSystemObject*>& rowsToCopyOnLeft, +void fff::copyToAlternateFolder(const std::vector<const FileSystemObject*>& rowsToCopyOnLeft, const std::vector<const FileSystemObject*>& rowsToCopyOnRight, const Zstring& targetFolderPathPhrase, bool keepRelPaths, bool overwriteIfExists, - xmlAccess::OptionalDialogs& warnings, + WarningDialogs& warnings, ProcessCallback& callback) { std::vector<const FileSystemObject*> itemSelectionLeft = rowsToCopyOnLeft; @@ -1494,7 +1494,7 @@ void categorize(const std::vector<FileSystemObject*>& rows, } -void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDeleteOnLeft, //refresh GUI grid after deletion to remove invalid rows +void fff::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDeleteOnLeft, //refresh GUI grid after deletion to remove invalid rows const std::vector<FileSystemObject*>& rowsToDeleteOnRight, //all pointers need to be bound! FolderComparison& folderCmp, //attention: rows will be physically deleted! const std::vector<DirectionConfig>& directCfgs, @@ -1594,7 +1594,7 @@ void zen::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete //############################################################################################################ -bool zen::operator<(const FileDescriptor& lhs, const FileDescriptor& rhs) +bool fff::operator<(const FileDescriptor& lhs, const FileDescriptor& rhs) { if (lhs.attr.modTime != rhs.attr.modTime) return lhs.attr.modTime < rhs.attr.modTime; diff --git a/FreeFileSync/Source/algorithm.h b/FreeFileSync/Source/algorithm.h index 98067b4e..719d0f9b 100755 --- a/FreeFileSync/Source/algorithm.h +++ b/FreeFileSync/Source/algorithm.h @@ -14,7 +14,7 @@ #include "process_callback.h" -namespace zen +namespace fff { void swapGrids(const MainConfiguration& config, FolderComparison& folderCmp); //throw FileError @@ -48,8 +48,8 @@ struct PathDependency AbstractPath basePathChild; Zstring relPath; //filled if child path is sub folder of parent path; empty if child path == parent path }; -Opt<PathDependency> getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL, - const AbstractPath& basePathR, const HardFilter& filterR); +zen::Opt<PathDependency> getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL, + const AbstractPath& basePathR, const HardFilter& filterR); std::pair<std::wstring, int> getSelectedItemsAsString( //returns string with item names and total count of selected(!) items, NOT total files/dirs! const std::vector<const FileSystemObject*>& selectionLeft, //all pointers need to be bound! @@ -61,7 +61,7 @@ void copyToAlternateFolder(const std::vector<const FileSystemObject*>& rowsToCop const Zstring& targetFolderPathPhrase, bool keepRelPaths, bool overwriteIfExists, - xmlAccess::OptionalDialogs& warnings, + WarningDialogs& warnings, ProcessCallback& callback); //manual deletion of files on main grid diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp index d38573ef..0c6a4382 100755 --- a/FreeFileSync/Source/application.cpp +++ b/FreeFileSync/Source/application.cpp @@ -26,7 +26,7 @@ #include <gtk/gtk.h> using namespace zen; -using namespace xmlAccess; +using namespace fff; IMPLEMENT_APP(Application) @@ -67,7 +67,7 @@ bool Application::OnInit() try { //tentatively set program language to OS default until GlobalSettings.xml is read later - setLanguage(xmlAccess::XmlGlobalSettings().programLanguage); //throw FileError + setLanguage(XmlGlobalSettings().programLanguage); //throw FileError } catch (const FileError&) { assert(false); } @@ -356,7 +356,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs) //distinguish sync scenarios: //--------------------------- - const Zstring globalConfigFilePath = !globalConfigFile.empty() ? globalConfigFile : xmlAccess::getGlobalConfigFile(); + const Zstring globalConfigFilePath = !globalConfigFile.empty() ? globalConfigFile : getGlobalConfigFile(); if (configFiles.empty()) { @@ -462,7 +462,7 @@ void runGuiMode(const Zstring& globalConfigFilePath) { MainDialog::create(global void runGuiMode(const Zstring& globalConfigFilePath, - const xmlAccess::XmlGuiConfig& guiCfg, + const XmlGuiConfig& guiCfg, const std::vector<Zstring>& cfgFilePaths, bool startComparison) { @@ -543,13 +543,14 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat try //begin of synchronization process (all in one try-catch block) { - const std::chrono::system_clock::time_point batchStartTime = std::chrono::system_clock::now(); + const std::chrono::system_clock::time_point syncStartTime = std::chrono::system_clock::now(); //class handling status updates and error messages BatchStatusHandler statusHandler(!batchCfg.batchExCfg.runMinimized, //throw AbortProcess, BatchRequestSwitchToMainDialog + batchCfg.batchExCfg.autoCloseSummary, extractJobName(cfgFilePath), globalCfg.soundFileSyncFinished, - batchStartTime, + syncStartTime, batchCfg.batchExCfg.logFolderPathPhrase, batchCfg.batchExCfg.logfilesCountLimit, globalCfg.lastSyncsLogFileSizeMax, @@ -570,7 +571,7 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat std::unique_ptr<LockHolder> dirLocks; //COMPARE DIRECTORIES - FolderComparison cmpResult = compare(globalCfg.optDialogs, + FolderComparison cmpResult = compare(globalCfg.warnDlgs, globalCfg.fileTimeTolerance, showPopupAllowed, //allowUserInteraction globalCfg.runWithBackgroundPriority, @@ -585,7 +586,7 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat if (syncProcessCfg.size() != cmpResult.size()) throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); - synchronize(batchStartTime, + synchronize(syncStartTime, globalCfg.verifyFileCopy, globalCfg.copyLockedFiles, globalCfg.copyFilePermissions, @@ -594,11 +595,11 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat globalCfg.folderAccessTimeout, syncProcessCfg, cmpResult, - globalCfg.optDialogs, + globalCfg.warnDlgs, statusHandler); //throw ? //not cancelled? => update last sync date for the selected cfg file - for (xmlAccess::ConfigFileItem& cfi : globalCfg.gui.mainDlg.cfgFileHistory) + for (ConfigFileItem& cfi : globalCfg.gui.mainDlg.cfgFileHistory) if (equalFilePath(cfi.filePath, cfgFilePath)) { cfi.lastSyncTime = std::time(nullptr); @@ -609,12 +610,12 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat catch (BatchRequestSwitchToMainDialog&) { //open new toplevel window *after* progress dialog is gone => run on main event loop - return MainDialog::create(globalConfigFilePath, &globalCfg, xmlAccess::convertBatchToGui(batchCfg), { cfgFilePath }, true /*startComparison*/); + return MainDialog::create(globalConfigFilePath, &globalCfg, convertBatchToGui(batchCfg), { cfgFilePath }, true /*startComparison*/); } try //save global settings to XML: e.g. ignored warnings { - xmlAccess::writeConfig(globalCfg, globalConfigFilePath); //FileError + writeConfig(globalCfg, globalConfigFilePath); //FileError } catch (const FileError& e) { diff --git a/FreeFileSync/Source/application.h b/FreeFileSync/Source/application.h index 5803bee6..1b930074 100755 --- a/FreeFileSync/Source/application.h +++ b/FreeFileSync/Source/application.h @@ -13,6 +13,8 @@ #include "lib/return_codes.h" +namespace fff //avoid name clash with "int ffs()" for fuck's sake! (maxOS, Linux issue only: <string> internally includes <strings.h>, WTF!) +{ class Application : public wxApp { private: @@ -26,7 +28,8 @@ private: void onQueryEndSession(wxEvent& event); void launch(const std::vector<Zstring>& commandArgs); - zen::FfsReturnCode returnCode_ = zen::FFS_RC_SUCCESS; + FfsReturnCode returnCode_ = FFS_RC_SUCCESS; }; +} #endif //APPLICATION_H_081568741942010985702395 diff --git a/FreeFileSync/Source/comparison.cpp b/FreeFileSync/Source/comparison.cpp index ac89cb6c..ca7c6d03 100755 --- a/FreeFileSync/Source/comparison.cpp +++ b/FreeFileSync/Source/comparison.cpp @@ -16,9 +16,10 @@ #include "fs/concrete.h" using namespace zen; +using namespace fff; -std::vector<FolderPairCfg> zen::extractCompareCfg(const MainConfiguration& mainCfg) +std::vector<FolderPairCfg> fff::extractCompareCfg(const MainConfiguration& mainCfg) { //merge first and additional pairs std::vector<FolderPairEnh> allPairs = { mainCfg.firstPair }; @@ -152,7 +153,6 @@ ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, int itemsReported_ = itemsTotal; callback_.reportStatus(statusMsg); //may throw - //callback_.requestUiRefresh(); //already called by reportStatus() } HandleError reportError(const std::wstring& msg, size_t retryNumber) override @@ -170,6 +170,8 @@ ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, int return ON_ERROR_CONTINUE; } + int getItemsTotal() const { return itemsReported_; } + private: ProcessCallback& callback_; int itemsReported_ = 0; @@ -178,7 +180,9 @@ ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, int fillBuffer(keysToRead, //in directoryBuffer_, //out cb, - UI_UPDATE_INTERVAL_MS / 2); //every ~50 ms + UI_UPDATE_INTERVAL / 2); //every ~50 ms + + callback.reportInfo(_("Comparison finished:") + L" " + _P("1 item found", "%x items found", cb.getItemsTotal())); } @@ -714,11 +718,11 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::performComparison(const Resolv std::vector<SymlinkPair*>& undefinedSymlinks) const { callback_.reportStatus(_("Generating file list...")); - callback_.forceUiRefresh(); + callback_.forceUiRefresh(); //throw X auto getDirValue = [&](const AbstractPath& folderPath) -> const DirectoryValue* { - auto it = directoryBuffer_.find(DirectoryKey(folderPath, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); + auto it = directoryBuffer_.find({ folderPath, fpCfg.filter.nameFilter, fpCfg.handleSymlinks }); return it != directoryBuffer_.end() ? &it->second : nullptr; }; @@ -775,9 +779,9 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::performComparison(const Resolv } -void zen::logNonDefaultSettings(const xmlAccess::XmlGlobalSettings& activeSettings, ProcessCallback& callback) +void fff::logNonDefaultSettings(const XmlGlobalSettings& activeSettings, ProcessCallback& callback) { - const xmlAccess::XmlGlobalSettings defaultSettings; + const XmlGlobalSettings defaultSettings; std::wstring changedSettingsMsg; if (activeSettings.failSafeFileCopy != defaultSettings.failSafeFileCopy) @@ -809,7 +813,7 @@ void zen::logNonDefaultSettings(const xmlAccess::XmlGlobalSettings& activeSettin } -FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings, +FolderComparison fff::compare(WarningDialogs& warnings, int fileTimeTolerance, bool allowUserInteraction, bool runWithBackgroundPriority, @@ -823,8 +827,8 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings, //indicator at the very beginning of the log to make sense of "total time" //init process: keep at beginning so that all gui elements are initialized properly - callback.initNewPhase(-1, 0, ProcessCallback::PHASE_SCANNING); //may throw; it's not known how many files will be scanned => -1 objects - //callback.reportInfo(_("Starting comparison")); -> still useful? + callback.initNewPhase(-1, 0, ProcessCallback::PHASE_SCANNING); //may throw; it's unknown how many files will be scanned => -1 objects + //callback.reportInfo(Comparison started")); -> still useful? //------------------------------------------------------------------------------- @@ -923,9 +927,9 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings, for (const auto& w : workLoad) { if (basefolderExisting(w.first.folderPathLeft)) //only traverse *currently existing* folders: at this point user is aware that non-ex + empty string are seen as empty folder! - dirsToRead.emplace(w.first.folderPathLeft, w.second.filter.nameFilter, w.second.handleSymlinks); + dirsToRead.insert({ w.first.folderPathLeft, w.second.filter.nameFilter, w.second.handleSymlinks }); if (basefolderExisting(w.first.folderPathRight)) - dirsToRead.emplace(w.first.folderPathRight, w.second.filter.nameFilter, w.second.handleSymlinks); + dirsToRead.insert({ w.first.folderPathRight, w.second.filter.nameFilter, w.second.handleSymlinks }); } FolderComparison output; @@ -940,15 +944,9 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings, //process binary comparison as one junk std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>> workLoadByContent; for (const auto& w : workLoad) - switch (w.second.compareVar) - { - case CompareVariant::TIME_SIZE: - case CompareVariant::SIZE: - break; - case CompareVariant::CONTENT: - workLoadByContent.push_back(w); - break; - } + if (w.second.compareVar == CompareVariant::CONTENT) + workLoadByContent.push_back(w); + std::list<std::shared_ptr<BaseFolderPair>> outputByContent = cmpBuff.compareByContent(workLoadByContent); //write output in expected order @@ -980,11 +978,11 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings, const FolderPairCfg& fpCfg = cfgList[it - output.begin()]; callback.reportStatus(_("Calculating sync directions...")); - callback.forceUiRefresh(); + callback.forceUiRefresh(); //throw X tryReportingError([&] { - zen::redetermineSyncDirection(fpCfg.directionCfg, *it, //throw FileError + redetermineSyncDirection(fpCfg.directionCfg, *it, //throw FileError [&](const std::wstring& msg) { callback.reportStatus(msg); }); //throw X }, callback); //throw X? diff --git a/FreeFileSync/Source/comparison.h b/FreeFileSync/Source/comparison.h index 03afa415..5bc5cd9d 100755 --- a/FreeFileSync/Source/comparison.h +++ b/FreeFileSync/Source/comparison.h @@ -14,7 +14,7 @@ #include "lib/lock_holder.h" -namespace zen +namespace fff { struct FolderPairCfg { @@ -48,10 +48,10 @@ struct FolderPairCfg std::vector<FolderPairCfg> extractCompareCfg(const MainConfiguration& mainCfg); //fill FolderPairCfg and resolve folder pairs //inform about (important) non-default global settings related to comparison and synchronization -void logNonDefaultSettings(const xmlAccess::XmlGlobalSettings& currentSettings, ProcessCallback& callback); +void logNonDefaultSettings(const XmlGlobalSettings& currentSettings, ProcessCallback& callback); //FFS core routine: -FolderComparison compare(xmlAccess::OptionalDialogs& warnings, +FolderComparison compare(WarningDialogs& warnings, int fileTimeTolerance, bool allowUserInteraction, bool runWithBackgroundPriority, diff --git a/FreeFileSync/Source/file_hierarchy.cpp b/FreeFileSync/Source/file_hierarchy.cpp index dfe1bffb..bc6db6d6 100755 --- a/FreeFileSync/Source/file_hierarchy.cpp +++ b/FreeFileSync/Source/file_hierarchy.cpp @@ -10,6 +10,7 @@ #include <zen/file_error.h> using namespace zen; +using namespace fff; @@ -281,7 +282,7 @@ SyncOperation FilePair::getSyncOperation() const } -std::wstring zen::getCategoryDescription(CompareFilesResult cmpRes) +std::wstring fff::getCategoryDescription(CompareFilesResult cmpRes) { switch (cmpRes) { @@ -314,7 +315,7 @@ const wchar_t arrowRight[] = L"->"; } -std::wstring zen::getCategoryDescription(const FileSystemObject& fsObj) +std::wstring fff::getCategoryDescription(const FileSystemObject& fsObj) { const std::wstring footer = L"\n[" + utfTo<std::wstring>(fsObj. getPairItemName()) + L"]"; @@ -336,14 +337,14 @@ std::wstring zen::getCategoryDescription(const FileSystemObject& fsObj) [&](const FilePair& file) { descr += std::wstring(L"\n") + - arrowLeft + L" " + zen::formatUtcToLocalTime(file.getLastWriteTime< LEFT_SIDE>()) + L"\n" + - arrowRight + L" " + zen::formatUtcToLocalTime(file.getLastWriteTime<RIGHT_SIDE>()); + arrowLeft + L" " + formatUtcToLocalTime(file.getLastWriteTime< LEFT_SIDE>()) + L"\n" + + arrowRight + L" " + formatUtcToLocalTime(file.getLastWriteTime<RIGHT_SIDE>()); }, [&](const SymlinkPair& symlink) { descr += std::wstring(L"\n") + - arrowLeft + L" " + zen::formatUtcToLocalTime(symlink.getLastWriteTime< LEFT_SIDE>()) + L"\n" + - arrowRight + L" " + zen::formatUtcToLocalTime(symlink.getLastWriteTime<RIGHT_SIDE>()); + arrowLeft + L" " + formatUtcToLocalTime(symlink.getLastWriteTime< LEFT_SIDE>()) + L"\n" + + arrowRight + L" " + formatUtcToLocalTime(symlink.getLastWriteTime<RIGHT_SIDE>()); }); return descr + footer; } @@ -357,7 +358,7 @@ std::wstring zen::getCategoryDescription(const FileSystemObject& fsObj) } -std::wstring zen::getSyncOpDescription(SyncOperation op) +std::wstring fff::getSyncOpDescription(SyncOperation op) { switch (op) { @@ -395,7 +396,7 @@ std::wstring zen::getSyncOpDescription(SyncOperation op) } -std::wstring zen::getSyncOpDescription(const FileSystemObject& fsObj) +std::wstring fff::getSyncOpDescription(const FileSystemObject& fsObj) { const std::wstring footer = L"\n[" + utfTo<std::wstring>(fsObj. getPairItemName()) + L"]"; diff --git a/FreeFileSync/Source/file_hierarchy.h b/FreeFileSync/Source/file_hierarchy.h index 5b0ae6c7..d6a7c6fc 100755 --- a/FreeFileSync/Source/file_hierarchy.h +++ b/FreeFileSync/Source/file_hierarchy.h @@ -22,7 +22,7 @@ #include "fs/abstract.h" -namespace zen +namespace fff { using AFS = AbstractFileSystem; @@ -199,9 +199,9 @@ class ContainerObject : public virtual PathInformation friend class FileSystemObject; public: - using FileList = FixedList<FilePair>; //MergeSides::execute() requires a structure that doesn't invalidate pointers after push_back() - using SymlinkList = FixedList<SymlinkPair>; // - using FolderList = FixedList<FolderPair>; + using FileList = zen::FixedList<FilePair>; //MergeSides::execute() requires a structure that doesn't invalidate pointers after push_back() + using SymlinkList = zen::FixedList<SymlinkPair>; // + using FolderList = zen::FixedList<FolderPair>; FolderPair& addSubFolder(const Zstring& itemNameL, const FolderAttributes& left, //file exists on both sides @@ -545,9 +545,9 @@ private: void flip () override; void removeObjectL() override; void removeObjectR() override; - void notifySyncCfgChanged() override { syncOpBuffered_ = NoValue(); FileSystemObject::notifySyncCfgChanged(); ContainerObject::notifySyncCfgChanged(); } + void notifySyncCfgChanged() override { syncOpBuffered_ = zen::NoValue(); FileSystemObject::notifySyncCfgChanged(); ContainerObject::notifySyncCfgChanged(); } - mutable Opt<SyncOperation> syncOpBuffered_; //determining sync-op for directory may be expensive as it depends on child-objects => buffer + mutable zen::Opt<SyncOperation> syncOpBuffered_; //determining sync-op for directory may be expensive as it depends on child-objects => buffer FolderAttributes attrL_; FolderAttributes attrR_; diff --git a/FreeFileSync/Source/fs/abstract.cpp b/FreeFileSync/Source/fs/abstract.cpp index 4c100bdd..6442008d 100755 --- a/FreeFileSync/Source/fs/abstract.cpp +++ b/FreeFileSync/Source/fs/abstract.cpp @@ -10,12 +10,14 @@ #include <zen/crc.h> using namespace zen; +using namespace fff; using AFS = AbstractFileSystem; + const Zchar* AFS::TEMP_FILE_ENDING = Zstr(".ffs_tmp"); -bool zen::isValidRelPath(const Zstring& relPath) +bool fff::isValidRelPath(const Zstring& relPath) { const bool check1 = !contains(relPath, '\\'); const bool check2 = !startsWith(relPath, FILE_NAME_SEPARATOR) && !endsWith(relPath, FILE_NAME_SEPARATOR); @@ -88,7 +90,7 @@ AFS::FileCopyResult AFS::copyFileAsStream(const AfsPath& afsPathSource, const St bufferedStreamCopy(*streamIn, *streamOut); //throw FileError, ErrorFileLocked, X - const FileId targetFileId = streamOut->finalize(); //throw FileError, X + const AFS::FileId targetFileId = streamOut->finalize(); //throw FileError, X //check if "expected == actual number of bytes written" //-> extra check: bytes reported via notifyUnbufferedIO() should match actual number of bytes written diff --git a/FreeFileSync/Source/fs/abstract.h b/FreeFileSync/Source/fs/abstract.h index c612dfd9..4899f32a 100755 --- a/FreeFileSync/Source/fs/abstract.h +++ b/FreeFileSync/Source/fs/abstract.h @@ -15,7 +15,7 @@ #include "../lib/icon_holder.h" -namespace zen +namespace fff { struct AbstractFileSystem; @@ -65,9 +65,9 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t static Zstring getItemName(const AbstractPath& ap) { assert(getParentFolderPath(ap)); return getItemName(ap.afsPath); } - static Opt<Zstring> getNativeItemPath(const AbstractPath& ap) { return ap.afs->getNativeItemPath(ap.afsPath); } + static zen::Opt<Zstring> getNativeItemPath(const AbstractPath& ap) { return ap.afs->getNativeItemPath(ap.afsPath); } - static Opt<AbstractPath> getParentFolderPath(const AbstractPath& ap); + static zen::Opt<AbstractPath> getParentFolderPath(const AbstractPath& ap); struct PathComponents { @@ -91,7 +91,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t //(hopefully) fast: does not distinguish between error/not existing static ItemType getItemType(const AbstractPath& ap) { return ap.afs->getItemType(ap.afsPath); } //throw FileError //execute potentially SLOW folder traversal but distinguish error/not existing - static Opt<ItemType> getItemTypeIfExists(const AbstractPath& ap); //throw FileError + static zen::Opt<ItemType> getItemTypeIfExists(const AbstractPath& ap); //throw FileError static PathStatus getPathStatus(const AbstractPath& ap); //throw FileError //---------------------------------------------------------------------------------------------------------------- @@ -125,7 +125,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t static void connectNetworkFolder(const AbstractPath& ap, bool allowUserInteraction) { return ap.afs->connectNetworkFolder(ap.afsPath, allowUserInteraction); } //throw FileError //---------------------------------------------------------------------------------------------------------------- - using FileId = Zbase<char>; + using FileId = zen::Zbase<char>; struct StreamAttributes { @@ -142,7 +142,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t virtual size_t getBlockSize() const = 0; //non-zero block size is AFS contract! it's implementer's job to always give a reasonable buffer size! //only returns attributes if they are already buffered within stream handle and determination would be otherwise expensive (e.g. FTP/SFTP): - virtual Opt<StreamAttributes> getAttributesBuffered() = 0; //throw FileError + virtual zen::Opt<StreamAttributes> getAttributesBuffered() = 0; //throw FileError }; struct OutputStreamImpl @@ -164,18 +164,18 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t std::unique_ptr<OutputStreamImpl> outStream_; //bound! const AbstractPath filePath_; bool finalizeSucceeded_ = false; - Opt<uint64_t> bytesExpected_; + zen::Opt<uint64_t> bytesExpected_; uint64_t bytesWrittenTotal_ = 0; }; //return value always bound: - static std::unique_ptr<InputStream> getInputStream(const AbstractPath& ap, const IOCallback& notifyUnbufferedIO) //throw FileError, ErrorFileLocked, X + static std::unique_ptr<InputStream> getInputStream(const AbstractPath& ap, const zen::IOCallback& notifyUnbufferedIO) //throw FileError, ErrorFileLocked, X { return ap.afs->getInputStream(ap.afsPath, notifyUnbufferedIO); } //target existing: undefined behavior! (fail/overwrite/auto-rename) static std::unique_ptr<OutputStream> getOutputStream(const AbstractPath& ap, //throw FileError const uint64_t* streamSize, //optional - const IOCallback& notifyUnbufferedIO) // + const zen::IOCallback& notifyUnbufferedIO) // { return std::make_unique<OutputStream>(ap.afs->getOutputStream(ap.afsPath, streamSize, notifyUnbufferedIO), ap, streamSize); } //---------------------------------------------------------------------------------------------------------------- @@ -244,7 +244,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t time_t modTime = 0; //number of seconds since Jan. 1st 1970 UTC FileId sourceFileId; FileId targetFileId; - Opt<FileError> errorModTime; //failure to set modification time + zen::Opt<zen::FileError> errorModTime; //failure to set modification time }; //symlink handling: follow @@ -258,7 +258,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t //if transactionalCopy == true, full read access on source had been proven at this point, so it's safe to delete it. const std::function<void()>& onDeleteTargetFile, //accummulated delta != file size! consider ADS, sparse, compressed files - const IOCallback& notifyUnbufferedIO); + const zen::IOCallback& notifyUnbufferedIO); //target existing: undefined behavior! (fail/overwrite) //symlink handling: follow link! @@ -295,8 +295,8 @@ protected: //grant derived classes access to AbstractPath: static const AbstractFileSystem& getAfs (const AbstractPath& ap) { return *ap.afs; } static AfsPath getAfsPath(const AbstractPath& ap) { return ap.afsPath; } - static Zstring getItemName(const AfsPath& afsPath) { return afterLast(afsPath.value, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); } - static Opt<AfsPath> getParentAfsPath(const AfsPath& afsPath); + static Zstring getItemName(const AfsPath& afsPath) { using namespace zen; return afterLast(afsPath.value, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); } + static zen::Opt<AfsPath> getParentAfsPath(const AfsPath& afsPath); struct PathStatusImpl { @@ -308,10 +308,10 @@ protected: //grant derived classes access to AbstractPath: //target existing: undefined behavior! (fail/overwrite/auto-rename) FileCopyResult copyFileAsStream(const AfsPath& afsPathSource, const StreamAttributes& attrSource, //throw FileError, ErrorFileLocked - const AbstractPath& apTarget, const IOCallback& notifyUnbufferedIO) const; //may be nullptr; throw X! + const AbstractPath& apTarget, const zen::IOCallback& notifyUnbufferedIO) const; //may be nullptr; throw X! private: - virtual Opt<Zstring> getNativeItemPath(const AfsPath& afsPath) const { return NoValue(); }; + virtual zen::Opt<Zstring> getNativeItemPath(const AfsPath& afsPath) const { return zen::NoValue(); }; virtual Zstring getInitPathPhrase(const AfsPath& afsPath) const = 0; @@ -339,12 +339,12 @@ private: virtual AbstractPath getSymlinkResolvedPath(const AfsPath& afsPath) const = 0; //throw FileError virtual std::string getSymlinkBinaryContent(const AfsPath& afsPath) const = 0; //throw FileError //---------------------------------------------------------------------------------------------------------------- - virtual std::unique_ptr<InputStream> getInputStream (const AfsPath& afsPath, const IOCallback& notifyUnbufferedIO) const = 0; //throw FileError, ErrorFileLocked, X + virtual std::unique_ptr<InputStream> getInputStream (const AfsPath& afsPath, const zen::IOCallback& notifyUnbufferedIO) const = 0; //throw FileError, ErrorFileLocked, X //target existing: undefined behavior! (fail/overwrite/auto-rename) virtual std::unique_ptr<OutputStreamImpl> getOutputStream(const AfsPath& afsPath, //throw FileError const uint64_t* streamSize, //optional - const IOCallback& notifyUnbufferedIO) const = 0; // + const zen::IOCallback& notifyUnbufferedIO) const = 0; // //---------------------------------------------------------------------------------------------------------------- virtual void traverseFolder(const AfsPath& afsPath, TraverserCallback& sink /*throw X*/) const = 0; //throw X //---------------------------------------------------------------------------------------------------------------- @@ -358,7 +358,7 @@ private: virtual FileCopyResult copyFileForSameAfsType(const AfsPath& afsPathSource, const StreamAttributes& attrSource, //throw FileError, ErrorFileLocked const AbstractPath& apTarget, bool copyFilePermissions, //accummulated delta != file size! consider ADS, sparse, compressed files - const IOCallback& notifyUnbufferedIO) const = 0; //may be nullptr; throw X! + const zen::IOCallback& notifyUnbufferedIO) const = 0; //may be nullptr; throw X! //target existing: undefined behavior! (fail/overwrite) @@ -391,7 +391,7 @@ bool tryReportingDirError(Command cmd, AbstractFileSystem::TraverserCallback& ca cmd(); //throw FileError return true; } - catch (const FileError& e) + catch (const zen::FileError& e) { switch (callback.reportDirError(e.toString(), retryNumber)) //throw X { @@ -413,7 +413,7 @@ bool tryReportingItemError(Command cmd, AbstractFileSystem::TraverserCallback& c cmd(); //throw FileError return true; } - catch (const FileError& e) + catch (const zen::FileError& e) { switch (callback.reportItemError(e.toString(), retryNumber, itemName)) //throw X { @@ -436,6 +436,8 @@ bool tryReportingItemError(Command cmd, AbstractFileSystem::TraverserCallback& c inline AbstractPath AbstractFileSystem::appendRelPath(const AbstractPath& ap, const Zstring& relPath) { + using namespace zen; + assert(isValidRelPath(relPath)); return AbstractPath(ap.afs, AfsPath(appendPaths(ap.afsPath.value, relPath, FILE_NAME_SEPARATOR))); } @@ -444,6 +446,8 @@ AbstractPath AbstractFileSystem::appendRelPath(const AbstractPath& ap, const Zst inline Zstring AbstractFileSystem::appendPaths(const Zstring& basePath, const Zstring& relPath, Zchar pathSep) { + using namespace zen; + assert(!startsWith(relPath, pathSep) && !endsWith(relPath, pathSep)); if (relPath.empty()) return basePath; @@ -482,6 +486,8 @@ AbstractFileSystem::OutputStream::OutputStream(std::unique_ptr<OutputStreamImpl> inline AbstractFileSystem::OutputStream::~OutputStream() { + using namespace zen; + //we delete the file on errors: => file should not have existed prior to creating OutputStream instance!! outStream_.reset(); //close file handle *before* remove! @@ -502,6 +508,8 @@ void AbstractFileSystem::OutputStream::write(const void* data, size_t len) //thr inline AbstractFileSystem::FileId AbstractFileSystem::OutputStream::finalize() //throw FileError, X { + using namespace zen; + //important check: catches corrupt SFTP download with libssh2! if (bytesExpected_ && *bytesExpected_ != bytesWrittenTotal_) throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getDisplayPath(filePath_))), //instead we should report the source file, but don't have it here... @@ -530,6 +538,8 @@ bool AbstractFileSystem::supportPermissionCopy(const AbstractPath& apSource, con inline void AbstractFileSystem::renameItem(const AbstractPath& apSource, const AbstractPath& apTarget) //throw FileError, ErrorDifferentVolume { + using namespace zen; + if (typeid(*apSource.afs) == typeid(*apTarget.afs)) return apSource.afs->renameItemForSameAfsType(apSource.afsPath, apTarget); //throw FileError, ErrorDifferentVolume @@ -543,6 +553,8 @@ void AbstractFileSystem::renameItem(const AbstractPath& apSource, const Abstract inline void AbstractFileSystem::copyNewFolder(const AbstractPath& apSource, const AbstractPath& apTarget, bool copyFilePermissions) //throw FileError { + using namespace zen; + if (typeid(*apSource.afs) == typeid(*apTarget.afs)) return apSource.afs->copyNewFolderForSameAfsType(apSource.afsPath, apTarget, copyFilePermissions); //throw FileError @@ -559,6 +571,8 @@ void AbstractFileSystem::copyNewFolder(const AbstractPath& apSource, const Abstr inline void AbstractFileSystem::copySymlink(const AbstractPath& apSource, const AbstractPath& apTarget, bool copyFilePermissions) //throw FileError { + using namespace zen; + if (typeid(*apSource.afs) == typeid(*apTarget.afs)) return apSource.afs->copySymlinkForSameAfsType(apSource.afsPath, apTarget, copyFilePermissions); //throw FileError diff --git a/FreeFileSync/Source/fs/concrete.cpp b/FreeFileSync/Source/fs/concrete.cpp index 056daace..ebf0778b 100755 --- a/FreeFileSync/Source/fs/concrete.cpp +++ b/FreeFileSync/Source/fs/concrete.cpp @@ -7,10 +7,10 @@ #include "concrete.h" #include "native.h" -using namespace zen; +using namespace fff; -AbstractPath zen::createAbstractPath(const Zstring& itemPathPhrase) //noexcept +AbstractPath fff::createAbstractPath(const Zstring& itemPathPhrase) //noexcept { //greedy: try native evaluation first if (acceptsItemPathPhraseNative(itemPathPhrase)) //noexcept diff --git a/FreeFileSync/Source/fs/concrete.h b/FreeFileSync/Source/fs/concrete.h index 3e3a933d..82c04c5b 100755 --- a/FreeFileSync/Source/fs/concrete.h +++ b/FreeFileSync/Source/fs/concrete.h @@ -9,7 +9,7 @@ #include "abstract.h" -namespace zen +namespace fff { AbstractPath createAbstractPath(const Zstring& itemPathPhrase); //noexcept } diff --git a/FreeFileSync/Source/fs/native.cpp b/FreeFileSync/Source/fs/native.cpp index 8a9e461d..98a7becc 100755 --- a/FreeFileSync/Source/fs/native.cpp +++ b/FreeFileSync/Source/fs/native.cpp @@ -15,11 +15,16 @@ #include <zen/crc.h> #include "../lib/resolve_path.h" #include "../lib/icon_loader.h" -#include "native_traverser_impl.h" + + #include <cstddef> //offsetof + #include <sys/stat.h> + #include <dirent.h> #include <fcntl.h> //fallocate, fcntl using namespace zen; +using namespace fff; +using AFS = AbstractFileSystem; namespace @@ -30,6 +35,158 @@ void initComForThread() //throw FileError { } +//==================================================================================================== +//==================================================================================================== + +inline +AFS::FileId convertToAbstractFileId(const zen::FileId& fid) +{ + if (fid == zen::FileId()) + return AFS::FileId(); + + AFS::FileId out(reinterpret_cast<const char*>(&fid.volumeId), sizeof(fid.volumeId)); + out. append(reinterpret_cast<const char*>(&fid.fileIndex), sizeof(fid.fileIndex)); + return out; +} + + +class DirTraverser +{ +public: + static void execute(const Zstring& baseDirPath, AFS::TraverserCallback& sink) + { + DirTraverser(baseDirPath, sink); //throw X + } + +private: + DirTraverser(const Zstring& baseDirPath, AFS::TraverserCallback& sink) + { + /* quote: "Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede + that field within the dirent structure, portable applications that use readdir_r() should allocate + the buffer whose address is passed in entry as follows: + len = offsetof(struct dirent, d_name) + pathconf(dirPath, _PC_NAME_MAX) + 1 + entryp = malloc(len); */ + const size_t nameMax = std::max<long>(::pathconf(baseDirPath.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1) + buffer_.resize(offsetof(struct ::dirent, d_name) + nameMax + 1); + + traverse(baseDirPath, sink); //throw X + } + + DirTraverser (const DirTraverser&) = delete; + DirTraverser& operator=(const DirTraverser&) = delete; + + void traverse(const Zstring& dirPath, AFS::TraverserCallback& sink) //throw X + { + tryReportingDirError([&] //throw X + { + traverseWithException(dirPath, sink); //throw FileError, X + }, sink); + } + + void traverseWithException(const Zstring& dirPath, AFS::TraverserCallback& sink) //throw FileError, X + { + //no need to check for endless recursion: + //1. Linux has a fixed limit on the number of symbolic links in a path + //2. fails with "too many open files" or "path too long" before reaching stack overflow + + DIR* folder = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/" + if (!folder) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir"); + ZEN_ON_SCOPE_EXIT(::closedir(folder)); //never close nullptr handles! -> crash + + for (;;) + { + struct ::dirent* dirEntry = nullptr; + if (::readdir_r(folder, reinterpret_cast< ::dirent*>(&buffer_[0]), &dirEntry) != 0) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r"); + //don't retry but restart dir traversal on error! https://blogs.msdn.microsoft.com/oldnewthing/20140612-00/?p=753/ + + if (!dirEntry) //no more items + return; + + const char* itemNameRaw = dirEntry->d_name; //evaluate dirEntry *before* going into recursion => we use a single "buffer"! + + //skip "." and ".." + if (itemNameRaw[0] == '.' && + (itemNameRaw[1] == 0 || (itemNameRaw[1] == '.' && itemNameRaw[2] == 0))) + continue; + const Zstring& itemName = itemNameRaw; + if (itemName.empty()) //checks result of normalizeUtfForPosix, too! + throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name."); + + const Zstring& itemPath = appendSeparator(dirPath) + itemName; + + struct ::stat statData = {}; + 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"); + }, sink, itemName)) + continue; //ignore error: skip file + + if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks! + { + const AFS::TraverserCallback::SymlinkInfo linkInfo = { itemName, statData.st_mtime }; + + switch (sink.onSymlink(linkInfo)) //throw X + { + case AFS::TraverserCallback::LINK_FOLLOW: + { + //try to resolve symlink (and report error on failure!!!) + struct ::stat statDataTrg = {}; + + 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"); + }, sink, itemName); + + if (validLink) + { + if (S_ISDIR(statDataTrg.st_mode)) //a directory + { + if (std::unique_ptr<AFS::TraverserCallback> trav = sink.onFolder({ itemName, &linkInfo })) //throw X + traverse(itemPath, *trav); //throw X + } + else //a file or named pipe, ect. + { + AFS::TraverserCallback::FileInfo fi = { itemName, makeUnsigned(statDataTrg.st_size), statDataTrg.st_mtime, convertToAbstractFileId(extractFileId(statDataTrg)), &linkInfo }; + sink.onFile(fi); //throw X + } + } + // else //broken symlink -> ignore: it's client's responsibility to handle error! + } + break; + + case AFS::TraverserCallback::LINK_SKIP: + break; + } + } + 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); //throw X + } + else //a file or named pipe, ect. + { + AFS::TraverserCallback::FileInfo fi = { itemName, makeUnsigned(statData.st_size), statData.st_mtime, convertToAbstractFileId(extractFileId(statData)), nullptr /*symlinkInfo*/ }; + sink.onFile(fi); //throw X + } + /* + It may be a good idea to not check "S_ISREG(statData.st_mode)" explicitly and to not issue an error message on other types to support these scenarios: + - RTS setup watch (essentially wants to read directories only) + - removeDirectory (wants to delete everything; pipes can be deleted just like files via "unlink") + + However an "open" on a pipe will block (https://sourceforge.net/p/freefilesync/bugs/221/), so the copy routines need to be smarter!! + */ + } + } + + std::vector<char> buffer_; +}; + +//==================================================================================================== +//==================================================================================================== class RecycleSessionNative : public AbstractFileSystem::RecycleSession { @@ -314,7 +471,7 @@ private: try { initComForThread(); //throw FileError - return zen::getFileIcon(getNativePath(afsPath), pixelSize); + return fff::getFileIcon(getNativePath(afsPath), pixelSize); } catch (FileError&) { assert(false); return ImageHolder(); } } @@ -324,7 +481,7 @@ private: try { initComForThread(); //throw FileError - return zen::getThumbnailImage(getNativePath(afsPath), pixelSize); + return fff::getThumbnailImage(getNativePath(afsPath), pixelSize); } catch (FileError&) { assert(false); return ImageHolder(); } } @@ -387,7 +544,7 @@ void RecycleSessionNative::tryCleanup(const std::function<void (const std::wstri //coordinate changes with getResolvedFilePath()! -bool zen::acceptsItemPathPhraseNative(const Zstring& itemPathPhrase) //noexcept +bool fff::acceptsItemPathPhraseNative(const Zstring& itemPathPhrase) //noexcept { Zstring path = itemPathPhrase; path = expandMacros(path); //expand before trimming! @@ -403,7 +560,7 @@ bool zen::acceptsItemPathPhraseNative(const Zstring& itemPathPhrase) //noexcept } -AbstractPath zen::createItemPathNative(const Zstring& itemPathPhrase) //noexcept +AbstractPath fff::createItemPathNative(const Zstring& itemPathPhrase) //noexcept { //TODO: get volume by name hangs for idle HDD! => run createItemPathNative during getFolderStatusNonBlocking() but getResolvedFilePath currently not thread-safe! const Zstring itemPath = getResolvedFilePath(itemPathPhrase); @@ -411,7 +568,7 @@ AbstractPath zen::createItemPathNative(const Zstring& itemPathPhrase) //noexcept } -AbstractPath zen::createItemPathNativeNoFormatting(const Zstring& nativePath) //noexcept +AbstractPath fff::createItemPathNativeNoFormatting(const Zstring& nativePath) //noexcept { if (const Opt<PathComponents> comp = parsePathComponents(nativePath)) return AbstractPath(std::make_shared<NativeFileSystem>(comp->rootPath), AfsPath(comp->relPath)); diff --git a/FreeFileSync/Source/fs/native.h b/FreeFileSync/Source/fs/native.h index b8957a51..d7d8c1bb 100755 --- a/FreeFileSync/Source/fs/native.h +++ b/FreeFileSync/Source/fs/native.h @@ -9,7 +9,7 @@ #include "abstract.h" -namespace zen +namespace fff { bool acceptsItemPathPhraseNative (const Zstring& itemPathPhrase); //noexcept AbstractPath createItemPathNative(const Zstring& itemPathPhrase); //noexcept diff --git a/FreeFileSync/Source/fs/native_traverser_impl.h b/FreeFileSync/Source/fs/native_traverser_impl.h deleted file mode 100755 index cc4d8a52..00000000 --- a/FreeFileSync/Source/fs/native_traverser_impl.h +++ /dev/null @@ -1,171 +0,0 @@ -// ***************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * -// ***************************************************************************** - -#include <zen/sys_error.h> -#include <zen/symlink_target.h> -#include <zen/file_access.h> - - - #include <cstddef> //offsetof - #include <sys/stat.h> - #include <dirent.h> - - -//implementation header for native.cpp, not for reuse!!! - -namespace -{ -using namespace zen; -using AFS = AbstractFileSystem; - - -inline -AFS::FileId convertToAbstractFileId(const zen::FileId& fid) -{ - if (fid == zen::FileId()) - return AFS::FileId(); - - AFS::FileId out(reinterpret_cast<const char*>(&fid.volumeId), sizeof(fid.volumeId)); - out. append(reinterpret_cast<const char*>(&fid.fileIndex), sizeof(fid.fileIndex)); - return out; -} - - -class DirTraverser -{ -public: - static void execute(const Zstring& baseDirPath, AFS::TraverserCallback& sink) - { - DirTraverser(baseDirPath, sink); //throw X - } - -private: - DirTraverser(const Zstring& baseDirPath, AFS::TraverserCallback& sink) - { - /* quote: "Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede - that field within the dirent structure, portable applications that use readdir_r() should allocate - the buffer whose address is passed in entry as follows: - len = offsetof(struct dirent, d_name) + pathconf(dirPath, _PC_NAME_MAX) + 1 - entryp = malloc(len); */ - const size_t nameMax = std::max<long>(::pathconf(baseDirPath.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1) - buffer_.resize(offsetof(struct ::dirent, d_name) + nameMax + 1); - - traverse(baseDirPath, sink); //throw X - } - - DirTraverser (const DirTraverser&) = delete; - DirTraverser& operator=(const DirTraverser&) = delete; - - void traverse(const Zstring& dirPath, AFS::TraverserCallback& sink) //throw X - { - tryReportingDirError([&] //throw X - { - traverseWithException(dirPath, sink); //throw FileError, X - }, sink); - } - - void traverseWithException(const Zstring& dirPath, AFS::TraverserCallback& sink) //throw FileError, X - { - //no need to check for endless recursion: - //1. Linux has a fixed limit on the number of symbolic links in a path - //2. fails with "too many open files" or "path too long" before reaching stack overflow - - DIR* folder = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/" - if (!folder) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir"); - ZEN_ON_SCOPE_EXIT(::closedir(folder)); //never close nullptr handles! -> crash - - for (;;) - { - struct ::dirent* dirEntry = nullptr; - if (::readdir_r(folder, reinterpret_cast< ::dirent*>(&buffer_[0]), &dirEntry) != 0) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r"); - //don't retry but restart dir traversal on error! https://blogs.msdn.microsoft.com/oldnewthing/20140612-00/?p=753/ - - if (!dirEntry) //no more items - return; - - const char* itemNameRaw = dirEntry->d_name; //evaluate dirEntry *before* going into recursion => we use a single "buffer"! - - //skip "." and ".." - if (itemNameRaw[0] == '.' && - (itemNameRaw[1] == 0 || (itemNameRaw[1] == '.' && itemNameRaw[2] == 0))) - continue; - const Zstring& itemName = itemNameRaw; - if (itemName.empty()) //checks result of osx::normalizeUtfForPosix, too! - throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name."); - - const Zstring& itemPath = appendSeparator(dirPath) + itemName; - - struct ::stat statData = {}; - 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"); - }, sink, itemName)) - continue; //ignore error: skip file - - if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks! - { - const AFS::TraverserCallback::SymlinkInfo linkInfo = { itemName, statData.st_mtime }; - - switch (sink.onSymlink(linkInfo)) //throw X - { - case AFS::TraverserCallback::LINK_FOLLOW: - { - //try to resolve symlink (and report error on failure!!!) - struct ::stat statDataTrg = {}; - - 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"); - }, sink, itemName); - - if (validLink) - { - if (S_ISDIR(statDataTrg.st_mode)) //a directory - { - if (std::unique_ptr<AFS::TraverserCallback> trav = sink.onFolder({ itemName, &linkInfo })) //throw X - traverse(itemPath, *trav); //throw X - } - else //a file or named pipe, ect. - { - AFS::TraverserCallback::FileInfo fi = { itemName, makeUnsigned(statDataTrg.st_size), statDataTrg.st_mtime, convertToAbstractFileId(extractFileId(statDataTrg)), &linkInfo }; - sink.onFile(fi); //throw X - } - } - // else //broken symlink -> ignore: it's client's responsibility to handle error! - } - break; - - case AFS::TraverserCallback::LINK_SKIP: - break; - } - } - 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); //throw X - } - else //a file or named pipe, ect. - { - AFS::TraverserCallback::FileInfo fi = { itemName, makeUnsigned(statData.st_size), statData.st_mtime, convertToAbstractFileId(extractFileId(statData)), nullptr /*symlinkInfo*/ }; - sink.onFile(fi); //throw X - } - /* - It may be a good idea to not check "S_ISREG(statData.st_mode)" explicitly and to not issue an error message on other types to support these scenarios: - - RTS setup watch (essentially wants to read directories only) - - removeDirectory (wants to delete everything; pipes can be deleted just like files via "unlink") - - However an "open" on a pipe will block (https://sourceforge.net/p/freefilesync/bugs/221/), so the copy routines need to be smarter!! - */ - } - } - - std::vector<char> buffer_; -}; -} diff --git a/FreeFileSync/Source/lib/binary.cpp b/FreeFileSync/Source/lib/binary.cpp index d7f7bf63..e4ac6fc3 100755 --- a/FreeFileSync/Source/lib/binary.cpp +++ b/FreeFileSync/Source/lib/binary.cpp @@ -9,6 +9,7 @@ #include <chrono> using namespace zen; +using namespace fff; using AFS = AbstractFileSystem; namespace @@ -62,9 +63,9 @@ struct StreamReader } size_t proposedBlockSize = 0; - const auto loopTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count(); + const auto loopTime = stopTime - startTime; - if (loopTimeMs >= 100) + if (loopTime >= std::chrono::milliseconds(100)) lastDelayViolation_ = stopTime; //avoid "flipping back": e.g. DVD-ROMs read 32MB at once, so first read may be > 500 ms, but second one will be 0ms! @@ -73,7 +74,7 @@ struct StreamReader lastDelayViolation_ = stopTime; proposedBlockSize = dynamicBlockSize_ * 2; } - if (loopTimeMs > 500) + if (loopTime > std::chrono::milliseconds(500)) proposedBlockSize = dynamicBlockSize_ / 2; if (defaultBlockSize_ <= proposedBlockSize && proposedBlockSize <= BLOCK_SIZE_MAX) @@ -92,7 +93,7 @@ private: } -bool zen::filesHaveSameContent(const AbstractPath& filePath1, const AbstractPath& filePath2, const IOCallback& notifyUnbufferedIO) //throw FileError +bool fff::filesHaveSameContent(const AbstractPath& filePath1, const AbstractPath& filePath2, const IOCallback& notifyUnbufferedIO) //throw FileError { int64_t totalUnbufferedIO = 0; diff --git a/FreeFileSync/Source/lib/binary.h b/FreeFileSync/Source/lib/binary.h index 914fedea..bba321da 100755 --- a/FreeFileSync/Source/lib/binary.h +++ b/FreeFileSync/Source/lib/binary.h @@ -10,11 +10,11 @@ #include "../fs/abstract.h" -namespace zen +namespace fff { bool filesHaveSameContent(const AbstractPath& filePath1, //throw FileError const AbstractPath& filePath2, - const IOCallback& notifyUnbufferedIO); //may be nullptr + const zen::IOCallback& notifyUnbufferedIO); //may be nullptr } #endif //BINARY_H_3941281398513241134 diff --git a/FreeFileSync/Source/lib/cmp_filetime.h b/FreeFileSync/Source/lib/cmp_filetime.h index e3c490df..ce3a04e9 100755 --- a/FreeFileSync/Source/lib/cmp_filetime.h +++ b/FreeFileSync/Source/lib/cmp_filetime.h @@ -11,7 +11,7 @@ #include <algorithm> -namespace zen +namespace fff { inline bool sameFileTime(int64_t lhs, int64_t rhs, int tolerance, const std::vector<unsigned int>& ignoreTimeShiftMinutes) diff --git a/FreeFileSync/Source/lib/db_file.cpp b/FreeFileSync/Source/lib/db_file.cpp index c4cfd837..6b13014d 100755 --- a/FreeFileSync/Source/lib/db_file.cpp +++ b/FreeFileSync/Source/lib/db_file.cpp @@ -11,6 +11,7 @@ using namespace zen; +using namespace fff; namespace @@ -23,7 +24,7 @@ const int DB_FORMAT_STREAM = 3; // struct SessionData { - bool isLeadStream; + bool isLeadStream = false; ByteArray rawStream; }; bool operator==(const SessionData& lhs, const SessionData& rhs) { return lhs.isLeadStream == rhs.isLeadStream && lhs.rawStream == rhs.rawStream; } @@ -42,7 +43,6 @@ AbstractPath getDatabaseFilePath(const BaseFolderPair& baseFolder, bool tempfile //precomposed/decomposed UTF? are UTC file times really compatible? what about endianess!? //however 32 and 64-bit FreeFileSync are designed to produce binary-identical db files! //Give db files different names. - //make sure they end with ".ffs_db". These files will be excluded from comparison const Zstring dbName = Zstr(".sync"); //files beginning with dots are hidden e.g. in Nautilus Zstring dbFileName; if (tempfile) //generate (hopefully) unique file name to avoid clashing with some remnant ffs_tmp file @@ -790,13 +790,13 @@ std::pair<DbStreams::const_iterator, throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + _("The database files do not yet contain information about the last synchronization.")); - return std::make_pair(itCommonL, itCommonR); + return { itCommonL, itCommonR }; } } //####################################################################################################################################### -std::shared_ptr<InSyncFolder> zen::loadLastSynchronousState(const BaseFolderPair& baseFolder, //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! +std::shared_ptr<InSyncFolder> fff::loadLastSynchronousState(const BaseFolderPair& baseFolder, //throw FileError, FileErrorDatabaseNotExisting -> return value always bound! const std::function<void(const std::wstring& statusMsg)>& notifyStatus) { const AbstractPath dbPathLeft = getDatabaseFilePath< LEFT_SIDE>(baseFolder); @@ -836,7 +836,7 @@ std::shared_ptr<InSyncFolder> zen::loadLastSynchronousState(const BaseFolderPair } -void zen::saveLastSynchronousState(const BaseFolderPair& baseFolder, const std::function<void(const std::wstring& statusMsg)>& notifyStatus) //throw FileError +void fff::saveLastSynchronousState(const BaseFolderPair& baseFolder, const std::function<void(const std::wstring& statusMsg)>& notifyStatus) //throw FileError { //transactional behaviour! write to tmp files first const AbstractPath dbPathLeft = getDatabaseFilePath< LEFT_SIDE>(baseFolder); @@ -861,19 +861,16 @@ void zen::saveLastSynchronousState(const BaseFolderPair& baseFolder, const std:: catch (FileError&) {} //if error occurs: just overwrite old file! User is already informed about issues right after comparing! - std::shared_ptr<InSyncFolder> lastSyncState = std::make_shared<InSyncFolder>(InSyncFolder::DIR_STATUS_IN_SYNC); + auto lastSyncState = std::make_shared<InSyncFolder>(InSyncFolder::DIR_STATUS_IN_SYNC); auto itStreamOldL = streamsLeft .cend(); auto itStreamOldR = streamsRight.cend(); try { //find associated session: there can be at most one session within intersection of left and right ids - std::pair<DbStreams::const_iterator, - DbStreams::const_iterator> session = getCommonSession(streamsLeft, streamsRight, //throw FileError, FileErrorDatabaseNotExisting - AFS::getDisplayPath(dbPathLeft), - AFS::getDisplayPath(dbPathRight)); + std::tie(itStreamOldL, itStreamOldR) = getCommonSession(streamsLeft, streamsRight, //throw FileError, FileErrorDatabaseNotExisting + AFS::getDisplayPath(dbPathLeft), + AFS::getDisplayPath(dbPathRight)); - itStreamOldL = session.first; - itStreamOldR = session.second; const bool leadStreamLeft = itStreamOldL->second.isLeadStream; //load last synchrounous state @@ -925,8 +922,8 @@ void zen::saveLastSynchronousState(const BaseFolderPair& baseFolder, const std:: //operation finished: rename temp files -> this should work (almost) transactionally: //if there were no write access, creation of temp files would have failed - AFS::removeFileIfExists(dbPathLeft); //throw FileError - AFS::renameItem(dbPathLeftTmp, dbPathLeft); //throw FileError, (ErrorDifferentVolume) + AFS::removeFileIfExists(dbPathLeft); //throw FileError + AFS::renameItem(dbPathLeftTmp, dbPathLeft); //throw FileError, (ErrorDifferentVolume) guardTmpL.dismiss(); AFS::removeFileIfExists(dbPathRight); // diff --git a/FreeFileSync/Source/lib/db_file.h b/FreeFileSync/Source/lib/db_file.h index 99bcfb79..df61d892 100755 --- a/FreeFileSync/Source/lib/db_file.h +++ b/FreeFileSync/Source/lib/db_file.h @@ -11,7 +11,7 @@ #include "../file_hierarchy.h" -namespace zen +namespace fff { const Zchar SYNC_DB_FILE_ENDING[] = Zstr(".ffs_db"); //don't use Zstring as global constant: avoid static initialization order problem in global namespace! @@ -21,7 +21,7 @@ struct InSyncDescrFile //subset of FileAttributes modTime(modTimeIn), fileId(idIn) {} - time_t modTime; + time_t modTime = 0; AFS::FileId fileId; // == file id: optional! (however, always set on Linux, and *generally* available on Windows) }; @@ -29,7 +29,7 @@ struct InSyncDescrLink { explicit InSyncDescrLink(time_t modTimeIn) : modTime(modTimeIn) {} - time_t modTime; + time_t modTime = 0; }; @@ -39,8 +39,8 @@ struct InSyncFile InSyncFile(const InSyncDescrFile& l, const InSyncDescrFile& r, CompareVariant cv, uint64_t fileSizeIn) : left(l), right(r), cmpVar(cv), fileSize(fileSizeIn) {} InSyncDescrFile left; //support flip()! InSyncDescrFile right; // - CompareVariant cmpVar; //the one active while finding "file in sync" - uint64_t fileSize; //file size must be identical on both sides! + CompareVariant cmpVar = CompareVariant::TIME_SIZE; //the one active while finding "file in sync" + uint64_t fileSize = 0; //file size must be identical on both sides! }; struct InSyncSymlink @@ -48,7 +48,7 @@ struct InSyncSymlink InSyncSymlink(const InSyncDescrLink& l, const InSyncDescrLink& r, CompareVariant cv) : left(l), right(r), cmpVar(cv) {} InSyncDescrLink left; InSyncDescrLink right; - CompareVariant cmpVar; + CompareVariant cmpVar = CompareVariant::TIME_SIZE; }; struct InSyncFolder @@ -62,7 +62,7 @@ struct InSyncFolder }; InSyncFolder(InSyncStatus statusIn) : status(statusIn) {} - InSyncStatus status; + InSyncStatus status = DIR_STATUS_STRAW_MAN; //------------------------------------------------------------------ using FolderList = std::map<Zstring, InSyncFolder, LessFilePath>; // diff --git a/FreeFileSync/Source/lib/dir_exist_async.h b/FreeFileSync/Source/lib/dir_exist_async.h index 47bf5241..e61cdc41 100755 --- a/FreeFileSync/Source/lib/dir_exist_async.h +++ b/FreeFileSync/Source/lib/dir_exist_async.h @@ -15,7 +15,7 @@ #include "../process_callback.h" -namespace zen +namespace fff { namespace { @@ -27,7 +27,7 @@ struct FolderStatus { std::set<AbstractPath, AFS::LessAbstractPath> existing; std::set<AbstractPath, AFS::LessAbstractPath> notExisting; - std::map<AbstractPath, FileError, AFS::LessAbstractPath> failedChecks; + std::map<AbstractPath, zen::FileError, AFS::LessAbstractPath> failedChecks; }; FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath, AFS::LessAbstractPath>& folderPaths, int folderAccessTimeout, bool allowUserInteraction, ProcessCallback& procCallback) @@ -60,7 +60,7 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath, AFS::LessAb procCallback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", displayPathFmt)); //may throw! while (numeric::dist(std::chrono::steady_clock::now(), startTime) < std::chrono::seconds(folderAccessTimeout) && //handle potential chrono wrap-around! - fi.second.wait_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS / 2)) != std::future_status::ready) + fi.second.wait_for(UI_UPDATE_INTERVAL / 2) != std::future_status::ready) procCallback.requestUiRefresh(); //may throw! if (isReady(fi.second)) diff --git a/FreeFileSync/Source/lib/dir_lock.cpp b/FreeFileSync/Source/lib/dir_lock.cpp index 03149a77..ab41804f 100755 --- a/FreeFileSync/Source/lib/dir_lock.cpp +++ b/FreeFileSync/Source/lib/dir_lock.cpp @@ -6,12 +6,10 @@ #include "dir_lock.h" #include <map> #include <memory> -//#include <chrono> #include <zen/sys_error.h> #include <zen/thread.h> #include <zen/scope_guard.h> #include <zen/guid.h> -//#include <zen/tick_count.h> #include <zen/file_access.h> #include <zen/file_io.h> #include <zen/optional.h> @@ -25,13 +23,14 @@ #include <pwd.h> //getpwuid_r() using namespace zen; +using namespace fff; namespace { -const int EMIT_LIFE_SIGN_INTERVAL = 5; //show life sign; unit: [s] -const int POLL_LIFE_SIGN_INTERVAL = 4; //poll for life sign; unit: [s] -const int DETECT_ABANDONED_INTERVAL = 30; //assume abandoned lock; unit: [s] +const std::chrono::seconds EMIT_LIFE_SIGN_INTERVAL (5); //show life sign; +const std::chrono::seconds POLL_LIFE_SIGN_INTERVAL (4); //poll for life sign; +const std::chrono::seconds DETECT_ABANDONED_INTERVAL(30); //assume abandoned lock; const char LOCK_FORMAT_DESCR[] = "FreeFileSync"; const int LOCK_FORMAT_VER = 2; //lock file format version @@ -52,7 +51,7 @@ public: { for (;;) { - interruptibleSleep(std::chrono::seconds(EMIT_LIFE_SIGN_INTERVAL)); //throw ThreadInterruption + interruptibleSleep(EMIT_LIFE_SIGN_INTERVAL); //throw ThreadInterruption //actual work emitLifeSign(); //throw () @@ -91,6 +90,8 @@ Zstring abandonedLockDeletionName(const Zstring& lockFilePath) //make sure to NO } +#if 0 +#endif using ProcessId = pid_t; @@ -249,63 +250,63 @@ ProcessStatus getProcessStatus(const LockInformation& lockInfo) //throw FileErro } -void waitOnDirLock(const Zstring& lockFilePath, DirLockCallback* callback) //throw FileError +void waitOnDirLock(const Zstring& lockFilePath, const DirLockCallback& notifyStatus /*throw X*/, std::chrono::milliseconds cbInterval) //throw FileError { - using namespace std::chrono; std::wstring infoMsg = _("Waiting while directory is locked:") + L' ' + fmtPath(lockFilePath); - if (callback) - callback->reportStatus(infoMsg); - //--------------------------------------------------------------- + if (notifyStatus) notifyStatus(infoMsg); //throw X + + //convenience optimization only: if we know the owning process crashed, we needn't wait DETECT_ABANDONED_INTERVAL sec + bool lockOwnderDead = false; + std::string originalLockId; //empty if it cannot be retrieved try { - //convenience optimization only: if we know the owning process crashed, we needn't wait DETECT_ABANDONED_INTERVAL sec - bool lockOwnderDead = false; - std::string originalLockId; //empty if it cannot be retrieved - try - { - const LockInformation& lockInfo = retrieveLockInfo(lockFilePath); //throw FileError - //enhance status message and show which user is holding the lock: - infoMsg += L" | " + _("Lock owner:") + L' ' + utfTo<std::wstring>(lockInfo.userId); + const LockInformation& lockInfo = retrieveLockInfo(lockFilePath); //throw FileError - originalLockId = lockInfo.lockId; - switch (getProcessStatus(lockInfo)) //throw FileError - { - case ProcessStatus::ITS_US: //since we've already passed LockAdmin, the lock file seems abandoned ("stolen"?) although it's from this process - case ProcessStatus::NOT_RUNNING: - lockOwnderDead = true; - break; - case ProcessStatus::RUNNING: - case ProcessStatus::CANT_TELL: - break; - } - } - catch (FileError&) {} //logfile may be only partly written -> this is no error! + infoMsg += L" | " + _("Lock owner:") + L' ' + utfTo<std::wstring>(lockInfo.userId); + originalLockId = lockInfo.lockId; + switch (getProcessStatus(lockInfo)) //throw FileError + { + case ProcessStatus::ITS_US: //since we've already passed LockAdmin, the lock file seems abandoned ("stolen"?) although it's from this process + case ProcessStatus::NOT_RUNNING: + lockOwnderDead = true; + break; + case ProcessStatus::RUNNING: + case ProcessStatus::CANT_TELL: + break; + } + } + catch (FileError&) {} //logfile may be only partly written -> this is no error! + //------------------------------------------------------------------------------ + try + { uint64_t fileSizeOld = 0; - auto lastLifeSign = steady_clock::now(); + auto lastLifeSign = std::chrono::steady_clock::now(); for (;;) { - const auto now = steady_clock::now(); const uint64_t fileSizeNew = getFileSize(lockFilePath); //throw FileError + const auto lastCheckTime = std::chrono::steady_clock::now(); if (fileSizeNew != fileSizeOld) //received life sign from lock { fileSizeOld = fileSizeNew; - lastLifeSign = now; + lastLifeSign = lastCheckTime; } if (lockOwnderDead || //no need to wait any longer... - now >= lastLifeSign + seconds(DETECT_ABANDONED_INTERVAL)) + lastCheckTime >= lastLifeSign + DETECT_ABANDONED_INTERVAL) { - DirLock dummy(abandonedLockDeletionName(lockFilePath), callback); //throw FileError + DirLock guardDeletion(abandonedLockDeletionName(lockFilePath), notifyStatus, cbInterval); //throw FileError //now that the lock is in place check existence again: meanwhile another process may have deleted and created a new lock! + std::string currentLockId; + try { currentLockId = retrieveLockId(lockFilePath); /*throw FileError*/ } + catch (FileError&) {} - if (!originalLockId.empty()) - if (retrieveLockId(lockFilePath) != originalLockId) //throw FileError -> since originalLockId is filled, we are not expecting errors! - return; //another process has placed a new lock, leave scope: the wait for the old lock is technically over... + if (currentLockId != originalLockId) //throw FileError + return; //another process has placed a new lock, leave scope: the wait for the old lock is technically over... if (getFileSize(lockFilePath) != fileSizeOld) //throw FileError continue; //late life sign @@ -315,29 +316,28 @@ void waitOnDirLock(const Zstring& lockFilePath, DirLockCallback* callback) //thr } //wait some time... - static_assert(1000 * POLL_LIFE_SIGN_INTERVAL % GUI_CALLBACK_INTERVAL == 0, ""); - for (size_t i = 0; i < 1000 * POLL_LIFE_SIGN_INTERVAL / GUI_CALLBACK_INTERVAL; ++i) + const auto delayUntil = std::chrono::steady_clock::now() + POLL_LIFE_SIGN_INTERVAL; + for (auto now = std::chrono::steady_clock::now(); now < delayUntil; now = std::chrono::steady_clock::now()) { - if (callback) callback->requestUiRefresh(); - std::this_thread::sleep_for(milliseconds(GUI_CALLBACK_INTERVAL)); - - if (callback) + if (notifyStatus) { //one signal missed: it's likely this is an abandoned lock => show countdown - if (now >= lastLifeSign + seconds(EMIT_LIFE_SIGN_INTERVAL + 1)) + if (lastCheckTime >= lastLifeSign + EMIT_LIFE_SIGN_INTERVAL + std::chrono::seconds(1)) { - const int remainingSeconds = std::max<int>(0, DETECT_ABANDONED_INTERVAL - duration_cast<seconds>(steady_clock::now() - lastLifeSign).count()); - const std::wstring remSecMsg = replaceCpy(_P("1 sec", "%x sec", remainingSeconds), L"%x", formatNumber(remainingSeconds)); - callback->reportStatus(infoMsg + L" | " + _("Detecting abandoned lock...") + L' ' + remSecMsg); + const int remainingSeconds = std::max<int>(0, std::chrono::duration_cast<std::chrono::seconds>(DETECT_ABANDONED_INTERVAL - (now - lastLifeSign)).count()); + notifyStatus(infoMsg + L" | " + _("Detecting abandoned lock...") + L' ' + _P("1 sec", "%x sec", remainingSeconds)); //throw X } else - callback->reportStatus(infoMsg); //emit a message in any case (might clear other one) + notifyStatus(infoMsg); //throw X; emit a message in any case (might clear other one) } + std::this_thread::sleep_for(cbInterval); } } } catch (FileError&) { + warn_static("race condition: above calls, e.g. getFileSize() might fail for not existing file, but another one might have been created at this point") + if (itemNotExisting(lockFilePath)) return; //what we are waiting for... throw; @@ -388,11 +388,13 @@ bool tryLock(const Zstring& lockFilePath) //throw FileError class DirLock::SharedDirLock { public: - SharedDirLock(const Zstring& lockFilePath, DirLockCallback* callback) : //throw FileError + SharedDirLock(const Zstring& lockFilePath, const DirLockCallback& notifyStatus, std::chrono::milliseconds cbInterval) : //throw FileError lockFilePath_(lockFilePath) { - while (!::tryLock(lockFilePath)) //throw FileError - ::waitOnDirLock(lockFilePath, callback); // + if (notifyStatus) notifyStatus(replaceCpy(_("Creating file %x"), L"%x", fmtPath(lockFilePath))); //throw X + + while (!::tryLock(lockFilePath)) //throw FileError + ::waitOnDirLock(lockFilePath, notifyStatus, cbInterval); // lifeSignthread_ = InterruptibleThread(LifeSigns(lockFilePath)); } @@ -424,7 +426,7 @@ public: } //create or retrieve a SharedDirLock - std::shared_ptr<SharedDirLock> retrieve(const Zstring& lockFilePath, DirLockCallback* callback) //throw FileError + std::shared_ptr<SharedDirLock> retrieve(const Zstring& lockFilePath, const DirLockCallback& notifyStatus, std::chrono::milliseconds cbInterval) //throw FileError { assert(std::this_thread::get_id() == mainThreadId); //function is not thread-safe! @@ -448,7 +450,7 @@ public: catch (FileError&) {} //catch everything, let SharedDirLock constructor deal with errors, e.g. 0-sized/corrupted lock files //lock not owned by us => create a new one - auto newLock = std::make_shared<SharedDirLock>(lockFilePath, callback); //throw FileError + auto newLock = std::make_shared<SharedDirLock>(lockFilePath, notifyStatus, cbInterval); //throw FileError const std::string& newLockGuid = retrieveLockId(lockFilePath); //throw FileError //update registry @@ -484,11 +486,20 @@ private: }; -DirLock::DirLock(const Zstring& lockFilePath, DirLockCallback* callback) //throw FileError +DirLock::DirLock(const Zstring& lockFilePath, const DirLockCallback& notifyStatus, std::chrono::milliseconds cbInterval) //throw FileError { - if (callback) - callback->reportStatus(replaceCpy(_("Creating file %x"), L"%x", fmtPath(lockFilePath))); - - - sharedLock_ = LockAdmin::instance().retrieve(lockFilePath, callback); //throw FileError + //#ifdef ZEN_WIN + // const DWORD bufferSize = 10000; + // std::vector<wchar_t> volName(bufferSize); + // if (::GetVolumePathName(lockFilePath.c_str(), //__in LPCTSTR lpszFileName, + // &volName[0], //__out LPTSTR lpszVolumePathName, + // bufferSize)) //__in DWORD cchBufferLength + // { + // const DWORD dt = ::GetDriveType(&volName[0]); + // if (dt == DRIVE_CDROM) + // return; //we don't need a lock for a CD ROM + // } + //#endif -> still relevant? better save the file I/O for the network scenario + + sharedLock_ = LockAdmin::instance().retrieve(lockFilePath, notifyStatus, cbInterval); //throw FileError } diff --git a/FreeFileSync/Source/lib/dir_lock.h b/FreeFileSync/Source/lib/dir_lock.h index 5a1e3b7e..910e551d 100755 --- a/FreeFileSync/Source/lib/dir_lock.h +++ b/FreeFileSync/Source/lib/dir_lock.h @@ -8,19 +8,13 @@ #define DIR_LOCK_H_81740832174954356 #include <memory> +#include <chrono> +#include <functional> #include <zen/file_error.h> -namespace zen -{ -const size_t GUI_CALLBACK_INTERVAL = 100; -struct DirLockCallback //while waiting for the lock +namespace fff { - virtual ~DirLockCallback() {} - virtual void requestUiRefresh() = 0; //allowed to throw exceptions - virtual void reportStatus(const std::wstring& text) = 0; -}; - /* RAII structure to place a directory lock against other FFS processes: - recursive locking supported, even with alternate lockfile names, e.g. via symlinks, network mounts etc. @@ -31,10 +25,13 @@ RAII structure to place a directory lock against other FFS processes: - race-free (Windows, almost on Linux(NFS)) - NOT thread-safe! (1. global LockAdmin 2. locks for directory aliases should be created sequentially to detect duplicate locks!) */ +//while waiting for the lock +using DirLockCallback = std::function<void(const std::wstring& msg)>; //throw X + class DirLock { public: - DirLock(const Zstring& lockFilePath, DirLockCallback* callback = nullptr); //throw FileError, callback only used during construction + DirLock(const Zstring& lockFilePath, const DirLockCallback& notifyStatus, std::chrono::milliseconds cbInterval); //throw FileError, callback only used during construction private: class LockAdmin; diff --git a/FreeFileSync/Source/lib/error_log.h b/FreeFileSync/Source/lib/error_log.h index 89c2c61d..062c8fbb 100755 --- a/FreeFileSync/Source/lib/error_log.h +++ b/FreeFileSync/Source/lib/error_log.h @@ -13,7 +13,7 @@ #include "ffs_paths.h" -namespace zen +namespace fff { //write error message to a file (even with corrupted stack)- call in desperate situations when no other means of error handling is available void logFatalError(const std::string& msg); //noexcept @@ -30,6 +30,8 @@ void logFatalError(const std::string& msg); //noexcept inline void logFatalError(const std::string& msg) //noexcept { + using namespace zen; + assert(false); //this is stuff we like to debug const std::string logEntry = "[" + formatTime<std::string>(FORMAT_DATE) + " "+ formatTime<std::string>(FORMAT_TIME) + "] " + msg; try diff --git a/FreeFileSync/Source/lib/ffs_paths.cpp b/FreeFileSync/Source/lib/ffs_paths.cpp index 0ae1f132..a9c43d36 100755 --- a/FreeFileSync/Source/lib/ffs_paths.cpp +++ b/FreeFileSync/Source/lib/ffs_paths.cpp @@ -25,14 +25,14 @@ Zstring getExecutablePathPf() //directory containing executable WITH path separa -bool zen::isPortableVersion() +bool fff::isPortableVersion() { return !endsWith(getExecutablePathPf(), "/bin/"); //this check is a bit lame... } -Zstring zen::getResourceDirPf() +Zstring fff::getResourceDirPf() { //make independent from wxWidgets global variable "appname"; support being called by RealTimeSync auto appName = wxTheApp->GetAppName(); @@ -46,7 +46,7 @@ Zstring zen::getResourceDirPf() } -Zstring zen::getConfigDirPathPf() +Zstring fff::getConfigDirPathPf() { //make independent from wxWidgets global variable "appname"; support being called by RealTimeSync auto appName = wxTheApp->GetAppName(); @@ -69,7 +69,7 @@ Zstring zen::getConfigDirPathPf() //this function is called by RealTimeSync!!! -Zstring zen::getFreeFileSyncLauncherPath() +Zstring fff::getFreeFileSyncLauncherPath() { return getExecutablePathPf() + Zstr("FreeFileSync"); diff --git a/FreeFileSync/Source/lib/ffs_paths.h b/FreeFileSync/Source/lib/ffs_paths.h index ba6f4b32..3cb4c07b 100755 --- a/FreeFileSync/Source/lib/ffs_paths.h +++ b/FreeFileSync/Source/lib/ffs_paths.h @@ -9,7 +9,8 @@ #include <zen/zstring.h> -namespace zen + +namespace fff { //------------------------------------------------------------------------------ //global program directories diff --git a/FreeFileSync/Source/lib/generate_logfile.h b/FreeFileSync/Source/lib/generate_logfile.h index 8906099b..0eb85762 100755 --- a/FreeFileSync/Source/lib/generate_logfile.h +++ b/FreeFileSync/Source/lib/generate_logfile.h @@ -15,50 +15,30 @@ #include "../file_hierarchy.h" -namespace zen +namespace fff { struct SummaryInfo { std::wstring jobName; //may be empty std::wstring finalStatus; - int itemsProcessed; - int64_t bytesProcessed; - int itemsTotal; - int64_t bytesTotal; - int64_t totalTime; //unit: [sec] + int itemsProcessed = 0; + int64_t bytesProcessed = 0; + int itemsTotal = 0; + int64_t bytesTotal = 0; + int64_t totalTime = 0; //unit: [sec] }; void streamToLogFile(const SummaryInfo& summary, //throw FileError - const ErrorLog& log, + const zen::ErrorLog& log, AFS::OutputStream& streamOut); void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError - const ErrorLog& log, + const zen::ErrorLog& log, size_t maxBytesToWrite, - const IOCallback& notifyUnbufferedIO); + const std::function<void(const std::wstring& msg)>& notifyStatus); -Zstring getLastSyncsLogfilePath(); - - - -struct OnUpdateLogfileStatusNoThrow -{ - OnUpdateLogfileStatusNoThrow(ProcessCallback& pc, const std::wstring& logfileDisplayPath) : pc_(pc), - msg(replaceCpy(_("Saving file %x..."), L"%x", fmtPath(logfileDisplayPath))) {} - - void operator()(int64_t bytesDelta) - { - bytesWritten += bytesDelta; - try { pc_.reportStatus(msg + L" (" + formatFilesizeShort(bytesWritten) + L")"); /*throw X*/ } - catch (...) {} - } - -private: - ProcessCallback& pc_; - int64_t bytesWritten = 0; - const std::wstring msg; -}; +inline Zstring getDefaultLogFolderPath() { return getConfigDirPathPf() + Zstr("Logs") ; } //####################### implementation ####################### @@ -66,6 +46,7 @@ namespace { std::wstring generateLogHeader(const SummaryInfo& s) { + using namespace zen; assert(s.itemsProcessed <= s.itemsTotal); assert(s.bytesProcessed <= s.bytesTotal); @@ -74,8 +55,8 @@ std::wstring generateLogHeader(const SummaryInfo& s) //write header std::wstring headerLine = formatTime<std::wstring>(FORMAT_DATE); if (!s.jobName.empty()) - headerLine += L" - " + s.jobName; - headerLine += L": " + s.finalStatus; + headerLine += L" | " + s.jobName; + headerLine += L" | " + s.finalStatus; //assemble results box std::vector<std::wstring> results; @@ -118,9 +99,10 @@ std::wstring generateLogHeader(const SummaryInfo& s) inline void streamToLogFile(const SummaryInfo& summary, //throw FileError - const ErrorLog& log, + const zen::ErrorLog& log, AFS::OutputStream& streamOut) { + using namespace zen; const std::string header = replaceCpy(utfTo<std::string>(generateLogHeader(summary)), '\n', LINE_BREAK); //don't replace line break any earlier streamOut.write(&header[0], header.size()); //throw FileError, X @@ -141,16 +123,13 @@ void streamToLogFile(const SummaryInfo& summary, //throw FileError inline -Zstring getLastSyncsLogfilePath() { return getConfigDirPathPf() + Zstr("LastSyncs.log"); } - - -inline void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError - const ErrorLog& log, + const zen::ErrorLog& log, size_t maxBytesToWrite, //log may be *huge*, e.g. 1 million items; LastSyncs.log *must not* create performance problems! - const IOCallback& notifyUnbufferedIO) + const std::function<void(const std::wstring& msg)>& notifyStatus) { - const Zstring filepath = getLastSyncsLogfilePath(); + using namespace zen; + const Zstring filePath = getConfigDirPathPf() + Zstr("LastSyncs.log"); Utf8String newStream = utfTo<Utf8String>(generateLogHeader(summary)); replace(newStream, '\n', LINE_BREAK); //don't replace line break any earlier @@ -170,13 +149,31 @@ void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError } } + auto notifyUnbufferedIOLoad = [notifyStatus, + bytesRead_ = int64_t(0), + msg_ = replaceCpy(_("Loading file %x..."), L"%x", fmtPath(filePath))] + (int64_t bytesDelta) mutable + { + if (notifyStatus) + notifyStatus(msg_ + L" (" + formatFilesizeShort(bytesRead_ += bytesDelta) + L")"); /*throw X*/ + }; + + auto notifyUnbufferedIOSave = [notifyStatus, + bytesWritten_ = int64_t(0), + msg_ = replaceCpy(_("Saving file %x..."), L"%x", fmtPath(filePath))] + (int64_t bytesDelta) mutable + { + if (notifyStatus) + notifyStatus(msg_ + L" (" + formatFilesizeShort(bytesWritten_ += bytesDelta) + L")"); /*throw X*/ + }; + //fill up the rest of permitted space by appending old log if (newStream.size() < maxBytesToWrite) { Utf8String oldStream; try { - oldStream = loadBinContainer<Utf8String>(filepath, notifyUnbufferedIO); //throw FileError, X + oldStream = loadBinContainer<Utf8String>(filePath, notifyUnbufferedIOLoad); //throw FileError, X //Note: we also report the loaded bytes via onUpdateSaveStatus()! } catch (FileError&) {} @@ -185,7 +182,7 @@ void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError { newStream += LINE_BREAK; newStream += LINE_BREAK; - newStream += oldStream; //impliticly limited by "maxBytesToWrite"! + newStream += oldStream; //implicitly limited by "maxBytesToWrite"! //truncate size if required if (newStream.size() > maxBytesToWrite) @@ -204,7 +201,7 @@ void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError } } - saveBinContainer(filepath, newStream, notifyUnbufferedIO); //throw FileError, X + saveBinContainer(filePath, newStream, notifyUnbufferedIOSave); //throw FileError, X } } diff --git a/FreeFileSync/Source/lib/hard_filter.cpp b/FreeFileSync/Source/lib/hard_filter.cpp index 7fc3b7f6..0f8ebeea 100755 --- a/FreeFileSync/Source/lib/hard_filter.cpp +++ b/FreeFileSync/Source/lib/hard_filter.cpp @@ -12,9 +12,10 @@ #include <iterator> using namespace zen; +using namespace fff; -bool zen::operator<(const HardFilter& lhs, const HardFilter& rhs) +bool fff::operator<(const HardFilter& lhs, const HardFilter& rhs) { if (typeid(lhs) != typeid(rhs)) return typeid(lhs).before(typeid(rhs)); //in worst case, order is guaranteed to be stable only during each program run @@ -217,7 +218,7 @@ bool matchesMaskBegin(const Zstring& name, const std::vector<Zstring>& masks) } -std::vector<Zstring> zen::splitByDelimiter(const Zstring& filterString) +std::vector<Zstring> fff::splitByDelimiter(const Zstring& filterString) { //delimiters may be FILTER_ITEM_SEPARATOR or '\n' std::vector<Zstring> output; diff --git a/FreeFileSync/Source/lib/hard_filter.h b/FreeFileSync/Source/lib/hard_filter.h index 801f06ca..4f6acb56 100755 --- a/FreeFileSync/Source/lib/hard_filter.h +++ b/FreeFileSync/Source/lib/hard_filter.h @@ -11,7 +11,8 @@ #include <memory> #include <zen/zstring.h> -namespace zen + +namespace fff { //------------------------------------------------------------------ /* diff --git a/FreeFileSync/Source/lib/help_provider.h b/FreeFileSync/Source/lib/help_provider.h index 755adad2..1ac0a278 100755 --- a/FreeFileSync/Source/lib/help_provider.h +++ b/FreeFileSync/Source/lib/help_provider.h @@ -8,7 +8,7 @@ #define HELP_PROVIDER_H_85930427583421563126 #if 1 -namespace zen +namespace fff { inline void displayHelpEntry(const wxString& topic, wxWindow* parent) { wxLaunchDefaultBrowser(L"https://www.freefilesync.org/manual.php?topic=" + topic); } inline void uninitializeHelp() {} @@ -22,7 +22,7 @@ inline void uninitializeHelp() {} #include <wx/html/helpctrl.h> -namespace zen +namespace fff { void displayHelpEntry(const wxString& topic, wxWindow* parent); void uninitializeHelp(); //clean up gracefully during app shutdown: leaving this up to static destruction crashes on Win 8.1! diff --git a/FreeFileSync/Source/lib/icon_buffer.cpp b/FreeFileSync/Source/lib/icon_buffer.cpp index dc4f03c0..650bbb55 100755 --- a/FreeFileSync/Source/lib/icon_buffer.cpp +++ b/FreeFileSync/Source/lib/icon_buffer.cpp @@ -14,6 +14,7 @@ using namespace zen; +using namespace fff; using AFS = AbstractFileSystem; @@ -330,7 +331,7 @@ struct IconBuffer::Impl }; -IconBuffer::IconBuffer(IconSize sz) : pimpl_(std::make_unique<Impl>()), iconSizeType(sz) +IconBuffer::IconBuffer(IconSize sz) : pimpl_(std::make_unique<Impl>()), iconSizeType_(sz) { pimpl_->worker = InterruptibleThread(WorkerThread(pimpl_->workload, pimpl_->buffer, sz)); } @@ -400,7 +401,7 @@ wxBitmap IconBuffer::getIconByExtension(const Zstring& filePath) const Zstring& templateName(ext.empty() ? Zstr("file") : Zstr("file.") + ext); //don't pass actual file name to getIconByTemplatePath(), e.g. "AUTHORS" has own mime type on Linux!!! //=> we want to buffer by extension only to minimize buffer-misses! - it = pimpl_->extensionIcons.emplace(ext, extractWxBitmap(getIconByTemplatePath(templateName, IconBuffer::getSize(iconSizeType)))).first; + it = pimpl_->extensionIcons.emplace(ext, extractWxBitmap(getIconByTemplatePath(templateName, IconBuffer::getSize(iconSizeType_)))).first; } //need buffer size limit??? return it->second; @@ -409,13 +410,13 @@ wxBitmap IconBuffer::getIconByExtension(const Zstring& filePath) wxBitmap IconBuffer::genericFileIcon(IconSize sz) { - return extractWxBitmap(zen::genericFileIcon(IconBuffer::getSize(sz))); + return extractWxBitmap(fff::genericFileIcon(IconBuffer::getSize(sz))); } wxBitmap IconBuffer::genericDirIcon(IconSize sz) { - return extractWxBitmap(zen::genericDirIcon(IconBuffer::getSize(sz))); + return extractWxBitmap(fff::genericDirIcon(IconBuffer::getSize(sz))); } @@ -434,7 +435,7 @@ wxBitmap IconBuffer::linkOverlayIcon(IconSize sz) } -bool zen::hasLinkExtension(const Zstring& filepath) +bool fff::hasLinkExtension(const Zstring& filepath) { const Zstring& ext = getFileExtension(filepath); return ext == "desktop"; diff --git a/FreeFileSync/Source/lib/icon_buffer.h b/FreeFileSync/Source/lib/icon_buffer.h index 55427fcb..e5ff932b 100755 --- a/FreeFileSync/Source/lib/icon_buffer.h +++ b/FreeFileSync/Source/lib/icon_buffer.h @@ -15,7 +15,7 @@ #include "../fs/abstract.h" -namespace zen +namespace fff { class IconBuffer { @@ -31,10 +31,10 @@ public: ~IconBuffer(); static int getSize(IconSize sz); //expected and *maximum* icon size in pixel - int getSize() const { return getSize(iconSizeType); } // + int getSize() const { return getSize(iconSizeType_); } // bool readyForRetrieval(const AbstractPath& filePath); - Opt<wxBitmap> retrieveFileIcon (const AbstractPath& filePath); //... and mark as hot + zen::Opt<wxBitmap> retrieveFileIcon (const AbstractPath& filePath); //... and mark as hot void setWorkload (const std::vector<AbstractPath>& load); //(re-)set new workload of icons to be retrieved; wxBitmap getIconByExtension(const Zstring& filePath); //...and add to buffer @@ -47,7 +47,7 @@ private: struct Impl; const std::unique_ptr<Impl> pimpl_; - const IconSize iconSizeType; + const IconSize iconSizeType_; }; bool hasLinkExtension(const Zstring& filepath); diff --git a/FreeFileSync/Source/lib/icon_holder.h b/FreeFileSync/Source/lib/icon_holder.h index f8a44611..52bfe474 100755 --- a/FreeFileSync/Source/lib/icon_holder.h +++ b/FreeFileSync/Source/lib/icon_holder.h @@ -9,9 +9,9 @@ #include <memory> -//used by fs/abstract.h => check carefully before adding dependencies! -namespace zen +//used by fs/abstract.h => check carefully before adding dependencies! +namespace fff { struct ImageHolder //prepare conversion to wxImage as much as possible while staying thread-safe (in contrast to wxIcon/wxBitmap) { diff --git a/FreeFileSync/Source/lib/icon_loader.cpp b/FreeFileSync/Source/lib/icon_loader.cpp index 5415919b..71b783a2 100755 --- a/FreeFileSync/Source/lib/icon_loader.cpp +++ b/FreeFileSync/Source/lib/icon_loader.cpp @@ -12,6 +12,7 @@ using namespace zen; +using namespace fff; namespace @@ -84,7 +85,7 @@ ImageHolder imageHolderFromGicon(GIcon* gicon, int pixelSize) ZEN_ON_SCOPE_EXIT(::gtk_icon_info_free(iconInfo)); if (GdkPixbuf* pixBuf = ::gtk_icon_info_load_icon(iconInfo, nullptr)) { - ZEN_ON_SCOPE_EXIT(::g_object_unref(pixBuf)); //superseedes "::gdk_pixbuf_unref"! + ZEN_ON_SCOPE_EXIT(::g_object_unref(pixBuf)); //supersedes "::gdk_pixbuf_unref"! return copyToImageHolder(pixBuf); } } @@ -93,7 +94,7 @@ ImageHolder imageHolderFromGicon(GIcon* gicon, int pixelSize) } -ImageHolder zen::getIconByTemplatePath(const Zstring& templatePath, int pixelSize) +ImageHolder fff::getIconByTemplatePath(const Zstring& templatePath, int pixelSize) { //uses full file name, e.g. "AUTHORS" has own mime type on Linux: if (gchar* contentType = ::g_content_type_guess(templatePath.c_str(), //const gchar* filename, @@ -113,7 +114,7 @@ ImageHolder zen::getIconByTemplatePath(const Zstring& templatePath, int pixelSiz } -ImageHolder zen::genericFileIcon(int pixelSize) +ImageHolder fff::genericFileIcon(int pixelSize) { //we're called by getDisplayIcon()! -> avoid endless recursion! if (GIcon* fileIcon = ::g_content_type_get_icon("text/plain")) @@ -126,7 +127,7 @@ ImageHolder zen::genericFileIcon(int pixelSize) } -ImageHolder zen::genericDirIcon(int pixelSize) +ImageHolder fff::genericDirIcon(int pixelSize) { if (GIcon* dirIcon = ::g_content_type_get_icon("inode/directory")) //should contain fallback to GTK_STOCK_DIRECTORY ("gtk-directory") { @@ -138,7 +139,7 @@ ImageHolder zen::genericDirIcon(int pixelSize) } -ImageHolder zen::getFileIcon(const Zstring& filePath, int pixelSize) +ImageHolder fff::getFileIcon(const Zstring& filePath, int pixelSize) { //2. retrieve file icons GFile* file = ::g_file_new_for_path(filePath.c_str()); //documented to "never fail" @@ -156,7 +157,7 @@ ImageHolder zen::getFileIcon(const Zstring& filePath, int pixelSize) } -ImageHolder zen::getThumbnailImage(const Zstring& filePath, int pixelSize) //return null icon on failure +ImageHolder fff::getThumbnailImage(const Zstring& filePath, int pixelSize) //return null icon on failure { struct ::stat fileInfo = {}; if (::stat(filePath.c_str(), &fileInfo) == 0) diff --git a/FreeFileSync/Source/lib/icon_loader.h b/FreeFileSync/Source/lib/icon_loader.h index c93113c8..efbd530f 100755 --- a/FreeFileSync/Source/lib/icon_loader.h +++ b/FreeFileSync/Source/lib/icon_loader.h @@ -11,7 +11,7 @@ #include "icon_holder.h" -namespace zen +namespace fff { //=> all functions are safe to call from multiple threads! //!!!Note: init COM + system image list before loading icons!!! diff --git a/FreeFileSync/Source/lib/localization.cpp b/FreeFileSync/Source/lib/localization.cpp index a594e004..a60e34e2 100755 --- a/FreeFileSync/Source/lib/localization.cpp +++ b/FreeFileSync/Source/lib/localization.cpp @@ -24,6 +24,7 @@ using namespace zen; +using namespace fff; namespace @@ -31,7 +32,7 @@ namespace class FFSTranslation : public TranslationHandler { public: - FFSTranslation(const Zstring& lngFilePath, wxLanguage langId); //throw lngfile::ParsingError, parse_plural::ParsingError + FFSTranslation(const Zstring& lngFilePath, wxLanguage langId); //throw lng::ParsingError, plural::ParsingError wxLanguage getLangId() const { return langId_; } @@ -46,7 +47,7 @@ public: std::wstring translate(const std::wstring& singular, const std::wstring& plural, int64_t n) const override { - auto it = transMappingPl.find(std::make_pair(singular, plural)); + auto it = transMappingPl.find({ singular, plural }); if (it != transMappingPl.end()) { const size_t formNo = pluralParser->getForm(n); @@ -62,12 +63,12 @@ private: Translation transMapping; //map original text |-> translation TranslationPlural transMappingPl; - std::unique_ptr<parse_plural::PluralForm> pluralParser; //bound! + std::unique_ptr<plural::PluralForm> pluralParser; //bound! const wxLanguage langId_; }; -FFSTranslation::FFSTranslation(const Zstring& lngFilePath, wxLanguage langId) : langId_(langId) //throw lngfile::ParsingError, parse_plural::ParsingError +FFSTranslation::FFSTranslation(const Zstring& lngFilePath, wxLanguage langId) : langId_(langId) //throw lng::ParsingError, plural::ParsingError { std::string inputStream; try @@ -76,14 +77,14 @@ FFSTranslation::FFSTranslation(const Zstring& lngFilePath, wxLanguage langId) : } catch (const FileError& e) { - throw lngfile::ParsingError(e.toString(), 0, 0); + throw lng::ParsingError({ e.toString(), 0, 0 }); //passing FileError is too high a level for Parsing error, OTOH user is unlikely to see this since file I/O issues are sorted out by getExistingTranslations()! } - lngfile::TransHeader header; - lngfile::TranslationMap transInput; - lngfile::TranslationPluralMap transPluralInput; - lngfile::parseLng(inputStream, header, transInput, transPluralInput); //throw ParsingError + lng::TransHeader header; + lng::TranslationMap transInput; + lng::TranslationPluralMap transPluralInput; + lng::parseLng(inputStream, header, transInput, transPluralInput); //throw ParsingError for (const auto& item : transInput) { @@ -101,10 +102,10 @@ FFSTranslation::FFSTranslation(const Zstring& lngFilePath, wxLanguage langId) : for (const std::string& pf : item.second) plFormsWide.push_back(utfTo<std::wstring>(pf)); - transMappingPl.emplace(std::make_pair(engSingular, engPlural), plFormsWide); + transMappingPl.insert({ { engSingular, engPlural }, plFormsWide }); } - pluralParser = std::make_unique<parse_plural::PluralForm>(header.pluralDefinition); //throw parse_plural::ParsingError + pluralParser = std::make_unique<plural::PluralForm>(header.pluralDefinition); //throw plural::ParsingError } @@ -125,7 +126,7 @@ std::vector<TranslationInfo> loadTranslations() //search language files available std::vector<Zstring> lngFilePaths; - traverseFolder(zen::getResourceDirPf() + Zstr("Languages"), [&](const zen::FileInfo& fi) //FileInfo is ambiguous on OS X + traverseFolder(fff::getResourceDirPf() + Zstr("Languages"), [&](const FileInfo& fi) //FileInfo is ambiguous on OS X { if (endsWith(fi.fullPath, Zstr(".lng"))) lngFilePaths.push_back(fi.fullPath); @@ -137,8 +138,8 @@ std::vector<TranslationInfo> loadTranslations() { const std::string stream = loadBinContainer<std::string>(filePath, nullptr /*notifyUnbufferedIO*/); //throw FileError - lngfile::TransHeader lngHeader; - lngfile::parseHeader(stream, lngHeader); //throw ParsingError + lng::TransHeader lngHeader; + lng::parseHeader(stream, lngHeader); //throw ParsingError assert(!lngHeader.languageName .empty()); assert(!lngHeader.translatorName.empty()); @@ -165,7 +166,7 @@ std::vector<TranslationInfo> loadTranslations() else assert(false); } catch (FileError&) { assert(false); } - catch (lngfile::ParsingError&) { assert(false); } //better not show an error message here; scenario: batch jobs + catch (lng::ParsingError&) { assert(false); } //better not show an error message here; scenario: batch jobs } std::sort(locMapping.begin(), locMapping.end(), [](const TranslationInfo& lhs, const TranslationInfo& rhs) @@ -372,21 +373,21 @@ private: } -const std::vector<TranslationInfo>& zen::getExistingTranslations() +const std::vector<TranslationInfo>& fff::getExistingTranslations() { static const std::vector<TranslationInfo> translations = loadTranslations(); return translations; } -void zen::releaseWxLocale() +void fff::releaseWxLocale() { wxWidgetsLocale::getInstance().tearDown(); - zen::setTranslator(nullptr); //good place for clean up rather than some time during static destruction: is this an actual benefit??? + setTranslator(nullptr); //good place for clean up rather than some time during static destruction: is this an actual benefit??? } -void zen::setLanguage(wxLanguage lng) //throw FileError +void fff::setLanguage(wxLanguage lng) //throw FileError { if (getLanguage() == lng && wxWidgetsLocale::getInstance().getLanguage() == lng) return; //support polling @@ -403,21 +404,21 @@ void zen::setLanguage(wxLanguage lng) //throw FileError //load language file into buffer if (langFilePath.empty()) //if languageFile is empty, texts will be english by default - zen::setTranslator(nullptr); + setTranslator(nullptr); else try { - zen::setTranslator(std::make_unique<FFSTranslation>(langFilePath, lng)); //throw lngfile::ParsingError, parse_plural::ParsingError + setTranslator(std::make_unique<FFSTranslation>(langFilePath, lng)); //throw lng::ParsingError, plural::ParsingError } - catch (lngfile::ParsingError& e) + catch (lng::ParsingError& e) { throw FileError(replaceCpy(replaceCpy(replaceCpy(_("Error parsing file %x, row %y, column %z."), L"%x", fmtPath(langFilePath)), - L"%y", numberTo<std::wstring>(e.row_ + 1)), - L"%z", numberTo<std::wstring>(e.col_ + 1)) - + L"\n\n" + e.msg_); + L"%y", numberTo<std::wstring>(e.row + 1)), + L"%z", numberTo<std::wstring>(e.col + 1)) + + L"\n\n" + e.msg); } - catch (parse_plural::ParsingError&) + catch (plural::ParsingError&) { throw FileError(replaceCpy<std::wstring>(L"%x: Invalid plural form definition", L"%x", fmtPath(langFilePath))); //user should never see this! } @@ -427,15 +428,15 @@ void zen::setLanguage(wxLanguage lng) //throw FileError } -wxLanguage zen::getLanguage() +wxLanguage fff::getLanguage() { - std::shared_ptr<const TranslationHandler> t = zen::getTranslator(); + std::shared_ptr<const TranslationHandler> t = getTranslator(); const FFSTranslation* loc = dynamic_cast<const FFSTranslation*>(t.get()); return loc ? loc->getLangId() : wxLANGUAGE_ENGLISH_US; } -wxLanguage zen::getSystemLanguage() +wxLanguage fff::getSystemLanguage() { return mapLanguageDialect(static_cast<wxLanguage>(wxLocale::GetSystemLanguage())); } diff --git a/FreeFileSync/Source/lib/localization.h b/FreeFileSync/Source/lib/localization.h index ad603af2..bf2f6b60 100755 --- a/FreeFileSync/Source/lib/localization.h +++ b/FreeFileSync/Source/lib/localization.h @@ -11,7 +11,8 @@ #include <zen/file_error.h> #include <wx/language.h> -namespace zen + +namespace fff { struct TranslationInfo { diff --git a/FreeFileSync/Source/lib/lock_holder.h b/FreeFileSync/Source/lib/lock_holder.h index fc3f7a5c..128f4b8e 100755 --- a/FreeFileSync/Source/lib/lock_holder.h +++ b/FreeFileSync/Source/lib/lock_holder.h @@ -8,7 +8,7 @@ #include "status_handler.h" -namespace zen +namespace fff { //intermediate locks created by DirLock use this extension, too: const Zchar LOCK_FILE_ENDING[] = Zstr(".ffs_lock"); //don't use Zstring as global constant: avoid static initialization order problem in global namespace! @@ -18,27 +18,21 @@ const Zchar LOCK_FILE_ENDING[] = Zstr(".ffs_lock"); //don't use Zstring as globa class LockHolder { public: - LockHolder(const std::set<Zstring, LessFilePath>& dirpathsExisting, //resolved paths + LockHolder(const std::set<Zstring, LessFilePath>& dirPathsExisting, //resolved paths bool& warnDirectoryLockFailed, ProcessCallback& pcb) { - class WaitOnLockHandler : public DirLockCallback - { - public: - WaitOnLockHandler(ProcessCallback& pc) : pc_(pc) {} - void requestUiRefresh() override { pc_.requestUiRefresh(); } //allowed to throw exceptions - void reportStatus(const std::wstring& text) override { pc_.reportStatus(text); } - private: - ProcessCallback& pc_; - } lcb(pcb); + using namespace zen; std::map<Zstring, FileError, LessFilePath> failedLocks; - for (const Zstring& dirpath : dirpathsExisting) + for (const Zstring& dirpath : dirPathsExisting) try { //lock file creation is synchronous and may block noticeably for very slow devices (usb sticks, mapped cloud storages) - lockHolder_.emplace_back(appendSeparator(dirpath) + Zstr("sync") + LOCK_FILE_ENDING, &lcb); //throw FileError + lockHolder_.emplace_back(appendSeparator(dirpath) + Zstr("sync") + LOCK_FILE_ENDING, + [&](const std::wstring& msg) { pcb.reportStatus(msg); /*throw X*/ }, + UI_UPDATE_INTERVAL / 2); //throw FileError } catch (const FileError& e) { failedLocks.emplace(dirpath, e); } diff --git a/FreeFileSync/Source/lib/norm_filter.h b/FreeFileSync/Source/lib/norm_filter.h index b357bdaa..c2606ffc 100755 --- a/FreeFileSync/Source/lib/norm_filter.h +++ b/FreeFileSync/Source/lib/norm_filter.h @@ -11,7 +11,7 @@ #include "soft_filter.h" -namespace zen +namespace fff { struct NormalizedFilter //grade-a filter: global/local filter settings combined, units resolved, ready for use { @@ -45,8 +45,6 @@ bool isNullFilter(const FilterConfig& filterCfg) - - // ----------------------- implementation ----------------------- inline NormalizedFilter normalizeFilters(const FilterConfig& global, const FilterConfig& local) diff --git a/FreeFileSync/Source/lib/parallel_scan.cpp b/FreeFileSync/Source/lib/parallel_scan.cpp index adb13bee..3ff7c9b6 100755 --- a/FreeFileSync/Source/lib/parallel_scan.cpp +++ b/FreeFileSync/Source/lib/parallel_scan.cpp @@ -11,11 +11,11 @@ #include <zen/thread.h> #include <zen/scope_guard.h> #include <zen/fixed_list.h> -//#include <zen/tick_count.h> #include "db_file.h" #include "lock_holder.h" using namespace zen; +using namespace fff; namespace @@ -159,7 +159,7 @@ using BasicWString = Zbase<wchar_t>; //thread-safe string class for UI texts class AsyncCallback //actor pattern { public: - AsyncCallback(size_t reportingIntervalMs) : reportingIntervalMs_(reportingIntervalMs) {} + AsyncCallback(std::chrono::milliseconds cbInterval) : cbInterval_(cbInterval) {} //blocking call: context of worker thread FillBufferCallback::HandleError reportError(const std::wstring& msg, size_t retryNumber) //throw ThreadInterruption @@ -206,7 +206,8 @@ public: const auto now = std::chrono::steady_clock::now(); //0 on error - if (numeric::dist(now, lastReportTime) > std::chrono::milliseconds(reportingIntervalMs_)) //perform ui updates not more often than necessary + handle potential chrono wrap-around! + //perform ui updates not more often than necessary + handle potential chrono wrap-around! + if (numeric::dist(now, lastReportTime) > cbInterval_) { lastReportTime = now; //keep "lastReportTime" at worker thread level to avoid locking! return true; @@ -235,7 +236,7 @@ public: const long activeCount = activeWorker_; if (activeCount >= 2) - statusText += L" [" + replaceCpy(_P("1 thread", "%x threads", activeCount), L"%x", numberTo<std::wstring>(activeCount)) + L"]"; + statusText += L" [" + _P("1 thread", "%x threads", activeCount) + L"]"; statusText += L" "; statusText += filepath; @@ -262,7 +263,7 @@ private: std::mutex lockCurrentStatus_; //use a different lock for current file: continue traversing while some thread may process an error BasicWString currentFile_; - const int64_t reportingIntervalMs_; + const std::chrono::milliseconds cbInterval_; const BasicWString textScanning_ { copyStringTo<BasicWString>(_("Scanning:")) }; //this one is (currently) not shared and could be made a std::wstring, but we stay consistent and use thread-safe variables in this class only! @@ -519,10 +520,10 @@ private: } -void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in +void fff::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in std::map<DirectoryKey, DirectoryValue>& buf, //out FillBufferCallback& callback, - size_t updateIntervalMs) + std::chrono::milliseconds cbInterval) { buf.clear(); @@ -537,7 +538,7 @@ void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in wt.join(); //in this context it is possible a thread is *not* joinable anymore due to the thread::try_join_for() below! ); - auto acb = std::make_shared<AsyncCallback>(updateIntervalMs / 2 /*reportingIntervalMs*/); + auto acb = std::make_shared<AsyncCallback>(cbInterval); //init worker threads for (const DirectoryKey& key : keysToRead) @@ -548,9 +549,9 @@ void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in const int threadId = static_cast<int>(worker.size()); worker.emplace_back(WorkerThread(threadId, acb, - key.folderPath_, //AbstractPath is thread-safe like an int! :) - key.filter_, - key.handleSymlinks_, + key.folderPath, //AbstractPath is thread-safe like an int! :) + key.filter, + key.handleSymlinks, dirOutput)); } @@ -565,7 +566,7 @@ void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in //process errors acb->processErrors(callback); } - while (!wt.tryJoinFor(std::chrono::milliseconds(updateIntervalMs))); + while (!wt.tryJoinFor(cbInterval)); acb->incrementNotifyingThreadId(); //process info messages of one thread at a time only } diff --git a/FreeFileSync/Source/lib/parallel_scan.h b/FreeFileSync/Source/lib/parallel_scan.h index fbfbca06..d28bb9d9 100755 --- a/FreeFileSync/Source/lib/parallel_scan.h +++ b/FreeFileSync/Source/lib/parallel_scan.h @@ -9,40 +9,34 @@ #include <map> #include <set> +#include <chrono> #include "hard_filter.h" #include "../structures.h" #include "../file_hierarchy.h" -namespace zen +namespace fff { struct DirectoryKey { - DirectoryKey(const AbstractPath& folderPath, - const HardFilter::FilterRef& filter, - SymLinkHandling handleSymlinks) : - folderPath_(folderPath), - filter_(filter), - handleSymlinks_(handleSymlinks) {} - - const AbstractPath folderPath_; //always bound! - HardFilter::FilterRef filter_; //filter interface: always bound by design! - SymLinkHandling handleSymlinks_; + AbstractPath folderPath; + HardFilter::FilterRef filter; //always bound by design! + SymLinkHandling handleSymlinks = SymLinkHandling::EXCLUDE; }; inline bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs) { - if (lhs.handleSymlinks_ != rhs.handleSymlinks_) - return lhs.handleSymlinks_ < rhs.handleSymlinks_; + if (lhs.handleSymlinks != rhs.handleSymlinks) + return lhs.handleSymlinks < rhs.handleSymlinks; - if (AFS::LessAbstractPath()(lhs.folderPath_, rhs.folderPath_)) + if (AFS::LessAbstractPath()(lhs.folderPath, rhs.folderPath)) return true; - if (AFS::LessAbstractPath()(rhs.folderPath_, lhs.folderPath_)) + if (AFS::LessAbstractPath()(rhs.folderPath, lhs.folderPath)) return false; - return *lhs.filter_ < *rhs.filter_; + return *lhs.filter < *rhs.filter; } @@ -75,7 +69,7 @@ struct FillBufferCallback void fillBuffer(const std::set<DirectoryKey>& keysToRead, //in std::map<DirectoryKey, DirectoryValue>& buf, //out FillBufferCallback& callback, - size_t updateIntervalMs); //unit: [ms] + std::chrono::milliseconds cbInterval); } #endif //PARALLEL_SCAN_H_924588904275284572857 diff --git a/FreeFileSync/Source/lib/parse_lng.h b/FreeFileSync/Source/lib/parse_lng.h index f2009585..78aa4a88 100755 --- a/FreeFileSync/Source/lib/parse_lng.h +++ b/FreeFileSync/Source/lib/parse_lng.h @@ -22,7 +22,8 @@ #include <zen/string_tools.h> #include "parse_plural.h" -namespace lngfile + +namespace lng { //singular forms using TranslationMap = std::map <std::string, std::string>; //orig |-> translation @@ -45,12 +46,11 @@ struct TransHeader struct ParsingError { - ParsingError(const std::wstring& msg, size_t row, size_t col) : msg_(msg), row_(row), col_(col) {} - std::wstring msg_; //parser error message - size_t row_; //starting with 0 - size_t col_; // + std::wstring msg; + size_t row = 0; //starting with 0 + size_t col = 0; // }; -void parseLng(const std::string& fileStream, TransHeader& header, TranslationMap& out, TranslationPluralMap& pluralOut); //throw ParsingError +void parseLng (const std::string& fileStream, TransHeader& header, TranslationMap& out, TranslationPluralMap& pluralOut); //throw ParsingError void parseHeader(const std::string& fileStream, TransHeader& header); //throw ParsingError class TranslationUnorderedList; //unordered list of unique translation items @@ -89,46 +89,46 @@ public: void addItem(const std::string& orig) { - if (!transUnique.insert(orig).second) return; + if (!transUnique_.insert(orig).second) return; auto it = transOld_.find(orig); if (it != transOld_.end() && !it->second.empty()) //preserve old translation from .lng file if existing - sequence.push_back(std::make_shared<RegularItem>(std::make_pair(orig, it->second))); + sequence_.push_back(std::make_shared<RegularItem>(std::make_pair(orig, it->second))); else switch (newItemPos_) { case TranslationNewItemPos::REL: - sequence.push_back(std::make_shared<RegularItem>(std::make_pair(orig, std::string()))); + sequence_.push_back(std::make_shared<RegularItem>(std::make_pair(orig, std::string()))); break; case TranslationNewItemPos::TOP: - sequence.push_front(std::make_shared<RegularItem>(std::make_pair(orig, std::string()))); //put untranslated items to the front of the .lng filebreak; + sequence_.push_front(std::make_shared<RegularItem>(std::make_pair(orig, std::string()))); //put untranslated items to the front of the .lng filebreak; break; } } void addItem(const SingularPluralPair& orig) { - if (!pluralUnique.insert(orig).second) return; + if (!pluralUnique_.insert(orig).second) return; auto it = transPluralOld_.find(orig); if (it != transPluralOld_.end() && !it->second.empty()) //preserve old translation from .lng file if existing - sequence.push_back(std::make_shared<PluralItem>(std::make_pair(orig, it->second))); + sequence_.push_back(std::make_shared<PluralItem>(std::make_pair(orig, it->second))); else switch (newItemPos_) { case TranslationNewItemPos::REL: - sequence.push_back(std::make_shared<PluralItem>(std::make_pair(orig, PluralForms()))); + sequence_.push_back(std::make_shared<PluralItem>(std::make_pair(orig, PluralForms()))); break; case TranslationNewItemPos::TOP: - sequence.push_front(std::make_shared<PluralItem>(std::make_pair(orig, PluralForms()))); //put untranslated items to the front of the .lng file + sequence_.push_front(std::make_shared<PluralItem>(std::make_pair(orig, PluralForms()))); //put untranslated items to the front of the .lng file break; } } - bool untranslatedTextExists() const { return std::any_of(sequence.begin(), sequence.end(), [](const std::shared_ptr<Item>& item) { return !item->hasTranslation(); }); } + bool untranslatedTextExists() const { return std::any_of(sequence_.begin(), sequence_.end(), [](const std::shared_ptr<Item>& item) { return !item->hasTranslation(); }); } template <class Function, class Function2> void visitItems(Function onTrans, Function2 onPluralTrans) const //onTrans takes (const TranslationMap::value_type&), onPluralTrans takes (const TranslationPluralMap::value_type&) { - for (const auto& item : sequence) + for (const auto& item : sequence_) if (auto regular = dynamic_cast<const RegularItem*>(item.get())) onTrans(regular->value); else if (auto plural = dynamic_cast<const PluralItem*>(item.get())) @@ -142,10 +142,10 @@ private: struct PluralItem : public Item { PluralItem (const TranslationPluralMap::value_type& val) : value(val) {} bool hasTranslation() const override { return !value.second.empty(); } TranslationPluralMap::value_type value; }; const TranslationNewItemPos newItemPos_; - std::list<std::shared_ptr<Item>> sequence; //ordered list of translation elements + std::list<std::shared_ptr<Item>> sequence_; //ordered list of translation elements - std::set<TranslationMap ::key_type> transUnique; //check uniqueness - std::set<TranslationPluralMap::key_type> pluralUnique; // + std::set<TranslationMap ::key_type> transUnique_; //check uniqueness + std::set<TranslationPluralMap::key_type> pluralUnique_; // const TranslationMap transOld_; //reuse existing translation const TranslationPluralMap transPluralOld_; // @@ -241,37 +241,37 @@ private: class Scanner { public: - Scanner(const std::string& byteStream) : stream(byteStream), pos(stream.begin()) + Scanner(const std::string& byteStream) : stream_(byteStream), pos_(stream_.begin()) { - if (zen::startsWith(stream, zen::BYTE_ORDER_MARK_UTF8)) - pos += zen::strLength(zen::BYTE_ORDER_MARK_UTF8); + if (zen::startsWith(stream_, zen::BYTE_ORDER_MARK_UTF8)) + pos_ += zen::strLength(zen::BYTE_ORDER_MARK_UTF8); } Token nextToken() { //skip whitespace - pos = std::find_if(pos, stream.end(), [](char c) { return !zen::isWhiteSpace(c); }); + pos_ = std::find_if(pos_, stream_.end(), [](char c) { return !zen::isWhiteSpace(c); }); - if (pos == stream.end()) + if (pos_ == stream_.end()) return Token(Token::TK_END); - for (const auto& token : tokens.getList()) + for (const auto& token : tokens_.getList()) if (startsWith(token.second)) { - pos += token.second.size(); + pos_ += token.second.size(); return Token(token.first); } //rest must be "text" - auto itBegin = pos; - while (pos != stream.end() && !startsWithKnownTag()) - pos = std::find(pos + 1, stream.end(), '<'); + auto itBegin = pos_; + while (pos_ != stream_.end() && !startsWithKnownTag()) + pos_ = std::find(pos_ + 1, stream_.end(), '<'); - std::string text(itBegin, pos); + std::string text(itBegin, pos_); normalize(text); //remove whitespace from end ect. - if (text.empty() && pos == stream.end()) + if (text.empty() && pos_ == stream_.end()) return Token(Token::TK_END); Token out(Token::TK_TEXT); @@ -282,8 +282,8 @@ public: size_t posRow() const //current row beginning with 0 { //count line endings - const size_t crSum = std::count(stream.begin(), pos, '\r'); //carriage returns - const size_t nlSum = std::count(stream.begin(), pos, '\n'); //new lines + const size_t crSum = std::count(stream_.begin(), pos_, '\r'); //carriage returns + const size_t nlSum = std::count(stream_.begin(), pos_, '\n'); //new lines assert(crSum == 0 || nlSum == 0 || crSum == nlSum); return std::max(crSum, nlSum); //be compatible with Linux/Mac/Win } @@ -291,27 +291,27 @@ public: size_t posCol() const //current col beginning with 0 { //seek beginning of line - for (auto it = pos; it != stream.begin(); ) + for (auto it = pos_; it != stream_.begin(); ) { --it; if (*it == '\r' || *it == '\n') - return pos - it - 1; + return pos_ - it - 1; } - return pos - stream.begin(); + return pos_ - stream_.begin(); } private: bool startsWithKnownTag() const { - return std::any_of(tokens.getList().begin(), tokens.getList().end(), + return std::any_of(tokens_.getList().begin(), tokens_.getList().end(), [&](const KnownTokens::TokenMap::value_type& p) { return startsWith(p.second); }); } bool startsWith(const std::string& prefix) const { - if (stream.end() - pos < static_cast<ptrdiff_t>(prefix.size())) + if (stream_.end() - pos_ < static_cast<ptrdiff_t>(prefix.size())) return false; - return std::equal(prefix.begin(), prefix.end(), pos); + return std::equal(prefix.begin(), prefix.end(), pos_); } static void normalize(std::string& text) @@ -327,9 +327,9 @@ private: zen::replace(text, "\r", '\n'); //ensure c-style line breaks } - const std::string stream; - std::string::const_iterator pos; - const KnownTokens tokens; //no need for static non-POD! + const std::string stream_; + std::string::const_iterator pos_; + const KnownTokens tokens_; //no need for static non-POD! }; @@ -344,15 +344,15 @@ public: try { - parse_plural::PluralFormInfo pi(header.pluralDefinition, header.pluralCount); + plural::PluralFormInfo pi(header.pluralDefinition, header.pluralCount); //items while (token().type != Token::TK_END) parseRegular(out, pluralOut, pi); } - catch (const parse_plural::InvalidPluralForm&) + catch (const plural::InvalidPluralForm&) { - throw ParsingError(L"Invalid plural form definition", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Invalid plural form definition", scn_.posRow(), scn_.posCol() }); } } @@ -394,7 +394,7 @@ public: } private: - void parseRegular(TranslationMap& out, TranslationPluralMap& pluralOut, const parse_plural::PluralFormInfo& pluralInfo) + void parseRegular(TranslationMap& out, TranslationPluralMap& pluralOut, const plural::PluralFormInfo& pluralInfo) { consumeToken(Token::TK_SRC_BEGIN); @@ -418,7 +418,7 @@ private: out.emplace(original, translation); } - void parsePlural(TranslationPluralMap& pluralOut, const parse_plural::PluralFormInfo& pluralInfo) + void parsePlural(TranslationPluralMap& pluralOut, const plural::PluralFormInfo& pluralInfo) { //Token::TK_SRC_BEGIN already consumed @@ -457,12 +457,12 @@ private: using namespace zen; if (original.empty()) - throw ParsingError(L"Translation source text is empty", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Translation source text is empty", scn_.posRow(), scn_.posCol() }); if (!isValidUtf(original)) - throw ParsingError(L"Translation source text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Translation source text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol() }); if (!isValidUtf(translation)) - throw ParsingError(L"Translation text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Translation text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol() }); if (!translation.empty()) { @@ -471,7 +471,7 @@ private: { if (contains(original, placeholder) && !contains(translation, placeholder)) - throw ParsingError(replaceCpy<std::wstring>(L"Placeholder %x missing in translation", L"%x", utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol()); + throw ParsingError({ replaceCpy<std::wstring>(L"Placeholder %x missing in translation", L"%x", utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol() }); }; checkPlaceholder("%x"); checkPlaceholder("%y"); @@ -487,19 +487,19 @@ private: const size_t ampCountOrig = ampersandTokenCount(original); if (ampCountOrig != ampersandTokenCount(translation) || ampCountOrig > 1) - throw ParsingError(L"Source and translation both need exactly one & character to mark a menu item access key or none at all", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Source and translation both need exactly one & character to mark a menu item access key or none at all", scn_.posRow(), scn_.posCol() }); //ampersand at the end makes buggy wxWidgets crash miserably if (ampCountOrig > 0) if ((endsWith(original, "&") && !endsWith(original, "&&")) || (endsWith(translation, "&") && !endsWith(translation, "&&"))) - throw ParsingError(L"The & character to mark a menu item access key must not occur at the end of a string", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"The & character to mark a menu item access key must not occur at the end of a string", scn_.posRow(), scn_.posCol() }); //if source ends with colon, so must translation (note: character seems to be universally used, even for asian and arabic languages) if (endsWith(original, ":") && !endsWith(translation, ":") && !endsWith(translation, "\xef\xbc\x9a")) //chinese colon - throw ParsingError(L"Source text ends with a colon character \":\", but translation does not", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Source text ends with a colon character \":\", but translation does not", scn_.posRow(), scn_.posCol() }); auto endsWithSingleDot = [](const std::string& s) { return endsWith(s, ".") && !endsWith(s, ".."); }; @@ -508,43 +508,43 @@ private: !endsWithSingleDot(translation) && !endsWith(translation, "\xe0\xa5\xa4") && //hindi period !endsWith(translation, "\xe3\x80\x82")) //chinese period - throw ParsingError(L"Source text ends with a punctuation mark character \".\", but translation does not", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Source text ends with a punctuation mark character \".\", but translation does not", scn_.posRow(), scn_.posCol() }); //if source ends with an ellipsis, so must translation (note: character seems to be universally used, even for asian and arabic languages) if (endsWith(original, "...") && !endsWith(translation, "...") && !endsWith(translation, "\xe2\x80\xa6")) //narrow ellipsis (spanish?) - throw ParsingError(L"Source text ends with an ellipsis \"...\", but translation does not", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Source text ends with an ellipsis \"...\", but translation does not", scn_.posRow(), scn_.posCol() }); //if source is a one-liner, so should be the translation if (!contains(original, '\n') && contains(translation, '\n')) - throw ParsingError(L"Source text is a one-liner, but translation consists of multiple lines", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Source text is a one-liner, but translation consists of multiple lines", scn_.posRow(), scn_.posCol() }); //check for correct FFS brand names if (contains(original, "FreeFileSync") && !contains(translation, "FreeFileSync")) - throw ParsingError(L"Misspelled \"FreeFileSync\" in translation", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Misspelled \"FreeFileSync\" in translation", scn_.posRow(), scn_.posCol() }); if (contains(original, "RealTimeSync") && !contains(translation, "RealTimeSync")) - throw ParsingError(L"Misspelled \"RealTimeSync\" in translation", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Misspelled \"RealTimeSync\" in translation", scn_.posRow(), scn_.posCol() }); } } - void validateTranslation(const SingularPluralPair& original, const PluralForms& translation, const parse_plural::PluralFormInfo& pluralInfo) //throw ParsingError + void validateTranslation(const SingularPluralPair& original, const PluralForms& translation, const plural::PluralFormInfo& pluralInfo) //throw ParsingError { using namespace zen; //check the primary placeholder is existing at least for the second english text if (!contains(original.second, "%x")) - throw ParsingError(L"Plural form source text does not contain %x placeholder", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Plural form source text does not contain %x placeholder", scn_.posRow(), scn_.posCol() }); if (!isValidUtf(original.first) || !isValidUtf(original.second)) - throw ParsingError(L"Translation source text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Translation source text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol() }); if (std::any_of(translation.begin(), translation.end(), [](const std::string& pform) { return !isValidUtf(pform); })) - throw ParsingError(L"Translation text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Translation text contains UTF-8 encoding error", scn_.posRow(), scn_.posCol() }); if (!translation.empty()) { //check for invalid number of plural forms if (pluralInfo.getCount() != static_cast<int>(translation.size())) - throw ParsingError(replaceCpy(replaceCpy<std::wstring>(L"Invalid number of plural forms; actual: %x, expected: %y", L"%x", numberTo<std::wstring>(translation.size())), L"%y", numberTo<std::wstring>(pluralInfo.getCount())), scn_.posRow(), scn_.posCol()); + throw ParsingError({ replaceCpy(replaceCpy<std::wstring>(L"Invalid number of plural forms; actual: %x, expected: %y", L"%x", numberTo<std::wstring>(translation.size())), L"%y", numberTo<std::wstring>(pluralInfo.getCount())), scn_.posRow(), scn_.posCol() }); //check for duplicate plural form translations (catch copy & paste errors for single-number form translations) for (auto it = translation.begin(); it != translation.end(); ++it) @@ -552,7 +552,7 @@ private: { auto it2 = std::find(it + 1, translation.end(), *it); if (it2 != translation.end()) - throw ParsingError(replaceCpy<std::wstring>(L"Duplicate plural form translation at index position %x", L"%x", numberTo<std::wstring>(it2 - translation.begin())), scn_.posRow(), scn_.posCol()); + throw ParsingError({ replaceCpy<std::wstring>(L"Duplicate plural form translation at index position %x", L"%x", numberTo<std::wstring>(it2 - translation.begin())), scn_.posRow(), scn_.posCol() }); } for (int pos = 0; pos < static_cast<int>(translation.size()); ++pos) @@ -565,15 +565,15 @@ private: const int firstNumber = pluralInfo.getFirstNumber(pos); if (!(contains(translation[pos], "%x") || contains(translation[pos], numberTo<std::string>(firstNumber)))) - throw ParsingError(replaceCpy<std::wstring>(replaceCpy<std::wstring>(L"Plural form translation at index position %y needs to use the decimal number %z or the %x placeholder", - L"%y", numberTo<std::wstring>(pos)), L"%z", numberTo<std::wstring>(firstNumber)), scn_.posRow(), scn_.posCol()); + throw ParsingError({ replaceCpy<std::wstring>(replaceCpy<std::wstring>(L"Plural form translation at index position %y needs to use the decimal number %z or the %x placeholder", + L"%y", numberTo<std::wstring>(pos)), L"%z", numberTo<std::wstring>(firstNumber)), scn_.posRow(), scn_.posCol() }); } } else { //ensure the placeholder is used when needed if (!contains(translation[pos], "%x")) - throw ParsingError(replaceCpy<std::wstring>(L"Plural form at index position %y is missing the %x placeholder", L"%y", numberTo<std::wstring>(pos)), scn_.posRow(), scn_.posCol()); + throw ParsingError({ replaceCpy<std::wstring>(L"Plural form at index position %y is missing the %x placeholder", L"%y", numberTo<std::wstring>(pos)), scn_.posRow(), scn_.posCol() }); } auto checkSecondaryPlaceholder = [&](const std::string& placeholder) @@ -584,11 +584,11 @@ private: { if (!zen::contains(original.first, placeholder) || !zen::contains(original.second, placeholder)) - throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form source", L"%x", zen::utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol()); + throw ParsingError({ zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form source", L"%x", zen::utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol() }); //secondary placeholder is required for all plural forms if (std::any_of(translation.begin(), translation.end(), [&](const std::string& pform) { return !zen::contains(pform, placeholder); })) - throw ParsingError(zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form translation", L"%x", zen::utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol()); + throw ParsingError({ zen::replaceCpy<std::wstring>(L"Placeholder %x missing in plural form translation", L"%x", zen::utfTo<std::wstring>(placeholder)), scn_.posRow(), scn_.posCol() }); } }; checkSecondaryPlaceholder("%y"); @@ -597,7 +597,7 @@ private: //if source is a one-liner, so should be the translation if (!contains(original.first, '\n') && !contains(original.second, '\n') && std::any_of(translation.begin(), translation.end(), [](const std::string& pform) { return contains(pform, '\n'); })) - throw ParsingError(L"Source text is a one-liner, but at least one plural form translation consists of multiple lines", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Source text is a one-liner, but at least one plural form translation consists of multiple lines", scn_.posRow(), scn_.posCol() }); } } @@ -613,7 +613,7 @@ private: void expectToken(Token::Type t) //throw ParsingError { if (token().type != t) - throw ParsingError(L"Unexpected token", scn_.posRow(), scn_.posCol()); + throw ParsingError({ L"Unexpected token", scn_.posRow(), scn_.posCol() }); } Scanner scn_; diff --git a/FreeFileSync/Source/lib/parse_plural.h b/FreeFileSync/Source/lib/parse_plural.h index 3964d7b3..8a9173e3 100755 --- a/FreeFileSync/Source/lib/parse_plural.h +++ b/FreeFileSync/Source/lib/parse_plural.h @@ -12,7 +12,8 @@ #include <functional> #include <zen/string_base.h> -namespace parse_plural + +namespace plural { //expression interface struct Expression { virtual ~Expression() {} }; @@ -110,7 +111,7 @@ pm-expression: .po format,e.g.: (n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) */ -namespace implementation +namespace impl { template <class BinaryOp, class ParamType, class ResultType> struct BinaryExp : public Expr<ResultType> @@ -441,7 +442,7 @@ PluralFormInfo::PluralFormInfo(const std::string& definition, int pluralCount) / forms_.resize(pluralCount); try { - parse_plural::PluralForm pf(definition); //throw parse_plural::ParsingError + PluralForm pf(definition); //throw ParsingError //PERF_START //perf: 80ns per iteration max (for arabic) @@ -459,7 +460,7 @@ PluralFormInfo::PluralFormInfo(const std::string& definition, int pluralCount) / throw InvalidPluralForm(); } } - catch (const parse_plural::ParsingError&) + catch (const plural::ParsingError&) { throw InvalidPluralForm(); } @@ -471,7 +472,7 @@ PluralFormInfo::PluralFormInfo(const std::string& definition, int pluralCount) / inline -PluralForm::PluralForm(const std::string& stream) : expr_(implementation::Parser(stream, n_).parse()) {} //throw ParsingError +PluralForm::PluralForm(const std::string& stream) : expr_(impl::Parser(stream, n_).parse()) {} //throw ParsingError } #endif //PARSE_PLURAL_H_180465845670839576 diff --git a/FreeFileSync/Source/lib/perf_check.cpp b/FreeFileSync/Source/lib/perf_check.cpp index b101c9b2..fe4727c7 100755 --- a/FreeFileSync/Source/lib/perf_check.cpp +++ b/FreeFileSync/Source/lib/perf_check.cpp @@ -5,123 +5,93 @@ // ***************************************************************************** #include "perf_check.h" - -#include <limits> #include <zen/basic_math.h> #include <zen/i18n.h> #include <zen/format_unit.h> using namespace zen; +using namespace fff; -PerfCheck::PerfCheck(unsigned int windowSizeRemainingTime, - unsigned int windowSizeSpeed) : - windowSizeRemTime(windowSizeRemainingTime), - windowSizeSpeed_(windowSizeSpeed), - windowMax(std::max(windowSizeRemainingTime, windowSizeSpeed)) {} +PerfCheck::PerfCheck(std::chrono::milliseconds windowSizeRemTime, + std::chrono::milliseconds windowSizeSpeed) : + windowSizeRemTime_(windowSizeRemTime), + windowSizeSpeed_ (windowSizeSpeed), + windowMax_(std::max(windowSizeRemTime, windowSizeSpeed)) {} -PerfCheck::~PerfCheck() +void PerfCheck::addSample(std::chrono::nanoseconds timeElapsed, int itemsCurrent, double dataCurrent) { - /* - //write samples to a file - wxFFile outputFile(wxT("statistics.dat"), wxT("w")); - - outputFile.Write(wxT("Time(ms);Objects;Data\n")); + samples_.insert(samples_.end(), { timeElapsed, { itemsCurrent, dataCurrent }}); //use fact that time is monotonously ascending - for (auto it = samples.begin(); it != samples.end(); ++it) - { - outputFile.Write(numberTo<wxString>(it->first)); - outputFile.Write(wxT(";")); - outputFile.Write(numberTo<wxString>(it->second.objCount_)); - outputFile.Write(wxT(";")); - outputFile.Write(numberTo<wxString>(it->second.data_)); - outputFile.Write(wxT("\n")); - } - */ + //remove all records earlier than "now - windowMax" + auto it = samples_.upper_bound(timeElapsed - windowMax_); + if (it != samples_.begin()) + samples_.erase(samples_.begin(), --it); //keep one point before newBegin in order to handle "measurement holes" } -void PerfCheck::addSample(int itemsCurrent, double dataCurrent, int64_t timeMs) +std::tuple<double /*timeDelta*/, int /*itemsDelta*/, double /*bytesDelta*/> PerfCheck::getBlockDeltas(std::chrono::milliseconds windowSize) const { - samples.insert(samples.end(), std::make_pair(timeMs, Record(itemsCurrent, dataCurrent))); //use fact that time is monotonously ascending + if (samples_.empty()) return {}; - //remove all records earlier than "now - windowMax" - auto it = samples.upper_bound(timeMs - windowMax); - if (it != samples.begin()) - samples.erase(samples.begin(), --it); //keep one point before newBegin in order to handle "measurement holes" -} + auto itBack = samples_.rbegin(); + //find start of records "window" + auto itFront = samples_.upper_bound(itBack->first - windowSize); + if (itFront != samples_.begin()) + --itFront; //one point before window begin in order to handle "measurement holes" + const double timeDelta = std::chrono::duration<double>(itBack->first - itFront->first).count(); + const int itemsDelta = itBack->second.items - itFront->second.items; + const double bytesDelta = itBack->second.bytes - itFront->second.bytes; -inline -std::pair<const std::multimap<int64_t, PerfCheck::Record>::value_type*, const std::multimap<int64_t, PerfCheck::Record>::value_type*> PerfCheck::getBlockFromEnd(int64_t windowSize) const -{ - if (!samples.empty()) - { - auto itBack = samples.rbegin(); - //find start of records "window" - auto itFront = samples.upper_bound(itBack->first - windowSize); - if (itFront != samples.begin()) - --itFront; //one point before window begin in order to handle "measurement holes" - return std::make_pair(&*itFront, &*itBack); - } - return std::make_pair(nullptr, nullptr); + //return { timeDelta, itemsDelta, bytesDelta }; -> requires C++17 (Linux only issue) + return std::make_tuple(timeDelta, itemsDelta, bytesDelta); } -zen::Opt<double> PerfCheck::getRemainingTimeSec(double dataRemaining) const +Opt<double> PerfCheck::getRemainingTimeSec(double dataRemaining) const { - auto blk = getBlockFromEnd(windowSizeRemTime); - if (blk.first && blk.second) - { - const auto& itemFront = *blk.first; - const auto& itemBack = *blk.second; - //----------------------------------------------------------------------------------------------- - const int64_t timeDeltaMs = itemBack.first - itemFront.first; - const double bytesDelta = itemBack.second.bytes_ - itemFront.second.bytes_; + double timeDelta = 0; + int itemsDelta = 0; + double bytesDelta = 0; + std::tie(timeDelta, itemsDelta, bytesDelta) = getBlockDeltas(windowSizeRemTime_); + //const auto [timeDelta, itemsDelta, bytesDelta] = getBlockDeltas(windowSizeRemTime_); C++17 - //objects model logical operations *NOT* disk accesses, so we better play safe and use "bytes" only! - //http://sourceforge.net/p/freefilesync/feature-requests/197/ + //objects model logical operations *NOT* disk accesses, so we better play safe and use "bytes" only! + //http://sourceforge.net/p/freefilesync/feature-requests/197/ + + if (!numeric::isNull(bytesDelta)) //sign(dataRemaining) != sign(bytesDelta) usually an error, so show it! + return dataRemaining * timeDelta / bytesDelta; - if (!numeric::isNull(bytesDelta)) //sign(dataRemaining) != sign(bytesDelta) usually an error, so show it! - return dataRemaining * timeDeltaMs / 1000.0 / bytesDelta; - } return NoValue(); } -zen::Opt<std::wstring> PerfCheck::getBytesPerSecond() const +Opt<std::wstring> PerfCheck::getBytesPerSecond() const { - auto blk = getBlockFromEnd(windowSizeSpeed_); - if (blk.first && blk.second) - { - const auto& itemFront = *blk.first; - const auto& itemBack = *blk.second; - //----------------------------------------------------------------------------------------------- - const int64_t timeDeltaMs = itemBack.first - itemFront.first; - const double bytesDelta = itemBack.second.bytes_ - itemFront.second.bytes_; - - if (timeDeltaMs != 0) - return formatFilesizeShort(static_cast<int64_t>(bytesDelta * 1000.0 / timeDeltaMs)) + _("/sec"); - } + double timeDelta = 0; + int itemsDelta = 0; + double bytesDelta = 0; + std::tie(timeDelta, itemsDelta, bytesDelta) = getBlockDeltas(windowSizeSpeed_); + + if (!numeric::isNull(timeDelta)) + return formatFilesizeShort(numeric::round(bytesDelta / timeDelta)) + _("/sec"); + return NoValue(); } -zen::Opt<std::wstring> PerfCheck::getItemsPerSecond() const +Opt<std::wstring> PerfCheck::getItemsPerSecond() const { - auto blk = getBlockFromEnd(windowSizeSpeed_); - if (blk.first && blk.second) - { - const auto& itemFront = *blk.first; - const auto& itemBack = *blk.second; - //----------------------------------------------------------------------------------------------- - const int64_t timeDeltaMs = itemBack.first - itemFront.first; - const int itemsDelta = itemBack.second.items_ - itemFront.second.items_; - - if (timeDeltaMs != 0) - return replaceCpy(_("%x items/sec"), L"%x", formatTwoDigitPrecision(itemsDelta * 1000.0 / timeDeltaMs)); - } + double timeDelta = 0; + int itemsDelta = 0; + double bytesDelta = 0; + std::tie(timeDelta, itemsDelta, bytesDelta) = getBlockDeltas(windowSizeSpeed_); + + if (!numeric::isNull(timeDelta)) + return replaceCpy(_("%x items/sec"), L"%x", formatTwoDigitPrecision(itemsDelta / timeDelta)); + return NoValue(); } diff --git a/FreeFileSync/Source/lib/perf_check.h b/FreeFileSync/Source/lib/perf_check.h index 1f85ba63..401d08f5 100755 --- a/FreeFileSync/Source/lib/perf_check.h +++ b/FreeFileSync/Source/lib/perf_check.h @@ -7,41 +7,41 @@ #ifndef PERF_CHECK_H_87804217589312454 #define PERF_CHECK_H_87804217589312454 -#include <cstdint> #include <map> +#include <chrono> #include <string> #include <zen/optional.h> +namespace fff +{ class PerfCheck { public: - PerfCheck(unsigned int windowSizeRemainingTime, //unit: [ms] - unsigned int windowSizeSpeed); // - ~PerfCheck(); + PerfCheck(std::chrono::milliseconds windowSizeRemTime, + std::chrono::milliseconds windowSizeSpeed); - void addSample(int itemsCurrent, double dataCurrent, int64_t timeMs); //timeMs must be ascending! + void addSample(std::chrono::nanoseconds timeElapsed, int itemsCurrent, double dataCurrent); zen::Opt<double> getRemainingTimeSec(double dataRemaining) const; zen::Opt<std::wstring> getBytesPerSecond() const; //for window - zen::Opt<std::wstring> getItemsPerSecond() const; //for window + zen::Opt<std::wstring> getItemsPerSecond() const; // private: struct Record { - Record(int items, double bytes) : items_(items), bytes_(bytes) {} - const int items_; - const double bytes_; + int items = 0; + double bytes = 0; }; - std::pair<const std::multimap<int64_t, Record>::value_type*, - const std::multimap<int64_t, Record>::value_type*> getBlockFromEnd(int64_t windowSize) const; + std::tuple<double, int, double> getBlockDeltas(std::chrono::milliseconds windowSize) const; - const int64_t windowSizeRemTime; //unit: [ms] - const int64_t windowSizeSpeed_; // - const int64_t windowMax; + const std::chrono::milliseconds windowSizeRemTime_; + const std::chrono::milliseconds windowSizeSpeed_; + const std::chrono::milliseconds windowMax_; - std::map<int64_t, Record> samples; //time, unit: [ms] + std::map<std::chrono::nanoseconds, Record> samples_; }; +} #endif //PERF_CHECK_H_87804217589312454 diff --git a/FreeFileSync/Source/lib/process_xml.cpp b/FreeFileSync/Source/lib/process_xml.cpp index 8c9eb0b2..2b9b52c8 100755 --- a/FreeFileSync/Source/lib/process_xml.cpp +++ b/FreeFileSync/Source/lib/process_xml.cpp @@ -5,7 +5,6 @@ // ***************************************************************************** #include "process_xml.h" -#include <utility> #include <zenxml/xml.h> #include <zen/file_access.h> #include <zen/file_io.h> @@ -16,16 +15,14 @@ using namespace zen; -using namespace xmlAccess; //functionally needed for correct overload resolution!!! -using namespace std::rel_ops; +using namespace fff; //functionally needed for correct overload resolution!!! namespace { //------------------------------------------------------------------------------------------------------------------------------- -const int XML_FORMAT_VER_GLOBAL = 6; //2018-01-08 -const int XML_FORMAT_VER_FFS_GUI = 8; //2017-10-24 -const int XML_FORMAT_VER_FFS_BATCH = 8; // +const int XML_FORMAT_VER_GLOBAL = 8; //2018-02-01 +const int XML_FORMAT_VER_FFS_CFG = 9; //2018-02-01 //------------------------------------------------------------------------------------------------------------------------------- } @@ -48,10 +45,10 @@ XmlType getXmlTypeNoThrow(const XmlDoc& doc) //throw() } -XmlType xmlAccess::getXmlType(const Zstring& filepath) //throw FileError +XmlType fff::getXmlType(const Zstring& filePath) //throw FileError { //do NOT use zen::loadStream as it will needlessly load even huge files! - XmlDoc doc = loadXmlDocument(filepath); //throw FileError; quick exit if file is not an FFS XML + XmlDoc doc = loadXmlDocument(filePath); //throw FileError; quick exit if file is not an FFS XML return ::getXmlTypeNoThrow(doc); } @@ -82,13 +79,13 @@ XmlGlobalSettings::XmlGlobalSettings() //################################################################################################################ -Zstring xmlAccess::getGlobalConfigFile() +Zstring fff::getGlobalConfigFile() { - return zen::getConfigDirPathPf() + Zstr("GlobalSettings.xml"); + return getConfigDirPathPf() + Zstr("GlobalSettings.xml"); } -XmlGuiConfig xmlAccess::convertBatchToGui(const XmlBatchConfig& batchCfg) //noexcept +XmlGuiConfig fff::convertBatchToGui(const XmlBatchConfig& batchCfg) //noexcept { XmlGuiConfig output; output.mainCfg = batchCfg.mainCfg; @@ -96,7 +93,7 @@ XmlGuiConfig xmlAccess::convertBatchToGui(const XmlBatchConfig& batchCfg) //noex } -XmlBatchConfig xmlAccess::convertGuiToBatch(const XmlGuiConfig& guiCfg, const BatchExclusiveConfig& batchExCfg) //noexcept +XmlBatchConfig fff::convertGuiToBatch(const XmlGuiConfig& guiCfg, const BatchExclusiveConfig& batchExCfg) //noexcept { XmlBatchConfig output; output.mainCfg = guiCfg.mainCfg; @@ -284,11 +281,8 @@ void writeText(const PostSyncAction& value, std::string& output) { switch (value) { - case PostSyncAction::SUMMARY: - output = "Summary"; - break; - case PostSyncAction::EXIT: - output = "Exit"; + case PostSyncAction::NONE: + output = "None"; break; case PostSyncAction::SLEEP: output = "Sleep"; @@ -303,10 +297,8 @@ template <> inline bool readText(const std::string& input, PostSyncAction& value) { const std::string tmp = trimCpy(input); - if (tmp == "Summary") - value = PostSyncAction::SUMMARY; - else if (tmp == "Exit") - value = PostSyncAction::EXIT; + if (tmp == "None") + value = PostSyncAction::NONE; else if (tmp == "Sleep") value = PostSyncAction::SLEEP; else if (tmp == "Shutdown") @@ -770,10 +762,9 @@ bool readStruc(const XmlElement& input, ViewFilterDefault& value) success = false; }; - XmlIn sharedView = in["Shared"]; - readAttr(sharedView, "Equal", value.equal); - readAttr(sharedView, "Conflict", value.conflict); - readAttr(sharedView, "Excluded", value.excluded); + readAttr(in, "Equal", value.equal); + readAttr(in, "Conflict", value.conflict); + readAttr(in, "Excluded", value.excluded); XmlIn catView = in["CategoryView"]; readAttr(catView, "LeftOnly", value.leftOnly); @@ -799,10 +790,9 @@ void writeStruc(const ViewFilterDefault& value, XmlElement& output) { XmlOut out(output); - XmlOut sharedView = out["Shared"]; - sharedView.attribute("Equal", value.equal); - sharedView.attribute("Conflict", value.conflict); - sharedView.attribute("Excluded", value.excluded); + out.attribute("Equal", value.equal); + out.attribute("Conflict", value.conflict); + out.attribute("Excluded", value.excluded); XmlOut catView = out["CategoryView"]; catView.attribute("LeftOnly", value.leftOnly); @@ -820,6 +810,24 @@ void writeStruc(const ViewFilterDefault& value, XmlElement& output) actView.attribute("DeleteRight", value.deleteRight); actView.attribute("DoNothing", value.doNothing); } + + +template <> inline +bool readStruc(const XmlElement& input, ExternalApp& value) +{ + XmlIn in(input); + const bool rv1 = in(value.cmdLine); + const bool rv2 = in.attribute("Label", value.description); + return rv1 && rv2; +} + +template <> inline +void writeStruc(const ExternalApp& value, XmlElement& output) +{ + XmlOut out(output); + out(value.cmdLine); + out.attribute("Label", value.description); +} } @@ -870,45 +878,45 @@ void writeStruc(const ConfigFileItem& value, XmlElement& output) namespace { -void readConfig(const XmlIn& in, CompConfig& cmpConfig) +void readConfig(const XmlIn& in, CompConfig& cmpCfg) { - in["Variant" ](cmpConfig.compareVar); - in["Symlinks"](cmpConfig.handleSymlinks); + in["Variant" ](cmpCfg.compareVar); + in["Symlinks"](cmpCfg.handleSymlinks); //TODO: remove old parameter after migration! 2015-11-05 if (in["TimeShift"]) { std::wstring timeShiftPhrase; if (in["TimeShift"](timeShiftPhrase)) - cmpConfig.ignoreTimeShiftMinutes = fromTimeShiftPhrase(timeShiftPhrase); + cmpCfg.ignoreTimeShiftMinutes = fromTimeShiftPhrase(timeShiftPhrase); } else { std::wstring timeShiftPhrase; if (in["IgnoreTimeShift"](timeShiftPhrase)) - cmpConfig.ignoreTimeShiftMinutes = fromTimeShiftPhrase(timeShiftPhrase); + cmpCfg.ignoreTimeShiftMinutes = fromTimeShiftPhrase(timeShiftPhrase); } } -void readConfig(const XmlIn& in, DirectionConfig& directCfg) +void readConfig(const XmlIn& in, DirectionConfig& dirCfg) { - in["Variant"](directCfg.var); + in["Variant"](dirCfg.var); - if (directCfg.var == DirectionConfig::CUSTOM) + if (dirCfg.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); + inCustDir["LeftOnly" ](dirCfg.custom.exLeftSideOnly); + inCustDir["RightOnly" ](dirCfg.custom.exRightSideOnly); + inCustDir["LeftNewer" ](dirCfg.custom.leftNewer); + inCustDir["RightNewer"](dirCfg.custom.rightNewer); + inCustDir["Different" ](dirCfg.custom.different); + inCustDir["Conflict" ](dirCfg.custom.conflict); } else - directCfg.custom = DirectionSet(); + dirCfg.custom = DirectionSet(); - in["DetectMovedFiles"](directCfg.detectMovedFiles); + in["DetectMovedFiles"](dirCfg.detectMovedFiles); } @@ -1056,96 +1064,127 @@ void readConfig(const XmlIn& in, MainConfiguration& mainCfg, int formatVer) } -void readConfig(const XmlIn& in, XmlGuiConfig& config, int formatVer) +void readConfig(const XmlIn& in, XmlGuiConfig& cfg, int formatVer) { //read main config - readConfig(in, config.mainCfg, formatVer); + readConfig(in, cfg.mainCfg, formatVer); //read GUI specific config data XmlIn inGuiCfg = in["GuiConfig"]; std::string val; if (inGuiCfg["MiddleGridView"](val)) //refactor into enum!? - config.highlightSyncAction = val == "Action"; + cfg.highlightSyncAction = val == "Action"; //TODO: remove if clause after migration! 2017-10-24 if (formatVer < 8) { std::string str; if (inGuiCfg["HandleError"](str)) - config.mainCfg.ignoreErrors = str == "Ignore"; + cfg.mainCfg.ignoreErrors = str == "Ignore"; - str = trimCpy(utfTo<std::string>(config.mainCfg.postSyncCommand)); - if (str == "Close progress dialog") - config.mainCfg.postSyncCommand.clear(); + str = trimCpy(utfTo<std::string>(cfg.mainCfg.postSyncCommand)); + if (strEqual(str, "Close progress dialog", CmpAsciiNoCase())) + cfg.mainCfg.postSyncCommand.clear(); } } -void readConfig(const XmlIn& in, BatchExclusiveConfig& config, int formatVer) +void readConfig(const XmlIn& in, BatchExclusiveConfig& cfg, int formatVer) { XmlIn inBatchCfg = in["BatchConfig"]; + //TODO: remove if clause after migration! 2018-02-01 + if (formatVer < 9) + inBatchCfg["RunMinimized"](cfg.runMinimized); + else + inBatchCfg["ProgressDialog"].attribute("Minimized", cfg.runMinimized); + + //TODO: remove if clause after migration! 2018-02-01 + if (formatVer < 9) + ; //n/a + else + inBatchCfg["ProgressDialog"].attribute("AutoClose", cfg.autoCloseSummary); + //TODO: remove if clause after migration! 2017-10-24 if (formatVer < 8) { std::string str; if (inBatchCfg["HandleError"](str)) - config.batchErrorDialog = str == "Stop" ? BatchErrorDialog::CANCEL : BatchErrorDialog::SHOW; + cfg.batchErrorDialog = str == "Stop" ? BatchErrorDialog::CANCEL : BatchErrorDialog::SHOW; } else + inBatchCfg["ErrorDialog"](cfg.batchErrorDialog); + + //TODO: remove if clause after migration! 2017-10-24 + if (formatVer < 8) + ; //n/a + //TODO: remove if clause after migration! 2018-02-01 + else if (formatVer == 8) { - inBatchCfg["ErrorDialog"](config.batchErrorDialog); - inBatchCfg["PostSyncAction"](config.postSyncAction); + std::string tmp; + if (inBatchCfg["PostSyncAction"](tmp)) + { + tmp = trimCpy(tmp); + if (tmp == "Summary") + cfg.postSyncAction = PostSyncAction::NONE; + else if (tmp == "Exit") + cfg.autoCloseSummary = true; + else if (tmp == "Sleep") + cfg.postSyncAction = PostSyncAction::SLEEP; + else if (tmp == "Shutdown") + cfg.postSyncAction = PostSyncAction::SHUTDOWN; + } } + else + inBatchCfg["PostSyncAction"](cfg.postSyncAction); - inBatchCfg["RunMinimized" ](config.runMinimized); - inBatchCfg["LogfileFolder"](config.logFolderPathPhrase); - inBatchCfg["LogfileFolder"].attribute("Limit", config.logfilesCountLimit); + inBatchCfg["LogfileFolder"](cfg.logFolderPathPhrase); + inBatchCfg["LogfileFolder"].attribute("Limit", cfg.logfilesCountLimit); } -void readConfig(const XmlIn& in, XmlBatchConfig& config, int formatVer) +void readConfig(const XmlIn& in, XmlBatchConfig& cfg, int formatVer) { - readConfig(in, config.mainCfg, formatVer); - readConfig(in, config.batchExCfg, formatVer); + readConfig(in, cfg.mainCfg, formatVer); + readConfig(in, cfg.batchExCfg, formatVer); //TODO: remove if clause after migration! 2017-10-24 if (formatVer < 8) { std::string str; if (in["BatchConfig"]["HandleError"](str)) - config.mainCfg.ignoreErrors = str == "Ignore"; + cfg.mainCfg.ignoreErrors = str == "Ignore"; - str = trimCpy(utfTo<std::string>(config.mainCfg.postSyncCommand)); - if (str == "Close progress dialog") + str = trimCpy(utfTo<std::string>(cfg.mainCfg.postSyncCommand)); + if (strEqual(str, "Close progress dialog", CmpAsciiNoCase())) { - config.batchExCfg.postSyncAction = PostSyncAction::EXIT; - config.mainCfg.postSyncCommand.clear(); + cfg.batchExCfg.autoCloseSummary = true; + cfg.mainCfg.postSyncCommand.clear(); } else if (str == "rundll32.exe powrprof.dll,SetSuspendState Sleep" || str == "rundll32.exe powrprof.dll,SetSuspendState" || str == "systemctl suspend" || str == "osascript -e \'tell application \"System Events\" to sleep\'") { - config.batchExCfg.postSyncAction = PostSyncAction::SLEEP; - config.mainCfg.postSyncCommand.clear(); + cfg.batchExCfg.postSyncAction = PostSyncAction::SLEEP; + cfg.mainCfg.postSyncCommand.clear(); } else if (str == "shutdown /s /t 60" || str == "shutdown -s -t 60" || str == "systemctl poweroff" || str == "osascript -e \'tell application \"System Events\" to shut down\'") { - config.batchExCfg.postSyncAction = PostSyncAction::SHUTDOWN; - config.mainCfg.postSyncCommand.clear(); + cfg.batchExCfg.postSyncAction = PostSyncAction::SHUTDOWN; + cfg.mainCfg.postSyncCommand.clear(); } - else if (config.batchExCfg.runMinimized) - config.batchExCfg.postSyncAction = PostSyncAction::EXIT; + else if (cfg.batchExCfg.runMinimized) + cfg.batchExCfg.autoCloseSummary = true; } } -void readConfig(const XmlIn& in, XmlGlobalSettings& config, int formatVer) +void readConfig(const XmlIn& in, XmlGlobalSettings& cfg, int formatVer) { XmlIn inGeneral = in["General"]; @@ -1153,92 +1192,118 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config, int formatVer) if (in["Shared"]) inGeneral = in["Shared"]; - inGeneral["Language"].attribute("Name", config.programLanguage); - - inGeneral["FailSafeFileCopy" ].attribute("Enabled", config.failSafeFileCopy); - inGeneral["CopyLockedFiles" ].attribute("Enabled", config.copyLockedFiles); - inGeneral["CopyFilePermissions" ].attribute("Enabled", config.copyFilePermissions); - inGeneral["AutomaticRetry" ].attribute("Count", config.automaticRetryCount); - inGeneral["AutomaticRetry" ].attribute("Delay", config.automaticRetryDelay); - inGeneral["FileTimeTolerance" ].attribute("Seconds", config.fileTimeTolerance); - inGeneral["FolderAccessTimeout" ].attribute("Seconds", config.folderAccessTimeout); - inGeneral["RunWithBackgroundPriority"].attribute("Enabled", config.runWithBackgroundPriority); - inGeneral["LockDirectoriesDuringSync"].attribute("Enabled", config.createLockFile); - inGeneral["VerifyCopiedFiles" ].attribute("Enabled", config.verifyFileCopy); - inGeneral["LastSyncsLogSizeMax" ].attribute("Bytes", config.lastSyncsLogFileSizeMax); - inGeneral["NotificationSound" ].attribute("CompareFinished", config.soundFileCompareFinished); - inGeneral["NotificationSound" ].attribute("SyncFinished", config.soundFileSyncFinished); - - XmlIn inOpt = inGeneral["OptionalDialogs"]; - inOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warnUnresolvedConflicts); - inOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", config.optDialogs.warnNotEnoughDiskSpace); - inOpt["WarnSignificantDifference" ].attribute("Enabled", config.optDialogs.warnSignificantDifference); - inOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", config.optDialogs.warnRecyclerMissing); - inOpt["WarnInputFieldEmpty" ].attribute("Enabled", config.optDialogs.warnInputFieldEmpty); - inOpt["WarnModificationTimeError" ].attribute("Enabled", config.optDialogs.warnModificationTimeError); - //inOpt["WarnDatabaseError" ].attribute("Enabled", config.optDialogs.warnDatabaseError); - inOpt["WarnDependentFolderPair" ].attribute("Enabled", config.optDialogs.warnDependentFolderPair); - inOpt["WarnDependentBaseFolders" ].attribute("Enabled", config.optDialogs.warnDependentBaseFolders); - inOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warnDirectoryLockFailed); - inOpt["WarnVersioningFolderPartOfSync" ].attribute("Enabled", config.optDialogs.warnVersioningFolderPartOfSync); - inOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange); - inOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart); - inOpt["ConfirmExternalCommandMassInvoke"].attribute("Enabled", config.optDialogs.confirmExternalCommandMassInvoke); + inGeneral["Language"].attribute("Name", cfg.programLanguage); + + inGeneral["FailSafeFileCopy" ].attribute("Enabled", cfg.failSafeFileCopy); + inGeneral["CopyLockedFiles" ].attribute("Enabled", cfg.copyLockedFiles); + inGeneral["CopyFilePermissions" ].attribute("Enabled", cfg.copyFilePermissions); + inGeneral["AutomaticRetry" ].attribute("Count", cfg.automaticRetryCount); + inGeneral["AutomaticRetry" ].attribute("Delay", cfg.automaticRetryDelay); + inGeneral["FileTimeTolerance" ].attribute("Seconds", cfg.fileTimeTolerance); + inGeneral["FolderAccessTimeout" ].attribute("Seconds", cfg.folderAccessTimeout); + inGeneral["RunWithBackgroundPriority"].attribute("Enabled", cfg.runWithBackgroundPriority); + inGeneral["LockDirectoriesDuringSync"].attribute("Enabled", cfg.createLockFile); + inGeneral["VerifyCopiedFiles" ].attribute("Enabled", cfg.verifyFileCopy); + inGeneral["LastSyncsLogSizeMax" ].attribute("Bytes", cfg.lastSyncsLogFileSizeMax); + inGeneral["NotificationSound" ].attribute("CompareFinished", cfg.soundFileCompareFinished); + inGeneral["NotificationSound" ].attribute("SyncFinished", cfg.soundFileSyncFinished); + inGeneral["ProgressDialog" ].attribute("AutoClose", cfg.autoCloseProgressDialog); + + //TODO: remove old parameter after migration! 2018-02-04 + if (formatVer < 8) + { + XmlIn inOpt = inGeneral["OptionalDialogs"]; + inOpt["ConfirmStartSync" ].attribute("Enabled", cfg.confirmDlgs.confirmSyncStart); + inOpt["ConfirmSaveConfig" ].attribute("Enabled", cfg.confirmDlgs.popupOnConfigChange); + inOpt["ConfirmExternalCommandMassInvoke"].attribute("Enabled", cfg.confirmDlgs.confirmExternalCommandMassInvoke); + inOpt["WarnUnresolvedConflicts" ].attribute("Enabled", cfg.warnDlgs.warnUnresolvedConflicts); + inOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", cfg.warnDlgs.warnNotEnoughDiskSpace); + inOpt["WarnSignificantDifference" ].attribute("Enabled", cfg.warnDlgs.warnSignificantDifference); + inOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", cfg.warnDlgs.warnRecyclerMissing); + inOpt["WarnInputFieldEmpty" ].attribute("Enabled", cfg.warnDlgs.warnInputFieldEmpty); + inOpt["WarnModificationTimeError" ].attribute("Enabled", cfg.warnDlgs.warnModificationTimeError); + inOpt["WarnDependentFolderPair" ].attribute("Enabled", cfg.warnDlgs.warnDependentFolderPair); + inOpt["WarnDependentBaseFolders" ].attribute("Enabled", cfg.warnDlgs.warnDependentBaseFolders); + inOpt["WarnDirectoryLockFailed" ].attribute("Enabled", cfg.warnDlgs.warnDirectoryLockFailed); + inOpt["WarnVersioningFolderPartOfSync"].attribute("Enabled", cfg.warnDlgs.warnVersioningFolderPartOfSync); + } + else + { + XmlIn inOpt = inGeneral["OptionalDialogs"]; + inOpt["ConfirmStartSync" ].attribute("Show", cfg.confirmDlgs.confirmSyncStart); + inOpt["ConfirmSaveConfig" ].attribute("Show", cfg.confirmDlgs.popupOnConfigChange); + inOpt["ConfirmExternalCommandMassInvoke"].attribute("Show", cfg.confirmDlgs.confirmExternalCommandMassInvoke); + inOpt["WarnUnresolvedConflicts" ].attribute("Show", cfg.warnDlgs.warnUnresolvedConflicts); + inOpt["WarnNotEnoughDiskSpace" ].attribute("Show", cfg.warnDlgs.warnNotEnoughDiskSpace); + inOpt["WarnSignificantDifference" ].attribute("Show", cfg.warnDlgs.warnSignificantDifference); + inOpt["WarnRecycleBinNotAvailable" ].attribute("Show", cfg.warnDlgs.warnRecyclerMissing); + inOpt["WarnInputFieldEmpty" ].attribute("Show", cfg.warnDlgs.warnInputFieldEmpty); + inOpt["WarnModificationTimeError" ].attribute("Show", cfg.warnDlgs.warnModificationTimeError); + inOpt["WarnDependentFolderPair" ].attribute("Show", cfg.warnDlgs.warnDependentFolderPair); + inOpt["WarnDependentBaseFolders" ].attribute("Show", cfg.warnDlgs.warnDependentBaseFolders); + inOpt["WarnDirectoryLockFailed" ].attribute("Show", cfg.warnDlgs.warnDirectoryLockFailed); + inOpt["WarnVersioningFolderPartOfSync"].attribute("Show", cfg.warnDlgs.warnVersioningFolderPartOfSync); + } //gui specific global settings (optional) XmlIn inGui = in["Gui"]; XmlIn inWnd = inGui["MainDialog"]; //read application window size and position - inWnd.attribute("Width", config.gui.mainDlg.dlgSize.x); - inWnd.attribute("Height", config.gui.mainDlg.dlgSize.y); - inWnd.attribute("PosX", config.gui.mainDlg.dlgPos.x); - inWnd.attribute("PosY", config.gui.mainDlg.dlgPos.y); - inWnd.attribute("Maximized", config.gui.mainDlg.isMaximized); + inWnd.attribute("Width", cfg.gui.mainDlg.dlgSize.x); + inWnd.attribute("Height", cfg.gui.mainDlg.dlgSize.y); + inWnd.attribute("PosX", cfg.gui.mainDlg.dlgPos.x); + inWnd.attribute("PosY", cfg.gui.mainDlg.dlgPos.y); + inWnd.attribute("Maximized", cfg.gui.mainDlg.isMaximized); XmlIn inCopyTo = inWnd["ManualCopyTo"]; - inCopyTo.attribute("KeepRelativePaths", config.gui.mainDlg.copyToCfg.keepRelPaths); - inCopyTo.attribute("OverwriteIfExists", config.gui.mainDlg.copyToCfg.overwriteIfExists); + inCopyTo.attribute("KeepRelativePaths", cfg.gui.mainDlg.copyToCfg.keepRelPaths); + inCopyTo.attribute("OverwriteIfExists", cfg.gui.mainDlg.copyToCfg.overwriteIfExists); XmlIn inCopyToHistory = inCopyTo["FolderHistory"]; - inCopyToHistory(config.gui.mainDlg.copyToCfg.folderHistory); - inCopyToHistory.attribute("LastUsedPath", config.gui.mainDlg.copyToCfg.lastUsedPath); - inCopyToHistory.attribute("MaxSize", config.gui.mainDlg.copyToCfg.historySizeMax); + inCopyToHistory(cfg.gui.mainDlg.copyToCfg.folderHistory); + inCopyToHistory.attribute("LastUsedPath", cfg.gui.mainDlg.copyToCfg.lastUsedPath); + inCopyToHistory.attribute("MaxSize", cfg.gui.mainDlg.copyToCfg.historySizeMax); + + //TODO: remove old parameter after migration! 2018-02-04 + if (formatVer < 8) + inWnd["CaseSensitiveSearch"].attribute("Enabled", cfg.gui.mainDlg.textSearchRespectCase); + else + inWnd["Search"].attribute("CaseSensitive", cfg.gui.mainDlg.textSearchRespectCase); - inWnd["CaseSensitiveSearch"].attribute("Enabled", config.gui.mainDlg.textSearchRespectCase); - inWnd["FolderPairsVisible" ].attribute("Max", config.gui.mainDlg.maxFolderPairsVisible); + inWnd["FolderPairsVisible" ].attribute("Max", cfg.gui.mainDlg.maxFolderPairsVisible); //########################################################### XmlIn inConfig = inWnd["ConfigPanel"]; - inConfig.attribute("ScrollPos", config.gui.mainDlg.cfgGridTopRowPos); - inConfig.attribute("SyncOverdue", config.gui.mainDlg.cfgGridSyncOverdueDays); - inConfig.attribute("SortByColumn", config.gui.mainDlg.cfgGridLastSortColumn); - inConfig.attribute("SortAscending", config.gui.mainDlg.cfgGridLastSortAscending); + inConfig.attribute("ScrollPos", cfg.gui.mainDlg.cfgGridTopRowPos); + inConfig.attribute("SyncOverdue", cfg.gui.mainDlg.cfgGridSyncOverdueDays); + inConfig.attribute("SortByColumn", cfg.gui.mainDlg.cfgGridLastSortColumn); + inConfig.attribute("SortAscending", cfg.gui.mainDlg.cfgGridLastSortAscending); - inConfig["Columns"](config.gui.mainDlg.cfgGridColumnAttribs); + inConfig["Columns"](cfg.gui.mainDlg.cfgGridColumnAttribs); //TODO: remove parameter migration after some time! 2018-01-08 if (formatVer < 6) { - inGui["ConfigHistory"].attribute("MaxSize", config.gui.mainDlg.cfgHistItemsMax); + inGui["ConfigHistory"].attribute("MaxSize", cfg.gui.mainDlg.cfgHistItemsMax); - std::vector<Zstring> cfgHist; + std::vector<Zstring> cfgHist; inGui["ConfigHistory"](cfgHist); - for (const Zstring& cfgPath : cfgHist) - config.gui.mainDlg.cfgFileHistory.emplace_back(cfgPath, 0); + for (const Zstring& cfgPath : cfgHist) + cfg.gui.mainDlg.cfgFileHistory.emplace_back(cfgPath, 0); } else { - inConfig["Configurations"].attribute("MaxSize", config.gui.mainDlg.cfgHistItemsMax); - inConfig["Configurations"](config.gui.mainDlg.cfgFileHistory); + inConfig["Configurations"].attribute("MaxSize", cfg.gui.mainDlg.cfgHistItemsMax); + inConfig["Configurations"](cfg.gui.mainDlg.cfgFileHistory); } //TODO: remove parameter migration after some time! 2018-01-08 if (formatVer < 6) { - inGui["LastUsedConfig"](config.gui.mainDlg.lastUsedConfigFiles); + inGui["LastUsedConfig"](cfg.gui.mainDlg.lastUsedConfigFiles); } else { @@ -1248,116 +1313,144 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config, int formatVer) for (Zstring& filePath : cfgPaths) filePath = resolveFreeFileSyncDriveMacro(filePath); - config.gui.mainDlg.lastUsedConfigFiles = cfgPaths; + cfg.gui.mainDlg.lastUsedConfigFiles = cfgPaths; } } //########################################################### XmlIn inOverview = inWnd["OverviewPanel"]; - inOverview.attribute("ShowPercentage", config.gui.mainDlg.treeGridShowPercentBar); - inOverview.attribute("SortByColumn", config.gui.mainDlg.treeGridLastSortColumn); - inOverview.attribute("SortAscending", config.gui.mainDlg.treeGridLastSortAscending); + inOverview.attribute("ShowPercentage", cfg.gui.mainDlg.treeGridShowPercentBar); + inOverview.attribute("SortByColumn", cfg.gui.mainDlg.treeGridLastSortColumn); + inOverview.attribute("SortAscending", cfg.gui.mainDlg.treeGridLastSortAscending); //read column attributes XmlIn inColTree = inOverview["Columns"]; - inColTree(config.gui.mainDlg.treeGridColumnAttribs); + inColTree(cfg.gui.mainDlg.treeGridColumnAttribs); XmlIn inFileGrid = inWnd["FilePanel"]; //TODO: remove parameter migration after some time! 2018-01-08 if (formatVer < 6) inFileGrid = inWnd["CenterPanel"]; - inFileGrid.attribute("ShowIcons", config.gui.mainDlg.showIcons); - inFileGrid.attribute("IconSize", config.gui.mainDlg.iconSize); - inFileGrid.attribute("SashOffset", config.gui.mainDlg.sashOffset); - inFileGrid.attribute("HistoryMaxSize", config.gui.mainDlg.folderHistItemsMax); + inFileGrid.attribute("ShowIcons", cfg.gui.mainDlg.showIcons); + inFileGrid.attribute("IconSize", cfg.gui.mainDlg.iconSize); + inFileGrid.attribute("SashOffset", cfg.gui.mainDlg.sashOffset); + inFileGrid.attribute("HistoryMaxSize", cfg.gui.mainDlg.folderHistItemsMax); - inFileGrid["ColumnsLeft"].attribute("PathFormat", config.gui.mainDlg.itemPathFormatLeftGrid); - inFileGrid["ColumnsLeft"](config.gui.mainDlg.columnAttribLeft); + inFileGrid["ColumnsLeft"].attribute("PathFormat", cfg.gui.mainDlg.itemPathFormatLeftGrid); + inFileGrid["ColumnsLeft"](cfg.gui.mainDlg.columnAttribLeft); - inFileGrid["FolderHistoryLeft" ](config.gui.mainDlg.folderHistoryLeft); + inFileGrid["FolderHistoryLeft" ](cfg.gui.mainDlg.folderHistoryLeft); - inFileGrid["ColumnsRight"].attribute("PathFormat", config.gui.mainDlg.itemPathFormatRightGrid); - inFileGrid["ColumnsRight"](config.gui.mainDlg.columnAttribRight); + inFileGrid["ColumnsRight"].attribute("PathFormat", cfg.gui.mainDlg.itemPathFormatRightGrid); + inFileGrid["ColumnsRight"](cfg.gui.mainDlg.columnAttribRight); - inFileGrid["FolderHistoryRight"](config.gui.mainDlg.folderHistoryRight); + inFileGrid["FolderHistoryRight"](cfg.gui.mainDlg.folderHistoryRight); //TODO: remove parameter migration after some time! 2018-01-08 if (formatVer < 6) { - inGui["FolderHistoryLeft" ](config.gui.mainDlg.folderHistoryLeft); - inGui["FolderHistoryRight"](config.gui.mainDlg.folderHistoryRight); - inGui["FolderHistoryLeft"].attribute("MaxSize", config.gui.mainDlg.folderHistItemsMax); + inGui["FolderHistoryLeft" ](cfg.gui.mainDlg.folderHistoryLeft); + inGui["FolderHistoryRight"](cfg.gui.mainDlg.folderHistoryRight); + inGui["FolderHistoryLeft"].attribute("MaxSize", cfg.gui.mainDlg.folderHistItemsMax); } //########################################################### - inWnd["DefaultViewFilter"](config.gui.mainDlg.viewFilterDefault); - inWnd["Perspective5"](config.gui.mainDlg.guiPerspectiveLast); + inWnd["DefaultViewFilter"](cfg.gui.mainDlg.viewFilterDefault); - std::vector<Zstring> tmp = splitFilterByLines(config.gui.defaultExclusionFilter); //default value + //TODO: remove old parameter after migration! 2018-02-04 + if (formatVer < 8) + { + XmlIn sharedView = inWnd["DefaultViewFilter"]["Shared"]; + sharedView.attribute("Equal", cfg.gui.mainDlg.viewFilterDefault.equal); + sharedView.attribute("Conflict", cfg.gui.mainDlg.viewFilterDefault.conflict); + sharedView.attribute("Excluded", cfg.gui.mainDlg.viewFilterDefault.excluded); + } + + //TODO: remove old parameter after migration! 2018-01-16 + if (formatVer < 7) + inWnd["Perspective5"](cfg.gui.mainDlg.guiPerspectiveLast); + else + inWnd["Perspective"](cfg.gui.mainDlg.guiPerspectiveLast); + + std::vector<Zstring> tmp = splitFilterByLines(cfg.gui.defaultExclusionFilter); //default value inGui["DefaultExclusionFilter"](tmp); - config.gui.defaultExclusionFilter = mergeFilterLines(tmp); + cfg.gui.defaultExclusionFilter = mergeFilterLines(tmp); //TODO: remove parameter migration after some time! 2016-09-23 if (formatVer < 4) - config.gui.mainDlg.cfgHistItemsMax = std::max<size_t>(config.gui.mainDlg.cfgHistItemsMax, 100); + cfg.gui.mainDlg.cfgHistItemsMax = std::max<size_t>(cfg.gui.mainDlg.cfgHistItemsMax, 100); //TODO: remove if clause after migration! 2017-10-24 if (formatVer < 5) { - inGui["OnCompletionHistory"](config.gui.commandHistory); - inGui["OnCompletionHistory"].attribute("MaxSize", config.gui.commandHistItemsMax); + inGui["OnCompletionHistory"](cfg.gui.commandHistory); + inGui["OnCompletionHistory"].attribute("MaxSize", cfg.gui.commandHistItemsMax); } else { - inGui["CommandHistory"](config.gui.commandHistory); - inGui["CommandHistory"].attribute("MaxSize", config.gui.commandHistItemsMax); + inGui["CommandHistory"](cfg.gui.commandHistory); + inGui["CommandHistory"].attribute("MaxSize", cfg.gui.commandHistItemsMax); } //external applications //TODO: remove old parameter after migration! 2016-05-28 if (inGui["ExternalApplications"]) { - inGui["ExternalApplications"](config.gui.externelApplications); - if (config.gui.externelApplications.empty()) //who knows, let's repair some old failed data migrations - config.gui.externelApplications = XmlGlobalSettings().gui.externelApplications; + inGui["ExternalApplications"](cfg.gui.externalApps); + if (cfg.gui.externalApps.empty()) //who knows, let's repair some old failed data migrations + cfg.gui.externalApps = XmlGlobalSettings().gui.externalApps; else { } } else - inGui["ExternalApps"](config.gui.externelApplications); + { + //TODO: remove old parameter after migration! 2018-01-16 + if (formatVer < 7) + { + std::vector<std::pair<std::wstring, Zstring>> extApps; + if (inGui["ExternalApps"](extApps)) + { + cfg.gui.externalApps.clear(); + for (const auto& item : extApps) + cfg.gui.externalApps.push_back({ item.first, item.second }); + } + } + else + inGui["ExternalApps"](cfg.gui.externalApps); + } //TODO: remove macro migration after some time! 2016-06-30 if (formatVer < 3) - for (auto& item : config.gui.externelApplications) + for (auto& item : cfg.gui.externalApps) { - replace(item.second, Zstr("%item2_path%"), Zstr("%item_path2%")); - replace(item.second, Zstr("%item_folder%"), Zstr("%folder_path%")); - replace(item.second, Zstr("%item2_folder%"), Zstr("%folder_path2%")); - - replace(item.second, Zstr("explorer /select, \"%item_path%\""), Zstr("explorer /select, \"%local_path%\"")); - replace(item.second, Zstr("\"%item_path%\""), Zstr("\"%local_path%\"")); - replace(item.second, Zstr("xdg-open \"%item_path%\""), Zstr("xdg-open \"%local_path%\"")); - replace(item.second, Zstr("open -R \"%item_path%\""), Zstr("open -R \"%local_path%\"")); - replace(item.second, Zstr("open \"%item_path%\""), Zstr("open \"%local_path%\"")); - - if (contains(makeUpperCopy(item.second), Zstr("WINMERGEU.EXE")) || - contains(makeUpperCopy(item.second), Zstr("PSPAD.EXE"))) + replace(item.cmdLine, Zstr("%item2_path%"), Zstr("%item_path2%")); + replace(item.cmdLine, Zstr("%item_folder%"), Zstr("%folder_path%")); + replace(item.cmdLine, Zstr("%item2_folder%"), Zstr("%folder_path2%")); + + replace(item.cmdLine, Zstr("explorer /select, \"%item_path%\""), Zstr("explorer /select, \"%local_path%\"")); + replace(item.cmdLine, Zstr("\"%item_path%\""), Zstr("\"%local_path%\"")); + replace(item.cmdLine, Zstr("xdg-open \"%item_path%\""), Zstr("xdg-open \"%local_path%\"")); + replace(item.cmdLine, Zstr("open -R \"%item_path%\""), Zstr("open -R \"%local_path%\"")); + replace(item.cmdLine, Zstr("open \"%item_path%\""), Zstr("open \"%local_path%\"")); + + if (contains(makeUpperCopy(item.cmdLine), Zstr("WINMERGEU.EXE")) || + contains(makeUpperCopy(item.cmdLine), Zstr("PSPAD.EXE"))) { - replace(item.second, Zstr("%item_path%"), Zstr("%local_path%")); - replace(item.second, Zstr("%item_path2%"), Zstr("%local_path2%")); + replace(item.cmdLine, Zstr("%item_path%"), Zstr("%local_path%")); + replace(item.cmdLine, Zstr("%item_path2%"), Zstr("%local_path2%")); } } //TODO: remove macro migration after some time! 2016-07-18 - for (auto& item : config.gui.externelApplications) - replace(item.second, Zstr("%item_folder%"), Zstr("%folder_path%")); + for (auto& item : cfg.gui.externalApps) + replace(item.cmdLine, Zstr("%item_folder%"), Zstr("%folder_path%")); //last update check - inGui["LastOnlineCheck" ](config.gui.lastUpdateCheck); - inGui["LastOnlineVersion"](config.gui.lastOnlineVersion); + inGui["LastOnlineCheck" ](cfg.gui.lastUpdateCheck); + inGui["LastOnlineVersion"](cfg.gui.lastOnlineVersion); //batch specific global settings //XmlIn inBatch = in["Batch"]; @@ -1373,12 +1466,12 @@ int getConfigFormatVersion(const XmlDoc& doc) template <class ConfigType> -void readConfig(const Zstring& filepath, XmlType type, ConfigType& cfg, int currentXmlFormatVer, std::wstring& warningMsg) //throw FileError +void readConfig(const Zstring& filePath, XmlType type, ConfigType& cfg, int currentXmlFormatVer, std::wstring& warningMsg) //throw FileError { - XmlDoc doc = loadXmlDocument(filepath); //throw FileError + XmlDoc doc = loadXmlDocument(filePath); //throw FileError if (getXmlTypeNoThrow(doc) != type) //noexcept - throw FileError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filepath))); + throw FileError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filePath))); const int formatVer = getConfigFormatVersion(doc); @@ -1387,11 +1480,11 @@ void readConfig(const Zstring& filepath, XmlType type, ConfigType& cfg, int curr try { - checkForMappingErrors(in, filepath); //throw FileError + checkForMappingErrors(in, filePath); //throw FileError //(try to) migrate old configuration automatically if (formatVer< currentXmlFormatVer) - try { xmlAccess::writeConfig(cfg, filepath); /*throw FileError*/ } + try { fff::writeConfig(cfg, filePath); /*throw FileError*/ } catch (FileError&) { assert(false); } //don't bother user! } catch (const FileError& e) @@ -1402,28 +1495,28 @@ void readConfig(const Zstring& filepath, XmlType type, ConfigType& cfg, int curr } -void xmlAccess::readConfig(const Zstring& filepath, XmlGuiConfig& cfg, std::wstring& warningMsg) +void fff::readConfig(const Zstring& filePath, XmlGuiConfig& cfg, std::wstring& warningMsg) { - ::readConfig(filepath, XML_TYPE_GUI, cfg, XML_FORMAT_VER_FFS_GUI, warningMsg); //throw FileError + ::readConfig(filePath, XML_TYPE_GUI, cfg, XML_FORMAT_VER_FFS_CFG, warningMsg); //throw FileError } -void xmlAccess::readConfig(const Zstring& filepath, XmlBatchConfig& cfg, std::wstring& warningMsg) +void fff::readConfig(const Zstring& filePath, XmlBatchConfig& cfg, std::wstring& warningMsg) { - ::readConfig(filepath, XML_TYPE_BATCH, cfg, XML_FORMAT_VER_FFS_BATCH, warningMsg); //throw FileError + ::readConfig(filePath, XML_TYPE_BATCH, cfg, XML_FORMAT_VER_FFS_CFG, warningMsg); //throw FileError } -void xmlAccess::readConfig(const Zstring& filepath, XmlGlobalSettings& cfg, std::wstring& warningMsg) +void fff::readConfig(const Zstring& filePath, XmlGlobalSettings& cfg, std::wstring& warningMsg) { - ::readConfig(filepath, XML_TYPE_GLOBAL, cfg, XML_FORMAT_VER_GLOBAL, warningMsg); //throw FileError + ::readConfig(filePath, XML_TYPE_GLOBAL, cfg, XML_FORMAT_VER_GLOBAL, warningMsg); //throw FileError } namespace { template <class XmlCfg> -XmlCfg parseConfig(const XmlDoc& doc, const Zstring& filepath, int currentXmlFormatVer, std::wstring& warningMsg) //nothrow +XmlCfg parseConfig(const XmlDoc& doc, const Zstring& filePath, int currentXmlFormatVer, std::wstring& warningMsg) //nothrow { const int formatVer = getConfigFormatVersion(doc); @@ -1433,11 +1526,11 @@ XmlCfg parseConfig(const XmlDoc& doc, const Zstring& filepath, int currentXmlFor try { - checkForMappingErrors(in, filepath); //throw FileError + checkForMappingErrors(in, filePath); //throw FileError //(try to) migrate old configuration if needed if (formatVer < currentXmlFormatVer) - try { xmlAccess::writeConfig(cfg, filepath); /*throw FileError*/ } + try { fff::writeConfig(cfg, filePath); /*throw FileError*/ } catch (FileError&) { assert(false); } //don't bother user! } catch (const FileError& e) @@ -1451,76 +1544,76 @@ XmlCfg parseConfig(const XmlDoc& doc, const Zstring& filepath, int currentXmlFor } -void xmlAccess::readAnyConfig(const std::vector<Zstring>& filePaths, XmlGuiConfig& config, std::wstring& warningMsg) //throw FileError +void fff::readAnyConfig(const std::vector<Zstring>& filePaths, XmlGuiConfig& cfg, std::wstring& warningMsg) //throw FileError { assert(!filePaths.empty()); - std::vector<zen::MainConfiguration> mainCfgs; + std::vector<MainConfiguration> mainCfgs; for (auto it = filePaths.begin(); it != filePaths.end(); ++it) { - const Zstring& filepath = *it; + const Zstring& filePath = *it; const bool firstItem = it == filePaths.begin(); //init all non-"mainCfg" settings with first config file - XmlDoc doc = loadXmlDocument(filepath); //throw FileError + XmlDoc doc = loadXmlDocument(filePath); //throw FileError switch (getXmlTypeNoThrow(doc)) { case XML_TYPE_GUI: { - XmlGuiConfig guiCfg = parseConfig<XmlGuiConfig>(doc, filepath, XML_FORMAT_VER_FFS_GUI, warningMsg); //nothrow + XmlGuiConfig guiCfg = parseConfig<XmlGuiConfig>(doc, filePath, XML_FORMAT_VER_FFS_CFG, warningMsg); //nothrow if (firstItem) - config = guiCfg; + cfg = guiCfg; mainCfgs.push_back(guiCfg.mainCfg); } break; case XML_TYPE_BATCH: { - XmlBatchConfig batchCfg = parseConfig<XmlBatchConfig>(doc, filepath, XML_FORMAT_VER_FFS_BATCH, warningMsg); //nothrow + XmlBatchConfig batchCfg = parseConfig<XmlBatchConfig>(doc, filePath, XML_FORMAT_VER_FFS_CFG, warningMsg); //nothrow if (firstItem) - config = convertBatchToGui(batchCfg); + cfg = convertBatchToGui(batchCfg); mainCfgs.push_back(batchCfg.mainCfg); } break; case XML_TYPE_GLOBAL: case XML_TYPE_OTHER: - throw FileError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filepath))); + throw FileError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filePath))); } } - config.mainCfg = merge(mainCfgs); + cfg.mainCfg = merge(mainCfgs); } //################################################################################################ namespace { -void writeConfig(const CompConfig& cmpConfig, XmlOut& out) +void writeConfig(const CompConfig& cmpCfg, XmlOut& out) { - out["Variant" ](cmpConfig.compareVar); - out["Symlinks"](cmpConfig.handleSymlinks); - out["IgnoreTimeShift"](toTimeShiftPhrase(cmpConfig.ignoreTimeShiftMinutes)); + out["Variant" ](cmpCfg.compareVar); + out["Symlinks"](cmpCfg.handleSymlinks); + out["IgnoreTimeShift"](toTimeShiftPhrase(cmpCfg.ignoreTimeShiftMinutes)); } -void writeConfig(const DirectionConfig& directCfg, XmlOut& out) +void writeConfig(const DirectionConfig& dirCfg, XmlOut& out) { - out["Variant"](directCfg.var); + out["Variant"](dirCfg.var); - if (directCfg.var == DirectionConfig::CUSTOM) + if (dirCfg.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); + outCustDir["LeftOnly" ](dirCfg.custom.exLeftSideOnly); + outCustDir["RightOnly" ](dirCfg.custom.exRightSideOnly); + outCustDir["LeftNewer" ](dirCfg.custom.leftNewer); + outCustDir["RightNewer"](dirCfg.custom.rightNewer); + outCustDir["Different" ](dirCfg.custom.different); + outCustDir["Conflict" ](dirCfg.custom.conflict); } - out["DetectMovedFiles"](directCfg.detectMovedFiles); + out["DetectMovedFiles"](dirCfg.detectMovedFiles); } @@ -1619,108 +1712,109 @@ void writeConfig(const MainConfiguration& mainCfg, XmlOut& out) } -void writeConfig(const XmlGuiConfig& config, XmlOut& out) +void writeConfig(const XmlGuiConfig& cfg, XmlOut& out) { - writeConfig(config.mainCfg, out); //write main config + writeConfig(cfg.mainCfg, out); //write main config //write GUI specific config data XmlOut outGuiCfg = out["GuiConfig"]; - outGuiCfg["MiddleGridView"](config.highlightSyncAction ? "Action" : "Category"); //refactor into enum!? + outGuiCfg["MiddleGridView"](cfg.highlightSyncAction ? "Action" : "Category"); //refactor into enum!? } -void writeConfig(const BatchExclusiveConfig& config, XmlOut& out) +void writeConfig(const BatchExclusiveConfig& cfg, XmlOut& out) { XmlOut outBatchCfg = out["BatchConfig"]; - outBatchCfg["ErrorDialog" ](config.batchErrorDialog); - outBatchCfg["PostSyncAction"](config.postSyncAction); - outBatchCfg["RunMinimized" ](config.runMinimized); - outBatchCfg["LogfileFolder"](config.logFolderPathPhrase); - outBatchCfg["LogfileFolder"].attribute("Limit", config.logfilesCountLimit); + outBatchCfg["ProgressDialog"].attribute("Minimized", cfg.runMinimized); + outBatchCfg["ProgressDialog"].attribute("AutoClose", cfg.autoCloseSummary); + outBatchCfg["ErrorDialog" ](cfg.batchErrorDialog); + outBatchCfg["PostSyncAction"](cfg.postSyncAction); + outBatchCfg["LogfileFolder"](cfg.logFolderPathPhrase); + outBatchCfg["LogfileFolder"].attribute("Limit", cfg.logfilesCountLimit); } -void writeConfig(const XmlBatchConfig& config, XmlOut& out) +void writeConfig(const XmlBatchConfig& cfg, XmlOut& out) { - writeConfig(config.mainCfg, out); - writeConfig(config.batchExCfg, out); + writeConfig(cfg.mainCfg, out); + writeConfig(cfg.batchExCfg, out); } -void writeConfig(const XmlGlobalSettings& config, XmlOut& out) +void writeConfig(const XmlGlobalSettings& cfg, XmlOut& out) { XmlOut outGeneral = out["General"]; - outGeneral["Language"].attribute("Name", config.programLanguage); - - outGeneral["FailSafeFileCopy" ].attribute("Enabled", config.failSafeFileCopy); - outGeneral["CopyLockedFiles" ].attribute("Enabled", config.copyLockedFiles); - outGeneral["CopyFilePermissions" ].attribute("Enabled", config.copyFilePermissions); - outGeneral["AutomaticRetry" ].attribute("Count", config.automaticRetryCount); - outGeneral["AutomaticRetry" ].attribute("Delay", config.automaticRetryDelay); - outGeneral["FileTimeTolerance" ].attribute("Seconds", config.fileTimeTolerance); - outGeneral["FolderAccessTimeout" ].attribute("Seconds", config.folderAccessTimeout); - outGeneral["RunWithBackgroundPriority"].attribute("Enabled", config.runWithBackgroundPriority); - outGeneral["LockDirectoriesDuringSync"].attribute("Enabled", config.createLockFile); - outGeneral["VerifyCopiedFiles" ].attribute("Enabled", config.verifyFileCopy); - outGeneral["LastSyncsLogSizeMax" ].attribute("Bytes", config.lastSyncsLogFileSizeMax); - outGeneral["NotificationSound" ].attribute("CompareFinished", config.soundFileCompareFinished); - outGeneral["NotificationSound" ].attribute("SyncFinished", config.soundFileSyncFinished); + outGeneral["Language"].attribute("Name", cfg.programLanguage); + + outGeneral["FailSafeFileCopy" ].attribute("Enabled", cfg.failSafeFileCopy); + outGeneral["CopyLockedFiles" ].attribute("Enabled", cfg.copyLockedFiles); + outGeneral["CopyFilePermissions" ].attribute("Enabled", cfg.copyFilePermissions); + outGeneral["AutomaticRetry" ].attribute("Count", cfg.automaticRetryCount); + outGeneral["AutomaticRetry" ].attribute("Delay", cfg.automaticRetryDelay); + outGeneral["FileTimeTolerance" ].attribute("Seconds", cfg.fileTimeTolerance); + outGeneral["FolderAccessTimeout" ].attribute("Seconds", cfg.folderAccessTimeout); + outGeneral["RunWithBackgroundPriority"].attribute("Enabled", cfg.runWithBackgroundPriority); + outGeneral["LockDirectoriesDuringSync"].attribute("Enabled", cfg.createLockFile); + outGeneral["VerifyCopiedFiles" ].attribute("Enabled", cfg.verifyFileCopy); + outGeneral["LastSyncsLogSizeMax" ].attribute("Bytes", cfg.lastSyncsLogFileSizeMax); + outGeneral["NotificationSound" ].attribute("CompareFinished", cfg.soundFileCompareFinished); + outGeneral["NotificationSound" ].attribute("SyncFinished", cfg.soundFileSyncFinished); + outGeneral["ProgressDialog" ].attribute("AutoClose", cfg.autoCloseProgressDialog); XmlOut outOpt = outGeneral["OptionalDialogs"]; - outOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warnUnresolvedConflicts); - outOpt["WarnNotEnoughDiskSpace" ].attribute("Enabled", config.optDialogs.warnNotEnoughDiskSpace); - outOpt["WarnSignificantDifference" ].attribute("Enabled", config.optDialogs.warnSignificantDifference); - outOpt["WarnRecycleBinNotAvailable" ].attribute("Enabled", config.optDialogs.warnRecyclerMissing); - outOpt["WarnInputFieldEmpty" ].attribute("Enabled", config.optDialogs.warnInputFieldEmpty); - outOpt["WarnModificationTimeError" ].attribute("Enabled", config.optDialogs.warnModificationTimeError); - //outOpt["WarnDatabaseError" ].attribute("Enabled", config.optDialogs.warnDatabaseError); - outOpt["WarnDependentFolderPair" ].attribute("Enabled", config.optDialogs.warnDependentFolderPair); - outOpt["WarnDependentBaseFolders" ].attribute("Enabled", config.optDialogs.warnDependentBaseFolders); - outOpt["WarnDirectoryLockFailed" ].attribute("Enabled", config.optDialogs.warnDirectoryLockFailed); - outOpt["WarnVersioningFolderPartOfSync" ].attribute("Enabled", config.optDialogs.warnVersioningFolderPartOfSync); - outOpt["ConfirmSaveConfig" ].attribute("Enabled", config.optDialogs.popupOnConfigChange); - outOpt["ConfirmStartSync" ].attribute("Enabled", config.optDialogs.confirmSyncStart); - outOpt["ConfirmExternalCommandMassInvoke"].attribute("Enabled", config.optDialogs.confirmExternalCommandMassInvoke); + outOpt["ConfirmStartSync" ].attribute("Show", cfg.confirmDlgs.confirmSyncStart); + outOpt["ConfirmSaveConfig" ].attribute("Show", cfg.confirmDlgs.popupOnConfigChange); + outOpt["ConfirmExternalCommandMassInvoke"].attribute("Show", cfg.confirmDlgs.confirmExternalCommandMassInvoke); + outOpt["WarnUnresolvedConflicts" ].attribute("Show", cfg.warnDlgs.warnUnresolvedConflicts); + outOpt["WarnNotEnoughDiskSpace" ].attribute("Show", cfg.warnDlgs.warnNotEnoughDiskSpace); + outOpt["WarnSignificantDifference" ].attribute("Show", cfg.warnDlgs.warnSignificantDifference); + outOpt["WarnRecycleBinNotAvailable" ].attribute("Show", cfg.warnDlgs.warnRecyclerMissing); + outOpt["WarnInputFieldEmpty" ].attribute("Show", cfg.warnDlgs.warnInputFieldEmpty); + outOpt["WarnModificationTimeError" ].attribute("Show", cfg.warnDlgs.warnModificationTimeError); + outOpt["WarnDependentFolderPair" ].attribute("Show", cfg.warnDlgs.warnDependentFolderPair); + outOpt["WarnDependentBaseFolders" ].attribute("Show", cfg.warnDlgs.warnDependentBaseFolders); + outOpt["WarnDirectoryLockFailed" ].attribute("Show", cfg.warnDlgs.warnDirectoryLockFailed); + outOpt["WarnVersioningFolderPartOfSync"].attribute("Show", cfg.warnDlgs.warnVersioningFolderPartOfSync); //gui specific global settings (optional) XmlOut outGui = out["Gui"]; XmlOut outWnd = outGui["MainDialog"]; //write application window size and position - outWnd.attribute("Width", config.gui.mainDlg.dlgSize.x); - outWnd.attribute("Height", config.gui.mainDlg.dlgSize.y); - outWnd.attribute("PosX", config.gui.mainDlg.dlgPos.x); - outWnd.attribute("PosY", config.gui.mainDlg.dlgPos.y); - outWnd.attribute("Maximized", config.gui.mainDlg.isMaximized); + outWnd.attribute("Width", cfg.gui.mainDlg.dlgSize.x); + outWnd.attribute("Height", cfg.gui.mainDlg.dlgSize.y); + outWnd.attribute("PosX", cfg.gui.mainDlg.dlgPos.x); + outWnd.attribute("PosY", cfg.gui.mainDlg.dlgPos.y); + outWnd.attribute("Maximized", cfg.gui.mainDlg.isMaximized); XmlOut outCopyTo = outWnd["ManualCopyTo"]; - outCopyTo.attribute("KeepRelativePaths", config.gui.mainDlg.copyToCfg.keepRelPaths); - outCopyTo.attribute("OverwriteIfExists", config.gui.mainDlg.copyToCfg.overwriteIfExists); + outCopyTo.attribute("KeepRelativePaths", cfg.gui.mainDlg.copyToCfg.keepRelPaths); + outCopyTo.attribute("OverwriteIfExists", cfg.gui.mainDlg.copyToCfg.overwriteIfExists); XmlOut outCopyToHistory = outCopyTo["FolderHistory"]; - outCopyToHistory(config.gui.mainDlg.copyToCfg.folderHistory); - outCopyToHistory.attribute("LastUsedPath", config.gui.mainDlg.copyToCfg.lastUsedPath); - outCopyToHistory.attribute("MaxSize", config.gui.mainDlg.copyToCfg.historySizeMax); + outCopyToHistory(cfg.gui.mainDlg.copyToCfg.folderHistory); + outCopyToHistory.attribute("LastUsedPath", cfg.gui.mainDlg.copyToCfg.lastUsedPath); + outCopyToHistory.attribute("MaxSize", cfg.gui.mainDlg.copyToCfg.historySizeMax); - outWnd["CaseSensitiveSearch"].attribute("Enabled", config.gui.mainDlg.textSearchRespectCase); - outWnd["FolderPairsVisible" ].attribute("Max", config.gui.mainDlg.maxFolderPairsVisible); + outWnd["Search" ].attribute("CaseSensitive", cfg.gui.mainDlg.textSearchRespectCase); + outWnd["FolderPairsVisible"].attribute("Max", cfg.gui.mainDlg.maxFolderPairsVisible); //########################################################### XmlOut outConfig = outWnd["ConfigPanel"]; - outConfig.attribute("ScrollPos", config.gui.mainDlg.cfgGridTopRowPos); - outConfig.attribute("SyncOverdue", config.gui.mainDlg.cfgGridSyncOverdueDays); - outConfig.attribute("SortByColumn", config.gui.mainDlg.cfgGridLastSortColumn); - outConfig.attribute("SortAscending", config.gui.mainDlg.cfgGridLastSortAscending); - - outConfig["Columns"](config.gui.mainDlg.cfgGridColumnAttribs); - outConfig["Configurations"].attribute("MaxSize", config.gui.mainDlg.cfgHistItemsMax); - outConfig["Configurations"](config.gui.mainDlg.cfgFileHistory); + outConfig.attribute("ScrollPos", cfg.gui.mainDlg.cfgGridTopRowPos); + outConfig.attribute("SyncOverdue", cfg.gui.mainDlg.cfgGridSyncOverdueDays); + outConfig.attribute("SortByColumn", cfg.gui.mainDlg.cfgGridLastSortColumn); + outConfig.attribute("SortAscending", cfg.gui.mainDlg.cfgGridLastSortAscending); + + outConfig["Columns"](cfg.gui.mainDlg.cfgGridColumnAttribs); + outConfig["Configurations"].attribute("MaxSize", cfg.gui.mainDlg.cfgHistItemsMax); + outConfig["Configurations"](cfg.gui.mainDlg.cfgFileHistory); { - std::vector<Zstring> cfgPaths = config.gui.mainDlg.lastUsedConfigFiles; + std::vector<Zstring> cfgPaths = cfg.gui.mainDlg.lastUsedConfigFiles; for (Zstring& filePath : cfgPaths) filePath = substituteFreeFileSyncDriveLetter(filePath); @@ -1730,46 +1824,46 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) //########################################################### XmlOut outOverview = outWnd["OverviewPanel"]; - outOverview.attribute("ShowPercentage", config.gui.mainDlg.treeGridShowPercentBar); - outOverview.attribute("SortByColumn", config.gui.mainDlg.treeGridLastSortColumn); - outOverview.attribute("SortAscending", config.gui.mainDlg.treeGridLastSortAscending); + outOverview.attribute("ShowPercentage", cfg.gui.mainDlg.treeGridShowPercentBar); + outOverview.attribute("SortByColumn", cfg.gui.mainDlg.treeGridLastSortColumn); + outOverview.attribute("SortAscending", cfg.gui.mainDlg.treeGridLastSortAscending); //write column attributes XmlOut outColTree = outOverview["Columns"]; - outColTree(config.gui.mainDlg.treeGridColumnAttribs); + outColTree(cfg.gui.mainDlg.treeGridColumnAttribs); XmlOut outFileGrid = outWnd["FilePanel"]; - outFileGrid.attribute("ShowIcons", config.gui.mainDlg.showIcons); - outFileGrid.attribute("IconSize", config.gui.mainDlg.iconSize); - outFileGrid.attribute("SashOffset", config.gui.mainDlg.sashOffset); - outFileGrid.attribute("HistoryMaxSize", config.gui.mainDlg.folderHistItemsMax); + outFileGrid.attribute("ShowIcons", cfg.gui.mainDlg.showIcons); + outFileGrid.attribute("IconSize", cfg.gui.mainDlg.iconSize); + outFileGrid.attribute("SashOffset", cfg.gui.mainDlg.sashOffset); + outFileGrid.attribute("HistoryMaxSize", cfg.gui.mainDlg.folderHistItemsMax); - outFileGrid["ColumnsLeft"].attribute("PathFormat", config.gui.mainDlg.itemPathFormatLeftGrid); - outFileGrid["ColumnsLeft"](config.gui.mainDlg.columnAttribLeft); + outFileGrid["ColumnsLeft"].attribute("PathFormat", cfg.gui.mainDlg.itemPathFormatLeftGrid); + outFileGrid["ColumnsLeft"](cfg.gui.mainDlg.columnAttribLeft); - outFileGrid["FolderHistoryLeft" ](config.gui.mainDlg.folderHistoryLeft); + outFileGrid["FolderHistoryLeft" ](cfg.gui.mainDlg.folderHistoryLeft); - outFileGrid["ColumnsRight"].attribute("PathFormat", config.gui.mainDlg.itemPathFormatRightGrid); - outFileGrid["ColumnsRight"](config.gui.mainDlg.columnAttribRight); + outFileGrid["ColumnsRight"].attribute("PathFormat", cfg.gui.mainDlg.itemPathFormatRightGrid); + outFileGrid["ColumnsRight"](cfg.gui.mainDlg.columnAttribRight); - outFileGrid["FolderHistoryRight"](config.gui.mainDlg.folderHistoryRight); + outFileGrid["FolderHistoryRight"](cfg.gui.mainDlg.folderHistoryRight); //########################################################### - outWnd["DefaultViewFilter"](config.gui.mainDlg.viewFilterDefault); - outWnd["Perspective5"](config.gui.mainDlg.guiPerspectiveLast); + outWnd["DefaultViewFilter"](cfg.gui.mainDlg.viewFilterDefault); + outWnd["Perspective" ](cfg.gui.mainDlg.guiPerspectiveLast); - outGui["DefaultExclusionFilter"](splitFilterByLines(config.gui.defaultExclusionFilter)); + outGui["DefaultExclusionFilter"](splitFilterByLines(cfg.gui.defaultExclusionFilter)); - outGui["CommandHistory"](config.gui.commandHistory); - outGui["CommandHistory"].attribute("MaxSize", config.gui.commandHistItemsMax); + outGui["CommandHistory"](cfg.gui.commandHistory); + outGui["CommandHistory"].attribute("MaxSize", cfg.gui.commandHistItemsMax); //external applications - outGui["ExternalApps"](config.gui.externelApplications); + outGui["ExternalApps"](cfg.gui.externalApps); //last update check - outGui["LastOnlineCheck" ](config.gui.lastUpdateCheck); - outGui["LastOnlineVersion"](config.gui.lastOnlineVersion); + outGui["LastOnlineCheck" ](cfg.gui.lastUpdateCheck); + outGui["LastOnlineVersion"](cfg.gui.lastOnlineVersion); //batch specific global settings //XmlOut outBatch = out["Batch"]; @@ -1777,7 +1871,7 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) template <class ConfigType> -void writeConfig(const ConfigType& config, XmlType type, int xmlFormatVer, const Zstring& filepath) +void writeConfig(const ConfigType& cfg, XmlType type, int xmlFormatVer, const Zstring& filePath) { XmlDoc doc("FreeFileSync"); setXmlType(doc, type); //throw() @@ -1785,33 +1879,33 @@ void writeConfig(const ConfigType& config, XmlType type, int xmlFormatVer, const doc.root().setAttribute("XmlFormat", xmlFormatVer); XmlOut out(doc); - writeConfig(config, out); + writeConfig(cfg, out); - saveXmlDocument(doc, filepath); //throw FileError + saveXmlDocument(doc, filePath); //throw FileError } } -void xmlAccess::writeConfig(const XmlGuiConfig& cfg, const Zstring& filepath) +void fff::writeConfig(const XmlGuiConfig& cfg, const Zstring& filePath) { - ::writeConfig(cfg, XML_TYPE_GUI, XML_FORMAT_VER_FFS_GUI, filepath); //throw FileError + ::writeConfig(cfg, XML_TYPE_GUI, XML_FORMAT_VER_FFS_CFG, filePath); //throw FileError } -void xmlAccess::writeConfig(const XmlBatchConfig& cfg, const Zstring& filepath) +void fff::writeConfig(const XmlBatchConfig& cfg, const Zstring& filePath) { - ::writeConfig(cfg, XML_TYPE_BATCH, XML_FORMAT_VER_FFS_BATCH, filepath); //throw FileError + ::writeConfig(cfg, XML_TYPE_BATCH, XML_FORMAT_VER_FFS_CFG, filePath); //throw FileError } -void xmlAccess::writeConfig(const XmlGlobalSettings& cfg, const Zstring& filepath) +void fff::writeConfig(const XmlGlobalSettings& cfg, const Zstring& filePath) { - ::writeConfig(cfg, XML_TYPE_GLOBAL, XML_FORMAT_VER_GLOBAL, filepath); //throw FileError + ::writeConfig(cfg, XML_TYPE_GLOBAL, XML_FORMAT_VER_GLOBAL, filePath); //throw FileError } -std::wstring xmlAccess::extractJobName(const Zstring& configFilename) +std::wstring fff::extractJobName(const Zstring& cfgFilePath) { - const Zstring shortName = afterLast(configFilename, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); + const Zstring shortName = afterLast(cfgFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); const Zstring jobName = beforeLast(shortName, Zstr('.'), IF_MISSING_RETURN_ALL); return utfTo<std::wstring>(jobName); } diff --git a/FreeFileSync/Source/lib/process_xml.h b/FreeFileSync/Source/lib/process_xml.h index 1328ceb0..eeaf5c1b 100755 --- a/FreeFileSync/Source/lib/process_xml.h +++ b/FreeFileSync/Source/lib/process_xml.h @@ -16,7 +16,7 @@ #include "../ui/cfg_grid.h" -namespace xmlAccess +namespace fff { enum XmlType { @@ -26,7 +26,7 @@ enum XmlType XML_TYPE_OTHER }; -XmlType getXmlType(const Zstring& filepath); //throw FileError +XmlType getXmlType(const Zstring& filePath); //throw FileError enum class BatchErrorDialog @@ -38,21 +38,21 @@ enum class BatchErrorDialog enum class PostSyncAction { - SUMMARY, - EXIT, + NONE, SLEEP, SHUTDOWN }; -using Description = std::wstring; -using Commandline = Zstring; -using ExternalApps = std::vector<std::pair<Description, Commandline>>; +struct ExternalApp +{ + std::wstring description; + Zstring cmdLine; +}; //--------------------------------------------------------------------- struct XmlGuiConfig { - zen::MainConfiguration mainCfg; - + MainConfiguration mainCfg; bool highlightSyncAction = true; }; @@ -63,13 +63,15 @@ bool operator==(const XmlGuiConfig& lhs, const XmlGuiConfig& rhs) return lhs.mainCfg == rhs.mainCfg && lhs.highlightSyncAction == rhs.highlightSyncAction; } +inline bool operator!=(const XmlGuiConfig& lhs, const XmlGuiConfig& rhs) { return !(lhs == rhs); } struct BatchExclusiveConfig { BatchErrorDialog batchErrorDialog = BatchErrorDialog::SHOW; bool runMinimized = false; - PostSyncAction postSyncAction = PostSyncAction::SUMMARY; + bool autoCloseSummary = false; + PostSyncAction postSyncAction = PostSyncAction::NONE; Zstring logFolderPathPhrase; int logfilesCountLimit = -1; //max logfiles; 0 := don't save logfiles; < 0 := no limit }; @@ -77,27 +79,54 @@ struct BatchExclusiveConfig struct XmlBatchConfig { - zen::MainConfiguration mainCfg; + MainConfiguration mainCfg; BatchExclusiveConfig batchExCfg; }; -struct OptionalDialogs +struct ConfirmationDialogs { - bool warnDependentFolderPair = true; - bool warnDependentBaseFolders = true; - bool warnSignificantDifference = true; - bool warnNotEnoughDiskSpace = true; - bool warnUnresolvedConflicts = true; - bool warnModificationTimeError = true; - bool warnRecyclerMissing = true; - bool warnInputFieldEmpty = true; - bool warnDirectoryLockFailed = true; - bool warnVersioningFolderPartOfSync = true; bool popupOnConfigChange = true; bool confirmSyncStart = true; bool confirmExternalCommandMassInvoke = true; }; +inline bool operator==(const ConfirmationDialogs& lhs, const ConfirmationDialogs& rhs) +{ + return lhs.popupOnConfigChange == rhs.popupOnConfigChange && + lhs.confirmSyncStart == rhs.confirmSyncStart && + lhs.confirmExternalCommandMassInvoke == rhs.confirmExternalCommandMassInvoke; +} +inline bool operator!=(const ConfirmationDialogs& lhs, const ConfirmationDialogs& rhs) { return !(lhs == rhs); } + + +struct WarningDialogs +{ + bool warnDependentFolderPair = true; + bool warnDependentBaseFolders = true; + bool warnSignificantDifference = true; + bool warnNotEnoughDiskSpace = true; + bool warnUnresolvedConflicts = true; + bool warnModificationTimeError = true; + bool warnRecyclerMissing = true; + bool warnInputFieldEmpty = true; + bool warnDirectoryLockFailed = true; + bool warnVersioningFolderPartOfSync = true; +}; +inline bool operator==(const WarningDialogs& lhs, const WarningDialogs& rhs) +{ + return lhs.warnDependentFolderPair == rhs.warnDependentFolderPair && + lhs.warnDependentBaseFolders == rhs.warnDependentBaseFolders && + lhs.warnSignificantDifference == rhs.warnSignificantDifference && + lhs.warnNotEnoughDiskSpace == rhs.warnNotEnoughDiskSpace && + lhs.warnUnresolvedConflicts == rhs.warnUnresolvedConflicts && + lhs.warnModificationTimeError == rhs.warnModificationTimeError && + lhs.warnRecyclerMissing == rhs.warnRecyclerMissing && + lhs.warnInputFieldEmpty == rhs.warnInputFieldEmpty && + lhs.warnDirectoryLockFailed == rhs.warnDirectoryLockFailed && + lhs.warnVersioningFolderPartOfSync == rhs.warnVersioningFolderPartOfSync; +} +inline bool operator!=(const WarningDialogs& lhs, const WarningDialogs& rhs) { return !(lhs == rhs); } + enum FileIconSize @@ -150,7 +179,7 @@ struct XmlGlobalSettings //--------------------------------------------------------------------- //Shared (GUI/BATCH) settings - wxLanguage programLanguage = zen::getSystemLanguage(); + wxLanguage programLanguage = getSystemLanguage(); bool failSafeFileCopy = true; bool copyLockedFiles = false; //safer default: avoid copies of partially written files bool copyFilePermissions = false; @@ -165,8 +194,10 @@ struct XmlGlobalSettings size_t lastSyncsLogFileSizeMax = 100000; //maximum size for LastSyncs.log: use a human-readable number Zstring soundFileCompareFinished; Zstring soundFileSyncFinished = Zstr("gong.wav"); + bool autoCloseProgressDialog = false; - OptionalDialogs optDialogs; + ConfirmationDialogs confirmDlgs; + WarningDialogs warnDlgs; //--------------------------------------------------------------------- struct Gui @@ -192,17 +223,17 @@ struct XmlGlobalSettings size_t cfgGridTopRowPos = 0; int cfgGridSyncOverdueDays = 7; - zen::ColumnTypeCfg cfgGridLastSortColumn = zen::cfgGridLastSortColumnDefault; - bool cfgGridLastSortAscending = zen::getDefaultSortDirection(zen::cfgGridLastSortColumnDefault); - std::vector<zen::ColAttributesCfg> cfgGridColumnAttribs = zen::getCfgGridDefaultColAttribs(); + ColumnTypeCfg cfgGridLastSortColumn = cfgGridLastSortColumnDefault; + bool cfgGridLastSortAscending = getDefaultSortDirection(cfgGridLastSortColumnDefault); + std::vector<ColAttributesCfg> cfgGridColumnAttribs = getCfgGridDefaultColAttribs(); size_t cfgHistItemsMax = 100; std::vector<ConfigFileItem> cfgFileHistory; std::vector<Zstring> lastUsedConfigFiles; - bool treeGridShowPercentBar = zen::treeGridShowPercentageDefault; - zen::ColumnTypeTree treeGridLastSortColumn = zen::treeGridLastSortColumnDefault; //remember sort on overview panel - bool treeGridLastSortAscending = zen::getDefaultSortDirection(zen::treeGridLastSortColumnDefault); // - std::vector<zen::ColAttributesTree> treeGridColumnAttribs = zen::getTreeGridDefaultColAttribs(); + bool treeGridShowPercentBar = treeGridShowPercentageDefault; + ColumnTypeTree treeGridLastSortColumn = treeGridLastSortColumnDefault; //remember sort on overview panel + bool treeGridLastSortAscending = getDefaultSortDirection(treeGridLastSortColumnDefault); // + std::vector<ColAttributesTree> treeGridColumnAttribs = getTreeGridDefaultColAttribs(); std::vector<Zstring> folderHistoryLeft; std::vector<Zstring> folderHistoryRight; @@ -211,11 +242,11 @@ struct XmlGlobalSettings FileIconSize iconSize = ICON_SIZE_SMALL; int sashOffset = 0; - zen::ItemPathFormat itemPathFormatLeftGrid = zen::defaultItemPathFormatLeftGrid; - zen::ItemPathFormat itemPathFormatRightGrid = zen::defaultItemPathFormatRightGrid; + ItemPathFormat itemPathFormatLeftGrid = defaultItemPathFormatLeftGrid; + ItemPathFormat itemPathFormatRightGrid = defaultItemPathFormatRightGrid; - std::vector<zen::ColAttributesRim> columnAttribLeft = zen::getFileGridDefaultColAttribsLeft(); - std::vector<zen::ColAttributesRim> columnAttribRight = zen::getFileGridDefaultColAttribsRight(); + std::vector<ColAttributesRim> columnAttribLeft = getFileGridDefaultColAttribsLeft(); + std::vector<ColAttributesRim> columnAttribRight = getFileGridDefaultColAttribsRight(); ViewFilterDefault viewFilterDefault; wxString guiPerspectiveLast; //used by wxAuiManager @@ -227,7 +258,7 @@ struct XmlGlobalSettings std::vector<Zstring> commandHistory; size_t commandHistItemsMax = 8; - ExternalApps externelApplications + std::vector<ExternalApp> externalApps { //default external app descriptions will be translated "on the fly"!!! //CONTRACT: first entry will be used for [Enter] or mouse double-click! @@ -242,22 +273,22 @@ struct XmlGlobalSettings }; //read/write specific config types -void readConfig(const Zstring& filepath, XmlGuiConfig& config, std::wstring& warningMsg); // -void readConfig(const Zstring& filepath, XmlBatchConfig& config, std::wstring& warningMsg); //throw FileError -void readConfig(const Zstring& filepath, XmlGlobalSettings& config, std::wstring& warningMsg); // +void readConfig(const Zstring& filePath, XmlGuiConfig& cfg, std::wstring& warningMsg); // +void readConfig(const Zstring& filePath, XmlBatchConfig& cfg, std::wstring& warningMsg); //throw FileError +void readConfig(const Zstring& filePath, XmlGlobalSettings& cfg, std::wstring& warningMsg); // -void writeConfig(const XmlGuiConfig& config, const Zstring& filepath); // -void writeConfig(const XmlBatchConfig& config, const Zstring& filepath); //throw FileError -void writeConfig(const XmlGlobalSettings& config, const Zstring& filepath); // +void writeConfig(const XmlGuiConfig& cfg, const Zstring& filePath); // +void writeConfig(const XmlBatchConfig& cfg, const Zstring& filePath); //throw FileError +void writeConfig(const XmlGlobalSettings& cfg, const Zstring& filePath); // //convert (multiple) *.ffs_gui, *.ffs_batch files or combinations of both into target config structure: -void readAnyConfig(const std::vector<Zstring>& filepaths, XmlGuiConfig& config, std::wstring& warningMsg); //throw FileError +void readAnyConfig(const std::vector<Zstring>& filePaths, XmlGuiConfig& cfg, std::wstring& warningMsg); //throw FileError //config conversion utilities XmlGuiConfig convertBatchToGui(const XmlBatchConfig& batchCfg); //noexcept XmlBatchConfig convertGuiToBatch(const XmlGuiConfig& guiCfg, const BatchExclusiveConfig& batchExCfg); // -std::wstring extractJobName(const Zstring& configFilename); +std::wstring extractJobName(const Zstring& cfgFilePath); } #endif //PROCESS_XML_H_28345825704254262435 diff --git a/FreeFileSync/Source/lib/resolve_path.cpp b/FreeFileSync/Source/lib/resolve_path.cpp index 62d56577..7a17422b 100755 --- a/FreeFileSync/Source/lib/resolve_path.cpp +++ b/FreeFileSync/Source/lib/resolve_path.cpp @@ -32,8 +32,8 @@ Opt<Zstring> getEnvironmentVar(const Zstring& name) trim(value); //remove leading, trailing blanks //remove leading, trailing double-quotes - if (startsWith(value, Zstr("\"")) && - endsWith (value, Zstr("\"")) && + if (startsWith(value, Zstr('\"')) && + endsWith (value, Zstr('\"')) && value.length() >= 2) value = Zstring(value.c_str() + 1, value.length() - 2); @@ -124,7 +124,7 @@ const Zchar MACRO_SEP = Zstr('%'); } //returns expanded or original string -Zstring zen::expandMacros(const Zstring& text) +Zstring fff::expandMacros(const Zstring& text) { if (contains(text, MACRO_SEP)) { @@ -173,7 +173,6 @@ Zstring expandVolumeName(Zstring pathPhrase) // [volname]:\folder [volnam } return pathPhrase; } -} void getDirectoryAliasesRecursive(const Zstring& pathPhrase, std::set<Zstring, LessFilePath>& output) @@ -205,15 +204,16 @@ void getDirectoryAliasesRecursive(const Zstring& pathPhrase, std::set<Zstring, L //4. replace (all) macros: %UserProfile% -> C:\Users\<user> { - const Zstring pathExp = expandMacros(pathPhrase); + const Zstring pathExp = fff::expandMacros(pathPhrase); if (pathExp != pathPhrase) if (output.insert(pathExp).second) getDirectoryAliasesRecursive(pathExp, output); //recurse! } } +} -std::vector<Zstring> zen::getDirectoryAliases(const Zstring& folderPathPhrase) +std::vector<Zstring> fff::getDirectoryAliases(const Zstring& folderPathPhrase) { const Zstring dirPath = trimCpy(folderPathPhrase, true, false); if (dirPath.empty()) @@ -230,7 +230,7 @@ std::vector<Zstring> zen::getDirectoryAliases(const Zstring& folderPathPhrase) //coordinate changes with acceptsFolderPathPhraseNative()! -Zstring zen::getResolvedFilePath(const Zstring& pathPhrase) //noexcept +Zstring fff::getResolvedFilePath(const Zstring& pathPhrase) //noexcept { Zstring path = pathPhrase; diff --git a/FreeFileSync/Source/lib/resolve_path.h b/FreeFileSync/Source/lib/resolve_path.h index 8e977c4a..dc23d1de 100755 --- a/FreeFileSync/Source/lib/resolve_path.h +++ b/FreeFileSync/Source/lib/resolve_path.h @@ -11,7 +11,7 @@ #include <zen/zstring.h> -namespace zen +namespace fff { /* - expand macros diff --git a/FreeFileSync/Source/lib/return_codes.h b/FreeFileSync/Source/lib/return_codes.h index 21995ccc..9604142c 100755 --- a/FreeFileSync/Source/lib/return_codes.h +++ b/FreeFileSync/Source/lib/return_codes.h @@ -7,7 +7,7 @@ #ifndef RETURN_CODES_H_81307482137054156 #define RETURN_CODES_H_81307482137054156 -namespace zen +namespace fff { enum FfsReturnCode { diff --git a/FreeFileSync/Source/lib/soft_filter.h b/FreeFileSync/Source/lib/soft_filter.h index c6ce8cd0..95ff5a79 100755 --- a/FreeFileSync/Source/lib/soft_filter.h +++ b/FreeFileSync/Source/lib/soft_filter.h @@ -12,7 +12,7 @@ #include "../structures.h" -namespace zen +namespace fff { /* Semantics of SoftFilter: @@ -28,7 +28,7 @@ public: size_t sizeMin, UnitSize unitSizeMin, size_t sizeMax, UnitSize unitSizeMax); - bool matchTime(int64_t writeTime) const { return timeFrom_ <= writeTime; } + bool matchTime(time_t writeTime) const { return timeFrom_ <= writeTime; } bool matchSize(uint64_t fileSize) const { return sizeMin_ <= fileSize && fileSize <= sizeMax_; } bool matchFolder() const { return matchesFolder_; } bool isNull() const; //filter is equivalent to NullFilter, but may be technically slower @@ -37,17 +37,17 @@ public: friend SoftFilter combineFilters(const SoftFilter& first, const SoftFilter& second); private: - SoftFilter(int64_t timeFrom, + SoftFilter(time_t timeFrom, uint64_t sizeMin, uint64_t sizeMax, bool matchesFolder); - int64_t timeFrom_ = 0; //unit: UTC, seconds - uint64_t sizeMin_ = 0; //unit: bytes - uint64_t sizeMax_ = 0; //unit: bytes + time_t timeFrom_ = 0; //unit: UTC, seconds + uint64_t sizeMin_ = 0; //unit: bytes + uint64_t sizeMax_ = 0; //unit: bytes const bool matchesFolder_; }; -} + @@ -62,8 +62,6 @@ private: // ----------------------- implementation ----------------------- -namespace zen -{ inline SoftFilter::SoftFilter(size_t timeSpan, UnitTime unitTimeSpan, size_t sizeMin, UnitSize unitSizeMin, @@ -81,7 +79,7 @@ SoftFilter::SoftFilter(size_t timeSpan, UnitTime unitTimeSpan, } inline -SoftFilter::SoftFilter(int64_t timeFrom, +SoftFilter::SoftFilter(time_t timeFrom, uint64_t sizeMin, uint64_t sizeMax, bool matchesFolder) : @@ -90,6 +88,7 @@ SoftFilter::SoftFilter(int64_t timeFrom, sizeMax_ (sizeMax), matchesFolder_(matchesFolder) {} + inline SoftFilter combineFilters(const SoftFilter& lhs, const SoftFilter& rhs) { @@ -99,10 +98,11 @@ SoftFilter combineFilters(const SoftFilter& lhs, const SoftFilter& rhs) lhs.matchesFolder_ && rhs.matchesFolder_); } + inline bool SoftFilter::isNull() const //filter is equivalent to NullFilter, but may be technically slower { - return timeFrom_ == std::numeric_limits<int64_t>::min() && + return timeFrom_ == std::numeric_limits<time_t>::min() && sizeMin_ == 0U && sizeMax_ == std::numeric_limits<uint64_t>::max() && matchesFolder_ == true; diff --git a/FreeFileSync/Source/lib/status_handler.cpp b/FreeFileSync/Source/lib/status_handler.cpp index 11d04f48..9e2f78db 100755 --- a/FreeFileSync/Source/lib/status_handler.cpp +++ b/FreeFileSync/Source/lib/status_handler.cpp @@ -8,19 +8,18 @@ #include <chrono> #include <zen/basic_math.h> -using namespace zen; - namespace { std::chrono::steady_clock::time_point lastExec; }; -bool zen::updateUiIsAllowed() + +bool fff::updateUiIsAllowed() { const auto now = std::chrono::steady_clock::now(); - if (numeric::dist(now, lastExec) > std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS)) //handle potential chrono wrap-around! + if (numeric::dist(now, lastExec) > UI_UPDATE_INTERVAL) //handle potential chrono wrap-around! { lastExec = now; return true; diff --git a/FreeFileSync/Source/lib/status_handler.h b/FreeFileSync/Source/lib/status_handler.h index 6e8b5f96..779c2c60 100755 --- a/FreeFileSync/Source/lib/status_handler.h +++ b/FreeFileSync/Source/lib/status_handler.h @@ -9,11 +9,14 @@ #include "../process_callback.h" #include <vector> +#include <chrono> +#include <thread> #include <string> #include <zen/i18n.h> +#include <zen/basic_math.h> -namespace zen +namespace fff { bool updateUiIsAllowed(); //test if a specific amount of time is over @@ -55,6 +58,7 @@ struct Statistics virtual int64_t getBytesCurrent(ProcessCallback::Phase phaseId) const = 0; virtual int64_t getBytesTotal (ProcessCallback::Phase phaseId) const = 0; + virtual zen::Opt<AbortTrigger> getAbortStatus() const = 0; virtual const std::wstring& currentStatusText() const = 0; }; @@ -63,8 +67,7 @@ struct Statistics class StatusHandler : public ProcessCallback, public AbortCallback, public Statistics { public: - StatusHandler() : - numbersCurrent_(4), //init with phase count + StatusHandler() : numbersCurrent_(4), //init with phase count numbersTotal_ (4) {} // //implement parts of ProcessCallback @@ -75,29 +78,45 @@ public: } void updateProcessedData(int itemsDelta, int64_t bytesDelta) override { updateData(numbersCurrent_, itemsDelta, bytesDelta); } //note: these methods MUST NOT throw in order - void updateTotalData (int itemsDelta, int64_t bytesDelta) override { updateData(numbersTotal_, itemsDelta, bytesDelta); } //to properly allow undoing setting of statistics! + void updateTotalData (int itemsDelta, int64_t bytesDelta) override { updateData(numbersTotal_, itemsDelta, bytesDelta); } //to allow usage within destructors! - void requestUiRefresh() override //throw X + void requestUiRefresh() override final //throw X { - if (abortRequested_) //triggered by requestAbortion() + if (updateUiIsAllowed()) + forceUiRefresh(); //throw X + } + + void forceUiRefresh() override final //throw X + { + const bool abortRequestedBefore = static_cast<bool>(abortRequested_); + + forceUiRefreshNoThrow(); + + //triggered by userRequestAbort() + // => sufficient to evaluate occasionally when updateUiIsAllowed()! + // => refresh *before* throwing: support requestUiRefresh() during destruction + if (abortRequested_) { - forceUiRefresh(); + if (!abortRequestedBefore) + forceUiRefreshNoThrow(); //just once to immediately show the "Stop requested..." status after user clicks cancel throw AbortProcess(); } - if (updateUiIsAllowed()) - forceUiRefresh(); } - void reportStatus(const std::wstring& text) override //throw X + virtual void forceUiRefreshNoThrow() = 0; //noexcept + + void reportStatus(const std::wstring& text) override final //throw X { //assert(!text.empty()); -> possible: start of parallel scan - if (!abortRequested_) statusText_ = text; + + statusText_ = text; //update text *before* running operations that can throw requestUiRefresh(); //throw X } + void reportInfo(const std::wstring& text) override //throw X { assert(!text.empty()); - if (!abortRequested_) statusText_ = text; + statusText_ = text; requestUiRefresh(); //throw X //log text in derived class } @@ -105,24 +124,25 @@ public: void abortProcessNow() override { if (!abortRequested_) abortRequested_ = AbortTrigger::PROGRAM; + forceUiRefreshNoThrow(); throw AbortProcess(); } void userAbortProcessNow() { - if (!abortRequested_) abortRequested_ = AbortTrigger::USER; + abortRequested_ = AbortTrigger::USER; //may overwrite AbortTrigger::PROGRAM + forceUiRefreshNoThrow(); throw AbortProcess(); } //implement AbortCallback - void userRequestAbort() override + void userRequestAbort() override final { - if (!abortRequested_) abortRequested_ = AbortTrigger::USER; - statusText_ = _("Stop requested: Waiting for current operation to finish..."); + abortRequested_ = AbortTrigger::USER; //may overwrite AbortTrigger::PROGRAM } //called from GUI code: this does NOT call abortProcessNow() immediately, but later when we're out of the C GUI call stack //implement Statistics - Phase currentPhase() const override { return currentPhase_; } + Phase currentPhase() const override final { return currentPhase_; } int getItemsCurrent(Phase phaseId) const override { return refNumbers(numbersCurrent_, phaseId).items; } int getItemsTotal (Phase phaseId) const override { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_, phaseId).items; } @@ -132,8 +152,7 @@ public: const std::wstring& currentStatusText() const override { return statusText_; } -protected: - Opt<AbortTrigger> getAbortStatus() const { return abortRequested_; } + zen::Opt<AbortTrigger> getAbortStatus() const override { return abortRequested_; } private: struct StatNumber @@ -174,8 +193,26 @@ private: StatNumbers numbersTotal_; std::wstring statusText_; - Opt<AbortTrigger> abortRequested_; + zen::Opt<AbortTrigger> abortRequested_; }; + +//------------------------------------------------------------------------------------------ + +inline +void delayAndCountDown(const std::wstring& operationName, size_t delayInSec, const std::function<void(const std::wstring& msg)>& notifyStatus) +{ + assert(notifyStatus && !zen::endsWith(operationName, L".")); + + const auto delayUntil = std::chrono::steady_clock::now() + std::chrono::seconds(delayInSec); + for (auto now = std::chrono::steady_clock::now(); now < delayUntil; now = std::chrono::steady_clock::now()) + { + const auto timeMs = std::chrono::duration_cast<std::chrono::milliseconds>(delayUntil - now).count(); + if (notifyStatus) + notifyStatus(operationName + L"... " + _P("1 sec", "%x sec", numeric::integerDivideRoundUp(timeMs, 1000))); + + std::this_thread::sleep_for(UI_UPDATE_INTERVAL / 2); + } +} } #endif //STATUS_HANDLER_H_81704805908341534 diff --git a/FreeFileSync/Source/lib/status_handler_impl.h b/FreeFileSync/Source/lib/status_handler_impl.h index cffee505..eab46960 100755 --- a/FreeFileSync/Source/lib/status_handler_impl.h +++ b/FreeFileSync/Source/lib/status_handler_impl.h @@ -12,7 +12,7 @@ #include "../process_callback.h" -namespace zen +namespace fff { template <typename Function> inline zen::Opt<std::wstring> tryReportingError(Function cmd, ProcessCallback& handler /*throw X*/) //return ignored error message if available @@ -21,9 +21,9 @@ zen::Opt<std::wstring> tryReportingError(Function cmd, ProcessCallback& handler try { cmd(); //throw FileError - return NoValue(); + return zen::NoValue(); } - catch (FileError& error) + catch (zen::FileError& error) { switch (handler.reportError(error.toString(), retryNumber)) //throw X { @@ -60,7 +60,7 @@ public: { cb_.updateProcessedData(itemsDelta, bytesDelta); //nothrow! -> ensure client and service provider are in sync! itemsReported_ += itemsDelta; - bytesReported_ += bytesDelta; // + bytesReported_ += bytesDelta; // //special rule: avoid temporary statistics mess up, even though they are corrected anyway below: if (itemsReported_ > itemsExpected_) diff --git a/FreeFileSync/Source/lib/versioning.cpp b/FreeFileSync/Source/lib/versioning.cpp index 591b3117..6d8ea721 100755 --- a/FreeFileSync/Source/lib/versioning.cpp +++ b/FreeFileSync/Source/lib/versioning.cpp @@ -2,6 +2,7 @@ #include <cstddef> //required by GCC 4.8.1 to find ptrdiff_t using namespace zen; +using namespace fff; namespace @@ -14,7 +15,7 @@ Zstring getDotExtension(const Zstring& relativePath) //including "." if extensio }; } -bool impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameVersioned) //e.g. ("Sample.txt", "Sample.txt 2012-05-15 131513.txt") +bool fff::impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameVersioned) //e.g. ("Sample.txt", "Sample.txt 2012-05-15 131513.txt") { auto it = shortnameVersioned.begin(); auto itLast = shortnameVersioned.end(); @@ -110,13 +111,10 @@ void moveExistingItemToVersioning(const AbstractPath& sourcePath, const Abstract try { pd = AFS::getPathStatus(targetPath); /*throw FileError*/ } catch (FileError&) { - - warn_static("remove after test: https://www.freefilesync.org/forum/viewtopic.php?f=8&t=4765&p=15981#p15981") - warn_static("really? is previous exception is more relevant?") - throw; - - - } //previous exception is more relevant + //previous exception is more relevant in general + //BUT, we might be hiding a second unrelated issue: https://www.freefilesync.org/forum/viewtopic.php?t=4765#p16016 + //=> FFS considers session faulty and tries to create a new one, which might fail with: LIBSSH2_ERROR_AUTHENTICATION_FAILED + } if (pd) { diff --git a/FreeFileSync/Source/lib/versioning.h b/FreeFileSync/Source/lib/versioning.h index a0a437f3..4236aff2 100755 --- a/FreeFileSync/Source/lib/versioning.h +++ b/FreeFileSync/Source/lib/versioning.h @@ -15,7 +15,7 @@ #include "../algorithm.h" -namespace zen +namespace fff { //e.g. move C:\Source\subdir\Sample.txt -> D:\Revisions\subdir\Sample.txt 2012-05-15 131513.txt //scheme: <revisions directory>\<relpath>\<filename>.<ext> YYYY-MM-DD HHMMSS.<ext> @@ -34,11 +34,13 @@ class FileVersioner public: FileVersioner(const AbstractPath& versioningFolderPath, //throw FileError VersioningStyle versioningStyle, - const TimeComp& timeStamp) : + const zen::TimeComp& timeStamp) : versioningFolderPath_(versioningFolderPath), versioningStyle_(versioningStyle), - timeStamp_(formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp)) //e.g. "2012-05-15 131513" + timeStamp_(zen::formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp)) //e.g. "2012-05-15 131513" { + using namespace zen; + if (AbstractFileSystem::isNullPath(versioningFolderPath_)) throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); @@ -49,7 +51,7 @@ public: bool revisionFile(const FileDescriptor& fileDescr, //throw FileError; return "false" if file is not existing const Zstring& relativePath, //called frequently if move has to revert to copy + delete => see zen::copyFile for limitations when throwing exceptions! - const IOCallback& notifyUnbufferedIO); //may be nullptr + const zen::IOCallback& notifyUnbufferedIO); //may be nullptr bool revisionSymlink(const AbstractPath& linkPath, const Zstring& relativePath); //throw FileError; return "false" if file is not existing @@ -59,7 +61,7 @@ public: const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFileMove, //one call for each *existing* object! const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFolderMove, // //called frequently if move has to revert to copy + delete => see zen::copyFile for limitations when throwing exceptions! - const IOCallback& notifyUnbufferedIO); + const zen::IOCallback& notifyUnbufferedIO); //void limitVersions(std::function<void()> updateUI); //throw FileError; call when done revisioning! @@ -67,7 +69,7 @@ private: void revisionFolderImpl(const AbstractPath& folderPath, const Zstring& relativePath, const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFileMove, const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFolderMove, - const IOCallback& notifyUnbufferedIO); //throw FileError + const zen::IOCallback& notifyUnbufferedIO); //throw FileError AbstractPath generateVersionedPath(const Zstring& relativePath) const; diff --git a/FreeFileSync/Source/process_callback.h b/FreeFileSync/Source/process_callback.h index 523c888b..7a792917 100755 --- a/FreeFileSync/Source/process_callback.h +++ b/FreeFileSync/Source/process_callback.h @@ -9,11 +9,14 @@ #include <string> #include <cstdint> +#include <chrono> +namespace fff +{ //interface for comparison and synchronization process status updates (used by GUI or Batch mode) -const int UI_UPDATE_INTERVAL_MS = 100; //unit: [ms]; perform ui updates not more often than necessary, -//100 seems to be a good value with only a minimal performance loss; also used by Win 7 copy progress bar +const std::chrono::milliseconds UI_UPDATE_INTERVAL(100); //perform ui updates not more often than necessary, +//100 ms seems to be a good value with only a minimal performance loss; also used by Win 7 copy progress bar //this one is required by async directory existence check! //report status during comparison and synchronization @@ -37,7 +40,7 @@ struct ProcessCallback //it is in general paired with a call to requestUiRefresh() to compensate! virtual void updateProcessedData(int itemsDelta, int64_t bytesDelta) = 0; //noexcept!! virtual void updateTotalData (int itemsDelta, int64_t bytesDelta) = 0; // - /*the estimated and actual total workload may change *during* sync: + /* the estimated and actual total workload may change *during* sync: 1. file cannot be moved -> fallback to copy + delete 2. file copy, actual size changed after comparison 3. file contains significant ADS data, is sparse or compressed @@ -73,5 +76,6 @@ struct ProcessCallback virtual void abortProcessNow() = 0; //will throw an exception => don't call while in a C GUI callstack }; +} #endif //PROCESS_CALLBACK_H_48257827842345454545 diff --git a/FreeFileSync/Source/structures.cpp b/FreeFileSync/Source/structures.cpp index 9e9d3cf8..47552362 100755 --- a/FreeFileSync/Source/structures.cpp +++ b/FreeFileSync/Source/structures.cpp @@ -13,9 +13,10 @@ #include "lib/hard_filter.h" using namespace zen; +using namespace fff; -std::vector<unsigned int> zen::fromTimeShiftPhrase(const std::wstring& timeShiftPhrase) +std::vector<unsigned int> fff::fromTimeShiftPhrase(const std::wstring& timeShiftPhrase) { std::wstring tmp = replaceCpy(timeShiftPhrase, L';', L','); //harmonize , and ; replace(tmp, L'-', L""); //there is no negative shift => treat as positive! @@ -35,7 +36,7 @@ std::vector<unsigned int> zen::fromTimeShiftPhrase(const std::wstring& timeShift } -std::wstring zen::toTimeShiftPhrase(const std::vector<unsigned int>& ignoreTimeShiftMinutes) +std::wstring fff::toTimeShiftPhrase(const std::vector<unsigned int>& ignoreTimeShiftMinutes) { std::wstring phrase; for (auto it = ignoreTimeShiftMinutes.begin(); it != ignoreTimeShiftMinutes.end(); ++it) @@ -51,7 +52,7 @@ std::wstring zen::toTimeShiftPhrase(const std::vector<unsigned int>& ignoreTimeS } -std::wstring zen::getVariantName(CompareVariant var) +std::wstring fff::getVariantName(CompareVariant var) { switch (var) { @@ -67,40 +68,56 @@ std::wstring zen::getVariantName(CompareVariant var) } -std::wstring zen::getVariantName(DirectionConfig::Variant var) +namespace { - const wchar_t arrowLeft [] = L"<-"; - const wchar_t arrowRight[] = L"->"; - const wchar_t angleRight[] = L">"; -#if 0 - //const wchar_t arrowLeft [] = L"\u2190"; unicode arrows -> too small - //const wchar_t arrowRight[] = L"\u2192"; - 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"; - => drawbacks: - - not drawn correctly before Vista - - used in sync log files where users expect ANSI: https://www.freefilesync.org/forum/viewtopic.php?t=4647 - - RTL: the full width less-than does not swap automatically -#endif - - switch (var) +std::wstring getVariantNameImpl(DirectionConfig::Variant var, const wchar_t* arrowLeft, const wchar_t* arrowRight, const wchar_t* angleRight) +{ + switch (var) { case DirectionConfig::TWO_WAY: - return std::wstring(arrowLeft) + L" " + _("Two way") + L" " + arrowRight; + return arrowLeft + _("Two way") + arrowRight; case DirectionConfig::MIRROR: - return _("Mirror") + L" " + arrowRight; + return _("Mirror") + arrowRight; case DirectionConfig::UPDATE: - return _("Update") + L" " + angleRight; + return _("Update") + angleRight; case DirectionConfig::CUSTOM: return _("Custom"); } assert(false); return _("Error"); } +} -DirectionSet zen::extractDirections(const DirectionConfig& cfg) +std::wstring fff::getVariantName(DirectionConfig::Variant var) +{ +#if 1 + const wchar_t arrowLeft [] = L"<\u2013 "; + const wchar_t arrowRight[] = L" \u2013>"; + const wchar_t angleRight[] = L" >"; +#else + //const wchar_t arrowLeft [] = L"\u2190 "; //unicode arrows -> too small + //const wchar_t arrowRight[] = L" \u2192"; // + //const wchar_t arrowLeft [] = L"\u25C4\u2013 "; //black triangle pointer + //const wchar_t arrowRight[] = L" \u2013\u25BA"; // + 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"; + //=> drawback: - not drawn correctly before Vista + // - RTL: the full width less-than is not mirrored automatically (=> Windows Unicode bug!?) +#endif + return getVariantNameImpl(var, arrowLeft, arrowRight, angleRight); +} + + +//use in sync log files where users expect ANSI: https://www.freefilesync.org/forum/viewtopic.php?t=4647 +std::wstring fff::getVariantNameForLog(DirectionConfig::Variant var) +{ + return getVariantNameImpl(var, L"<-", L"->", L">"); +} + + +DirectionSet fff::extractDirections(const DirectionConfig& cfg) { DirectionSet output; switch (cfg.var) @@ -134,12 +151,12 @@ DirectionSet zen::extractDirections(const DirectionConfig& cfg) } -bool zen::detectMovedFilesSelectable(const DirectionConfig& cfg) +bool fff::detectMovedFilesSelectable(const DirectionConfig& cfg) { if (cfg.var == DirectionConfig::TWO_WAY) return false; //moved files are always detected since we have the database file anyway - const DirectionSet tmp = zen::extractDirections(cfg); + const DirectionSet tmp = fff::extractDirections(cfg); return (tmp.exLeftSideOnly == SyncDirection::RIGHT && tmp.exRightSideOnly == SyncDirection::RIGHT) || (tmp.exLeftSideOnly == SyncDirection::LEFT && @@ -147,13 +164,13 @@ bool zen::detectMovedFilesSelectable(const DirectionConfig& cfg) } -bool zen::detectMovedFilesEnabled(const DirectionConfig& cfg) +bool fff::detectMovedFilesEnabled(const DirectionConfig& cfg) { return detectMovedFilesSelectable(cfg) ? cfg.detectMovedFiles : cfg.var == DirectionConfig::TWO_WAY; } -DirectionSet zen::getTwoWayUpdateSet() +DirectionSet fff::getTwoWayUpdateSet() { DirectionSet output; output.exLeftSideOnly = SyncDirection::RIGHT; @@ -208,7 +225,7 @@ std::wstring MainConfiguration::getSyncVariantName() const } -std::wstring zen::getSymbol(CompareFilesResult cmpRes) +std::wstring fff::getSymbol(CompareFilesResult cmpRes) { switch (cmpRes) { @@ -233,7 +250,7 @@ std::wstring zen::getSymbol(CompareFilesResult cmpRes) } -std::wstring zen::getSymbol(SyncOperation op) +std::wstring fff::getSymbol(SyncOperation op) { switch (op) { @@ -295,9 +312,14 @@ int daysSinceBeginOfWeek(int dayOfWeek) //0-6, 0=Monday, 6=Sunday */ -int64_t resolve(size_t value, UnitTime unit, int64_t defaultVal) +time_t resolve(size_t value, UnitTime unit, time_t defaultVal) { - TimeComp locTimeStruc = zen::getLocalTime(); + TimeComp tcLocal = getLocalTime(); + if (tcLocal == TimeComp()) + { + assert(false); + return defaultVal; + } switch (unit) { @@ -305,10 +327,10 @@ int64_t resolve(size_t value, UnitTime unit, int64_t defaultVal) return defaultVal; case UnitTime::TODAY: - locTimeStruc.second = 0; //0-61 - locTimeStruc.minute = 0; //0-59 - locTimeStruc.hour = 0; //0-23 - return localToTimeT(locTimeStruc); //convert local time back to UTC + tcLocal.second = 0; //0-61 + tcLocal.minute = 0; //0-59 + tcLocal.hour = 0; //0-23 + return localToTimeT(tcLocal); //convert local time back to UTC //case UnitTime::THIS_WEEK: //{ @@ -324,29 +346,29 @@ int64_t resolve(size_t value, UnitTime unit, int64_t defaultVal) //} case UnitTime::THIS_MONTH: - locTimeStruc.second = 0; //0-61 - locTimeStruc.minute = 0; //0-59 - locTimeStruc.hour = 0; //0-23 - locTimeStruc.day = 1; //1-31 - return localToTimeT(locTimeStruc); + tcLocal.second = 0; //0-61 + tcLocal.minute = 0; //0-59 + tcLocal.hour = 0; //0-23 + tcLocal.day = 1; //1-31 + return localToTimeT(tcLocal); case UnitTime::THIS_YEAR: - locTimeStruc.second = 0; //0-61 - locTimeStruc.minute = 0; //0-59 - locTimeStruc.hour = 0; //0-23 - locTimeStruc.day = 1; //1-31 - locTimeStruc.month = 1; //1-12 - return localToTimeT(locTimeStruc); + tcLocal.second = 0; //0-61 + tcLocal.minute = 0; //0-59 + tcLocal.hour = 0; //0-23 + tcLocal.day = 1; //1-31 + tcLocal.month = 1; //1-12 + return localToTimeT(tcLocal); case UnitTime::LAST_X_DAYS: - locTimeStruc.second = 0; //0-61 - locTimeStruc.minute = 0; //0-59 - locTimeStruc.hour = 0; //0-23 - return localToTimeT(locTimeStruc) - static_cast<int64_t>(value) * 24 * 3600; + tcLocal.second = 0; //0-61 + tcLocal.minute = 0; //0-59 + tcLocal.hour = 0; //0-23 + return localToTimeT(tcLocal) - value * 24 * 3600; } assert(false); - return localToTimeT(locTimeStruc); + return localToTimeT(tcLocal); } @@ -372,14 +394,14 @@ uint64_t resolve(size_t value, UnitSize unit, uint64_t defaultVal) } } -void zen::resolveUnits(size_t timeSpan, UnitTime unitTimeSpan, +void fff::resolveUnits(size_t timeSpan, UnitTime unitTimeSpan, size_t sizeMin, UnitSize unitSizeMin, size_t sizeMax, UnitSize unitSizeMax, - int64_t& timeFrom, //unit: UTC time, seconds + time_t& timeFrom, //unit: UTC time, seconds uint64_t& sizeMinBy, //unit: bytes uint64_t& sizeMaxBy) //unit: bytes { - timeFrom = resolve(timeSpan, unitTimeSpan, std::numeric_limits<int64_t>::min()); + timeFrom = resolve(timeSpan, unitTimeSpan, std::numeric_limits<time_t>::min()); sizeMinBy = resolve(sizeMin, unitSizeMin, 0U); sizeMaxBy = resolve(sizeMax, unitSizeMax, std::numeric_limits<uint64_t>::max()); } @@ -401,7 +423,7 @@ FilterConfig mergeFilterConfig(const FilterConfig& global, const FilterConfig& l trim(out.excludeFilter, true, false); //soft filter - int64_t loctimeFrom = 0; + time_t loctimeFrom = 0; uint64_t locSizeMinBy = 0; uint64_t locSizeMaxBy = 0; resolveUnits(out.timeSpan, out.unitTimeSpan, @@ -412,7 +434,7 @@ FilterConfig mergeFilterConfig(const FilterConfig& global, const FilterConfig& l locSizeMaxBy); //unit: bytes //soft filter - int64_t glotimeFrom = 0; + time_t glotimeFrom = 0; uint64_t gloSizeMinBy = 0; uint64_t gloSizeMaxBy = 0; resolveUnits(global.timeSpan, global.unitTimeSpan, @@ -450,7 +472,7 @@ bool effectivelyEmpty(const FolderPairEnh& fp) } -MainConfiguration zen::merge(const std::vector<MainConfiguration>& mainCfgs) +MainConfiguration fff::merge(const std::vector<MainConfiguration>& mainCfgs) { assert(!mainCfgs.empty()); if (mainCfgs.empty()) diff --git a/FreeFileSync/Source/structures.h b/FreeFileSync/Source/structures.h index 1395ca6e..4f9b6a65 100755 --- a/FreeFileSync/Source/structures.h +++ b/FreeFileSync/Source/structures.h @@ -11,7 +11,8 @@ #include <memory> #include <zen/zstring.h> -namespace zen + +namespace fff { enum class CompareVariant { @@ -145,7 +146,8 @@ bool detectMovedFilesEnabled (const DirectionConfig& cfg); DirectionSet extractDirections(const DirectionConfig& cfg); //get sync directions: DON'T call for DirectionConfig::TWO_WAY! -std::wstring getVariantName(DirectionConfig::Variant var); +std::wstring getVariantName (DirectionConfig::Variant var); +std::wstring getVariantNameForLog(DirectionConfig::Variant var); inline bool operator==(const DirectionConfig& lhs, const DirectionConfig& rhs) @@ -179,6 +181,7 @@ bool operator==(const CompConfig& lhs, const CompConfig& rhs) lhs.handleSymlinks == rhs.handleSymlinks && lhs.ignoreTimeShiftMinutes == rhs.ignoreTimeShiftMinutes; } +inline bool operator!=(const CompConfig& lhs, const CompConfig& rhs) { return !(lhs == rhs); } inline bool effectivelyEqual(const CompConfig& lhs, const CompConfig& rhs) { return lhs == rhs; } //no change in behavior @@ -223,6 +226,7 @@ bool operator==(const SyncConfig& lhs, const SyncConfig& rhs) lhs.versioningFolderPhrase == rhs.versioningFolderPhrase; //adapt effectivelyEqual() on changes, too! } +inline bool operator!=(const SyncConfig& lhs, const SyncConfig& rhs) { return !(lhs == rhs); } inline @@ -310,11 +314,12 @@ bool operator==(const FilterConfig& lhs, const FilterConfig& rhs) lhs.sizeMax == rhs.sizeMax && lhs.unitSizeMax == rhs.unitSizeMax; } +inline bool operator!=(const FilterConfig& lhs, const FilterConfig& rhs) { return !(lhs == rhs); } void resolveUnits(size_t timeSpan, UnitTime unitTimeSpan, size_t sizeMin, UnitSize unitSizeMin, size_t sizeMax, UnitSize unitSizeMax, - int64_t& timeFrom, //unit: UTC time, seconds + time_t& timeFrom, //unit: UTC time, seconds uint64_t& sizeMinBy, //unit: bytes uint64_t& sizeMaxBy); //unit: bytes diff --git a/FreeFileSync/Source/synchronization.cpp b/FreeFileSync/Source/synchronization.cpp index 5dae742e..b7a51fe5 100755 --- a/FreeFileSync/Source/synchronization.cpp +++ b/FreeFileSync/Source/synchronization.cpp @@ -23,6 +23,7 @@ #include <fcntl.h> //open using namespace zen; +using namespace fff; namespace @@ -244,7 +245,7 @@ void SyncStatistics::processFolder(const FolderPair& folder) //----------------------------------------------------------------------------------------------------------- -std::vector<zen::FolderPairSyncCfg> zen::extractSyncCfg(const MainConfiguration& mainCfg) +std::vector<FolderPairSyncCfg> fff::extractSyncCfg(const MainConfiguration& mainCfg) { //merge first and additional pairs std::vector<FolderPairEnh> allPairs = { mainCfg.firstPair }; @@ -336,10 +337,10 @@ public: //always (try to) clean up, even if synchronization is aborted! try { - tryCleanup(false); //throw FileError, (throw X) + tryCleanup(false /*allowCallbackException*/); //throw FileError, (throw X) } catch (FileError&) {} - catch (...) { assert(false); } //what is this? + catch (...) { assert(false); } //what is this? /* may block heavily, but still do not allow user callback: -> avoid throwing user cancel exception again, leading to incomplete clean-up! @@ -347,7 +348,7 @@ public: } //clean-up temporary directory (recycle bin optimization) - void tryCleanup(bool allowUserCallback); //throw FileError; throw X -> call this in non-exceptional coding, i.e. somewhere after sync! + void tryCleanup(bool allowCallbackException); //throw FileError; throw X -> call this in non-exceptional coding, i.e. somewhere after sync! template <class Function> void removeFileWithCallback (const FileDescriptor& fileDescr, const Zstring& relativePath, Function onNotifyItemDeletion, const IOCallback& notifyUnbufferedIO); // template <class Function> void removeDirWithCallback (const AbstractPath& dirPath, const Zstring& relativePath, Function onNotifyItemDeletion, const IOCallback& notifyUnbufferedIO); //throw FileError @@ -438,7 +439,7 @@ DeletionHandling::DeletionHandling(const AbstractPath& baseFolderPath, } -void DeletionHandling::tryCleanup(bool allowUserCallback) //throw FileError; throw X +void DeletionHandling::tryCleanup(bool allowCallbackException) //throw FileError; throw X { switch (deletionPolicy_) { @@ -450,17 +451,22 @@ void DeletionHandling::tryCleanup(bool allowUserCallback) //throw FileError; thr { auto notifyDeletionStatus = [&](const std::wstring& displayPath) { - if (!displayPath.empty()) - procCallback_.reportStatus(replaceCpy(txtRemovingFile_, L"%x", fmtPath(displayPath))); //throw ? - else - procCallback_.requestUiRefresh(); //throw ? + try + { + if (!displayPath.empty()) + procCallback_.reportStatus(replaceCpy(txtRemovingFile_, L"%x", fmtPath(displayPath))); //throw X + else + procCallback_.requestUiRefresh(); //throw X + } + catch (...) + { + if (allowCallbackException) + throw; + } }; //move content of temporary directory to recycle bin in a single call - if (allowUserCallback) - getOrCreateRecyclerSession().tryCleanup(notifyDeletionStatus); //throw FileError - else - getOrCreateRecyclerSession().tryCleanup(nullptr); //throw FileError + getOrCreateRecyclerSession().tryCleanup(notifyDeletionStatus); //throw FileError } break; @@ -591,7 +597,7 @@ public: { MinimumDiskSpaceNeeded inst; inst.recurse(baseFolder); - return std::make_pair(inst.spaceNeededLeft_, inst.spaceNeededRight_); + return { inst.spaceNeededLeft_, inst.spaceNeededRight_ }; } private: @@ -838,7 +844,7 @@ void SynchronizeFolderPair::prepare2StepMove(FilePair& sourceObj, tempFile .setMoveRef(targetObj.getId()); //NO statistics update! - procCallback_.requestUiRefresh(); //may throw + procCallback_.requestUiRefresh(); //throw ? } @@ -955,8 +961,7 @@ void SynchronizeFolderPair::runZeroPass(ContainerObject& hierObj) { SyncStatistics statSrc(*sourceObj); SyncStatistics statTrg(*targetObj); - return std::make_pair(getCUD(statSrc) + getCUD(statTrg), - statSrc.getBytesToProcess() + statTrg.getBytesToProcess()); + return { getCUD(statSrc) + getCUD(statTrg), statSrc.getBytesToProcess() + statTrg.getBytesToProcess() }; }; const auto statBefore = getStats(); @@ -1346,7 +1351,7 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn return; //no update on processed data! } - procCallback_.requestUiRefresh(); //may throw + procCallback_.requestUiRefresh(); //throw ? } @@ -1485,7 +1490,7 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& symlink, SyncOperati return; //no update on processed data! } - procCallback_.requestUiRefresh(); //may throw + procCallback_.requestUiRefresh(); //throw ? } @@ -1613,7 +1618,7 @@ void SynchronizeFolderPair::synchronizeFolderInt(FolderPair& folder, SyncOperati return; //no update on processed data! } - procCallback_.requestUiRefresh(); //may throw + procCallback_.requestUiRefresh(); //throw ? } //########################################################################################### @@ -1760,7 +1765,7 @@ enum class FolderPairJobType } -void zen::synchronize(const std::chrono::system_clock::time_point& syncStartTime, +void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime, bool verifyCopiedFiles, bool copyLockedFiles, bool copyFilePermissions, @@ -1769,7 +1774,7 @@ void zen::synchronize(const std::chrono::system_clock::time_point& syncStartTime int folderAccessTimeout, const std::vector<FolderPairSyncCfg>& syncConfig, FolderComparison& folderCmp, - xmlAccess::OptionalDialogs& warnings, + WarningDialogs& warnings, ProcessCallback& callback) { //PERF_START; @@ -1960,7 +1965,7 @@ void zen::synchronize(const std::chrono::system_clock::time_point& syncStartTime if (0 < freeSpace && //zero means "request not supported" (e.g. see WebDav) freeSpace < minSpaceNeeded) - diskSpaceMissing.emplace_back(baseFolderPath, std::make_pair(minSpaceNeeded, freeSpace)); + diskSpaceMissing.push_back({ baseFolderPath, { minSpaceNeeded, freeSpace } }); } catch (FileError&) {} //for warning only => no need for tryReportingError() }; @@ -2104,6 +2109,10 @@ void zen::synchronize(const std::chrono::system_clock::time_point& syncStartTime try { + const TimeComp timeStamp = getLocalTime(std::chrono::system_clock::to_time_t(syncStartTime)); + if (timeStamp == TimeComp()) + throw std::runtime_error("Failed to determine current time: " + numberTo<std::string>(syncStartTime.time_since_epoch().count())); + //loop through all directory pairs for (auto itBase = begin(folderCmp); itBase != end(folderCmp); ++itBase) { @@ -2116,7 +2125,7 @@ void zen::synchronize(const std::chrono::system_clock::time_point& syncStartTime continue; //------------------------------------------------------------------------------------------ - callback.reportInfo(_("Synchronizing folder pair:") + L" [" + getVariantName(folderPairCfg.syncVariant_) + L"]\n" + + callback.reportInfo(_("Synchronizing folder pair:") + L" " + getVariantNameForLog(folderPairCfg.syncVariant_) + L"\n" + L" " + AFS::getDisplayPath(baseFolder.getAbstractPath< LEFT_SIDE>()) + L"\n" + L" " + AFS::getDisplayPath(baseFolder.getAbstractPath<RIGHT_SIDE>())); //------------------------------------------------------------------------------------------ @@ -2136,15 +2145,16 @@ void zen::synchronize(const std::chrono::system_clock::time_point& syncStartTime //execute synchronization recursively //update synchronization database in case of errors: - ZEN_ON_SCOPE_FAIL - ( - try + auto guardDbSave = makeGuard<ScopeGuardRunMode::ON_FAIL>([&] { - if (folderPairCfg.saveSyncDB_) - zen::saveLastSynchronousState(baseFolder, nullptr); - } //throw FileError - catch (FileError&) {} - ); + try + { + if (folderPairCfg.saveSyncDB_) + saveLastSynchronousState(baseFolder, //throw FileError + [&](const std::wstring& statusMsg) { try { callback.reportStatus(statusMsg); /*throw X*/} catch (...) {}}); + } + catch (FileError&) {} + }); if (jobType[folderIndex] == FolderPairJobType::PROCESS) { @@ -2174,8 +2184,6 @@ void zen::synchronize(const std::chrono::system_clock::time_point& syncStartTime return folderPairCfg.handleDeletion; }; - const TimeComp timeStamp = getLocalTime(std::chrono::system_clock::to_time_t(syncStartTime)); - DeletionHandling delHandlerL(baseFolder.getAbstractPath<LEFT_SIDE>(), getEffectiveDeletionPolicy(baseFolder.getAbstractPath<LEFT_SIDE>()), folderPairCfg.versioningFolderPhrase, @@ -2197,21 +2205,23 @@ void zen::synchronize(const std::chrono::system_clock::time_point& syncStartTime syncFP.startSync(baseFolder); //(try to gracefully) cleanup temporary Recycle bin folders and versioning -> will be done in ~DeletionHandling anyway... - tryReportingError([&] { delHandlerL.tryCleanup(true /*allowUserCallback*/); /*throw FileError*/}, callback); //throw X? - tryReportingError([&] { delHandlerR.tryCleanup(true ); /*throw FileError*/}, callback); //throw X? + tryReportingError([&] { delHandlerL.tryCleanup(true /*allowCallbackException*/); /*throw FileError*/}, callback); //throw X? + tryReportingError([&] { delHandlerR.tryCleanup(true ); /*throw FileError*/}, callback); //throw X? } //(try to gracefully) write database file if (folderPairCfg.saveSyncDB_) { callback.reportStatus(_("Generating database...")); - callback.forceUiRefresh(); + callback.forceUiRefresh(); //throw X tryReportingError([&] { - zen::saveLastSynchronousState(baseFolder, //throw FileError + saveLastSynchronousState(baseFolder, //throw FileError [&](const std::wstring& statusMsg) { callback.reportStatus(statusMsg); /*throw X*/}); }, callback); //throw X + + guardDbSave.dismiss(); //[!] after "graceful" try: user might have cancelled during DB write: ensure DB is still written } } diff --git a/FreeFileSync/Source/synchronization.h b/FreeFileSync/Source/synchronization.h index 44a58114..076f0350 100755 --- a/FreeFileSync/Source/synchronization.h +++ b/FreeFileSync/Source/synchronization.h @@ -7,14 +7,13 @@ #ifndef SYNCHRONIZATION_H_8913470815943295 #define SYNCHRONIZATION_H_8913470815943295 -//#include <zen/time.h> #include <chrono> #include "file_hierarchy.h" #include "lib/process_xml.h" #include "process_callback.h" -namespace zen +namespace fff { class SyncStatistics //this class counts *logical* operations, (create, update, delete + bytes), *not* disk accesses! { @@ -104,7 +103,7 @@ void synchronize(const std::chrono::system_clock::time_point& syncStartTime, int folderAccessTimeout, const std::vector<FolderPairSyncCfg>& syncConfig, //CONTRACT: syncConfig and folderCmp correspond row-wise! FolderComparison& folderCmp, // - xmlAccess::OptionalDialogs& warnings, + WarningDialogs& warnings, ProcessCallback& callback); } diff --git a/FreeFileSync/Source/ui/app_icon.h b/FreeFileSync/Source/ui/app_icon.h index 265fdf38..68fa910b 100755 --- a/FreeFileSync/Source/ui/app_icon.h +++ b/FreeFileSync/Source/ui/app_icon.h @@ -11,11 +11,12 @@ #include <wx+/image_resources.h> -namespace zen +namespace fff { inline wxIcon getFfsIcon() { + using namespace zen; //wxWidgets' bitmap to icon conversion on OS X can only deal with very specific sizes => check on all platforms! assert(getResourceImage(L"FreeFileSync").GetWidth () == getResourceImage(L"FreeFileSync").GetHeight() && getResourceImage(L"FreeFileSync").GetWidth() % 128 == 0); diff --git a/FreeFileSync/Source/ui/batch_config.cpp b/FreeFileSync/Source/ui/batch_config.cpp index 814d23c8..0e4ff689 100755 --- a/FreeFileSync/Source/ui/batch_config.cpp +++ b/FreeFileSync/Source/ui/batch_config.cpp @@ -14,10 +14,11 @@ #include "gui_generated.h" #include "folder_selector.h" #include "../lib/help_provider.h" +#include "../lib/generate_logfile.h" using namespace zen; -using namespace xmlAccess; +using namespace fff; namespace @@ -40,7 +41,11 @@ private: void OnSaveBatchJob(wxCommandEvent& event) override; void OnToggleIgnoreErrors(wxCommandEvent& event) override { updateGui(); } - void OnToggleRunMinimized(wxCommandEvent& event) override { updateGui(); } + void OnToggleRunMinimized(wxCommandEvent& event) override + { + m_checkBoxAutoClose->SetValue(m_checkBoxRunMinimized->GetValue()); //usually user wants to change both + updateGui(); + } void OnHelpScheduleBatch(wxHyperlinkEvent& event) override { displayHelpEntry(L"schedule-a-batch-job", this); } @@ -76,11 +81,12 @@ BatchDialog::BatchDialog(wxWindow* parent, BatchDialogConfig& dlgCfg) : logfileDir_ = std::make_unique<FolderSelector>(*m_panelLogfile, *m_buttonSelectLogFolder, *m_bpButtonSelectAltLogFolder, *m_logFolderPath, nullptr /*staticText*/, nullptr /*wxWindow*/); + logfileDir_->setBackgroundText(utfTo<std::wstring>(getDefaultLogFolderPath())); + enumPostSyncAction_. - add(PostSyncAction::SUMMARY, _("Show summary")). - add(PostSyncAction::EXIT, replaceCpy(_("E&xit"), L"&", L"")). //reuse translation - add(PostSyncAction::SLEEP, _("Sleep")). - add(PostSyncAction::SHUTDOWN, _("Shut down")); + add(PostSyncAction::NONE, L""). + add(PostSyncAction::SLEEP, _("System: Sleep")). + add(PostSyncAction::SHUTDOWN, _("System: Shut down")); setConfig(dlgCfg); @@ -135,6 +141,7 @@ void BatchDialog::setConfig(const BatchDialogConfig& dlgCfg) } m_checkBoxRunMinimized->SetValue(dlgCfg.batchExCfg.runMinimized); + m_checkBoxAutoClose ->SetValue(dlgCfg.batchExCfg.autoCloseSummary); setEnumVal(enumPostSyncAction_, *m_choicePostSyncAction, dlgCfg.batchExCfg.postSyncAction); logfileDir_->setPath(dlgCfg.batchExCfg.logFolderPathPhrase); @@ -156,6 +163,7 @@ BatchDialogConfig BatchDialog::getConfig() const dlgCfg.batchExCfg.batchErrorDialog = m_radioBtnErrorDialogCancel->GetValue() ? BatchErrorDialog::CANCEL : BatchErrorDialog::SHOW; dlgCfg.batchExCfg.runMinimized = m_checkBoxRunMinimized->GetValue(); + dlgCfg.batchExCfg.autoCloseSummary = m_checkBoxAutoClose ->GetValue(); dlgCfg.batchExCfg.postSyncAction = getEnumVal(enumPostSyncAction_, *m_choicePostSyncAction); dlgCfg.batchExCfg.logFolderPathPhrase = utfTo<Zstring>(logfileDir_->getPath()); @@ -180,7 +188,7 @@ void BatchDialog::OnSaveBatchJob(wxCommandEvent& event) } -ReturnBatchConfig::ButtonPressed zen::showBatchConfigDialog(wxWindow* parent, +ReturnBatchConfig::ButtonPressed fff::showBatchConfigDialog(wxWindow* parent, BatchExclusiveConfig& batchExCfg, bool& ignoreErrors) { diff --git a/FreeFileSync/Source/ui/batch_config.h b/FreeFileSync/Source/ui/batch_config.h index e79d2f25..7dc68db5 100755 --- a/FreeFileSync/Source/ui/batch_config.h +++ b/FreeFileSync/Source/ui/batch_config.h @@ -11,7 +11,7 @@ #include "../lib/process_xml.h" -namespace zen +namespace fff { struct ReturnBatchConfig { @@ -25,8 +25,7 @@ struct ReturnBatchConfig //show and let user customize batch settings (without saving) ReturnBatchConfig::ButtonPressed showBatchConfigDialog(wxWindow* parent, - - xmlAccess::BatchExclusiveConfig& batchExCfg, + BatchExclusiveConfig& batchExCfg, bool& ignoreErrors); } diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp index e3e59690..be007e09 100755 --- a/FreeFileSync/Source/ui/batch_status_handler.cpp +++ b/FreeFileSync/Source/ui/batch_status_handler.cpp @@ -17,7 +17,7 @@ #include "../fs/concrete.h" using namespace zen; -using namespace xmlAccess; +using namespace fff; namespace @@ -25,12 +25,13 @@ namespace //"Backup FreeFileSync 2013-09-15 015052.123.log" -> //"Backup FreeFileSync 2013-09-15 015052.123 [Error].log" + //return value always bound! std::unique_ptr<AFS::OutputStream> prepareNewLogfile(const AbstractPath& logFolderPath, //throw FileError const std::wstring& jobName, - const std::chrono::system_clock::time_point& batchStartTime, + const std::chrono::system_clock::time_point& syncStartTime, const std::wstring& failStatus, - ProcessCallback& pc) + const std::function<void(const std::wstring& msg)>& notifyStatus) { assert(!jobName.empty()); @@ -41,11 +42,14 @@ std::unique_ptr<AFS::OutputStream> prepareNewLogfile(const AbstractPath& logFold //=> too many issues, most notably cmd.exe is not Unicode-aware: https://www.freefilesync.org/forum/viewtopic.php?t=1679 //assemble logfile name - const TimeComp timeStamp = getLocalTime(std::chrono::system_clock::to_time_t(batchStartTime)); - const auto timeMs = std::chrono::duration_cast<std::chrono::milliseconds>(batchStartTime.time_since_epoch()).count() % 1000; + const TimeComp tc = getLocalTime(std::chrono::system_clock::to_time_t(syncStartTime)); + if (tc == TimeComp()) + throw FileError(L"Failed to determine current time: " + numberTo<std::wstring>(syncStartTime.time_since_epoch().count())); + + const auto timeMs = std::chrono::duration_cast<std::chrono::milliseconds>(syncStartTime.time_since_epoch()).count() % 1000; Zstring logFileName = utfTo<Zstring>(jobName) + - Zstr(" ") + formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), timeStamp) + + Zstr(" ") + formatTime<Zstring>(Zstr("%Y-%m-%d %H%M%S"), tc) + Zstr(".") + printNumber<Zstring>(Zstr("%03d"), static_cast<int>(timeMs)); //[ms] should yield a fairly unique name if (!failStatus.empty()) logFileName += utfTo<Zstring>(L" [" + failStatus + L"]"); @@ -53,9 +57,16 @@ std::unique_ptr<AFS::OutputStream> prepareNewLogfile(const AbstractPath& logFold const AbstractPath logFilePath = AFS::appendRelPath(logFolderPath, logFileName); - return AFS::getOutputStream(logFilePath, //throw FileError - nullptr, /*streamSize*/ - OnUpdateLogfileStatusNoThrow(pc, AFS::getDisplayPath(logFilePath))); + auto notifyUnbufferedIO = [notifyStatus, + bytesWritten_ = int64_t(0), + msg_ = replaceCpy(_("Saving file %x..."), L"%x", fmtPath(AFS::getDisplayPath(logFilePath)))] + (int64_t bytesDelta) mutable + { + if (notifyStatus) + notifyStatus(msg_ + L" (" + formatFilesizeShort(bytesWritten_ += bytesDelta) + L")"); /*throw X*/ + }; + + return AFS::getOutputStream(logFilePath, nullptr, /*streamSize*/ notifyUnbufferedIO); //throw FileError } @@ -95,11 +106,13 @@ private: }; -void limitLogfileCount(const AbstractPath& logFolderPath, const std::wstring& jobname, size_t maxCount, const std::function<void()>& onUpdateStatus) //throw FileError +void limitLogfileCount(const AbstractPath& logFolderPath, const std::wstring& jobname, size_t maxCount, //throw FileError + const std::function<void(const std::wstring& msg)>& notifyStatus) { const Zstring prefix = utfTo<Zstring>(jobname); + const std::wstring cleaningMsg = _("Cleaning up old log files...");; - LogTraverserCallback lt(prefix, onUpdateStatus); //traverse source directory one level deep + LogTraverserCallback lt(prefix, [&] { if (notifyStatus) notifyStatus(cleaningMsg); }); //traverse source directory one level deep AFS::traverseFolder(logFolderPath, lt); std::vector<Zstring> logFileNames = lt.refFileNames(); @@ -112,13 +125,14 @@ void limitLogfileCount(const AbstractPath& logFolderPath, const std::wstring& jo std::for_each(logFileNames.begin(), logFileNames.end() - maxCount, [&](const Zstring& logFileName) { + const AbstractPath filePath = AFS::appendRelPath(logFolderPath, logFileName); + if (notifyStatus) notifyStatus(cleaningMsg + L" " + fmtPath(AFS::getDisplayPath(filePath))); + try { - AFS::removeFilePlain(AFS::appendRelPath(logFolderPath, logFileName)); //throw FileError + AFS::removeFilePlain(filePath); //throw FileError } catch (const FileError& e) { if (!lastError) lastError = e; }; - - if (onUpdateStatus) onUpdateStatus(); }); } @@ -130,9 +144,10 @@ void limitLogfileCount(const AbstractPath& logFolderPath, const std::wstring& jo //############################################################################################################################## BatchStatusHandler::BatchStatusHandler(bool showProgress, + bool autoCloseDialog, const std::wstring& jobName, const Zstring& soundFileSyncComplete, - const std::chrono::system_clock::time_point& batchStartTime, + const std::chrono::system_clock::time_point& startTime, const Zstring& logFolderPathPhrase, //may be empty int logfilesCountLimit, size_t lastSyncsLogFileSizeMax, @@ -150,19 +165,26 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress, returnCode_(returnCode), automaticRetryCount_(automaticRetryCount), automaticRetryDelay_(automaticRetryDelay), - progressDlg_(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, -*this, -nullptr, //parentWindow -showProgress, -jobName, -soundFileSyncComplete, -ignoreErrors, -postSyncAction)), - jobName_(jobName), - batchStartTime_(batchStartTime), - logFolderPathPhrase_(logFolderPathPhrase), - postSyncCommand_(postSyncCommand), - postSyncCondition_(postSyncCondition) + progressDlg_(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, nullptr /*parentWindow*/, showProgress, autoCloseDialog, +jobName, soundFileSyncComplete, ignoreErrors, [&] +{ + switch (postSyncAction) + { + case PostSyncAction::NONE: + return PostSyncAction2::NONE; + case PostSyncAction::SLEEP: + return PostSyncAction2::SLEEP; + case PostSyncAction::SHUTDOWN: + return PostSyncAction2::SHUTDOWN; + } + assert(false); + return PostSyncAction2::NONE; +}())), +jobName_(jobName), + startTime_(startTime), + logFolderPathPhrase_(logFolderPathPhrase), + postSyncCommand_(postSyncCommand), + postSyncCondition_(postSyncCondition) { //ATTENTION: "progressDlg_" is an unmanaged resource!!! However, at this point we already consider construction complete! => //ZEN_ON_SCOPE_FAIL( cleanup(); ); //destructor call would lead to member double clean-up!!! @@ -176,8 +198,8 @@ postSyncAction)), BatchStatusHandler::~BatchStatusHandler() { - const int totalErrors = errorLog_.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log - const int totalWarnings = errorLog_.getItemCount(TYPE_WARNING); + const int totalErrors = errorLog_.getItemCount(MSG_TYPE_ERROR | MSG_TYPE_FATAL_ERROR); //evaluate before finalizing log + const int totalWarnings = errorLog_.getItemCount(MSG_TYPE_WARNING); //finalize error log SyncProgressDialog::SyncResult finalStatus = SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS; @@ -187,24 +209,24 @@ BatchStatusHandler::~BatchStatusHandler() { finalStatus = SyncProgressDialog::RESULT_ABORTED; raiseReturnCode(returnCode_, FFS_RC_ABORTED); - finalStatusMsg = _("Synchronization stopped"); - errorLog_.logMsg(finalStatusMsg, TYPE_ERROR); + finalStatusMsg = _("Stopped"); + errorLog_.logMsg(finalStatusMsg, MSG_TYPE_ERROR); failStatus = _("Stopped"); } else if (totalErrors > 0) { finalStatus = SyncProgressDialog::RESULT_FINISHED_WITH_ERROR; raiseReturnCode(returnCode_, FFS_RC_FINISHED_WITH_ERRORS); - finalStatusMsg = _("Synchronization completed with errors"); - errorLog_.logMsg(finalStatusMsg, TYPE_ERROR); + finalStatusMsg = _("Completed with errors"); + errorLog_.logMsg(finalStatusMsg, MSG_TYPE_ERROR); failStatus = _("Error"); } else if (totalWarnings > 0) { finalStatus = SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS; raiseReturnCode(returnCode_, FFS_RC_FINISHED_WITH_WARNINGS); - finalStatusMsg = _("Synchronization completed with warnings"); - errorLog_.logMsg(finalStatusMsg, TYPE_WARNING); + finalStatusMsg = _("Completed with warnings"); + errorLog_.logMsg(finalStatusMsg, MSG_TYPE_WARNING); failStatus = _("Warning"); } else @@ -213,14 +235,16 @@ BatchStatusHandler::~BatchStatusHandler() getBytesTotal(PHASE_SYNCHRONIZING) == 0) finalStatusMsg = _("Nothing to synchronize"); //even if "ignored conflicts" occurred! else - finalStatusMsg = _("Synchronization completed successfully"); - errorLog_.logMsg(finalStatusMsg, TYPE_INFO); + finalStatusMsg = _("Completed"); + errorLog_.logMsg(finalStatusMsg, MSG_TYPE_INFO); } //post sync command Zstring commandLine = [&] { - if (!getAbortStatus() || *getAbortStatus() != AbortTrigger::USER) //user cancelled => don't run post sync command! + if (getAbortStatus() && *getAbortStatus() == AbortTrigger::USER) + ; //user cancelled => don't run post sync command! + else switch (postSyncCondition_) { case PostSyncCondition::COMPLETION: @@ -241,7 +265,7 @@ BatchStatusHandler::~BatchStatusHandler() trim(commandLine); if (!commandLine.empty()) - errorLog_.logMsg(replaceCpy(_("Executing command %x"), L"%x", fmtPath(commandLine)), TYPE_INFO); + errorLog_.logMsg(replaceCpy(_("Executing command %x"), L"%x", fmtPath(commandLine)), MSG_TYPE_INFO); //----------------- write results into user-specified logfile ------------------------ const SummaryInfo summary = @@ -250,110 +274,130 @@ BatchStatusHandler::~BatchStatusHandler() finalStatusMsg, getItemsCurrent(PHASE_SYNCHRONIZING), getBytesCurrent(PHASE_SYNCHRONIZING), getItemsTotal (PHASE_SYNCHRONIZING), getBytesTotal (PHASE_SYNCHRONIZING), - std::time(nullptr) - startTime_ + std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - startTime_).count() + }; + if (progressDlg_) progressDlg_->timerSetStatus(false /*active*/); //keep correct summary window stats considering count down timer, system sleep + + //do NOT use tryReportingError()! saving log files should not be cancellable! + auto notifyStatusNoThrow = [&](const std::wstring& msg) + { + try { reportStatus(msg); /*throw X*/ } + catch (...) {} }; //create not before destruction: 1. avoid issues with FFS trying to sync open log file 2. simplify transactional retry on failure 3. no need to rename log file to include status // 4. failure to write to particular stream must not be retried! if (logfilesCountLimit_ != 0) { - auto requestUiRefreshNoThrow = [&] { try { requestUiRefresh(); /*throw X*/ } catch (...) {} }; - - const AbstractPath logFolderPath = createAbstractPath(trimCpy(logFolderPathPhrase_).empty() ? getConfigDirPathPf() + Zstr("Logs") : logFolderPathPhrase_); //noexcept + const AbstractPath logFolderPath = createAbstractPath(trimCpy(logFolderPathPhrase_).empty() ? getDefaultLogFolderPath() : logFolderPathPhrase_); //noexcept try { - tryReportingError([&] //errors logged here do not impact final status calculation above! => not a problem! - { - std::unique_ptr<AFS::OutputStream> logFileStream = prepareNewLogfile(logFolderPath, jobName_, batchStartTime_, failStatus, *this); //throw FileError; return value always bound! + std::unique_ptr<AFS::OutputStream> logFileStream = prepareNewLogfile(logFolderPath, jobName_, startTime_, failStatus, notifyStatusNoThrow); //throw FileError; return value always bound! - streamToLogFile(summary, errorLog_, *logFileStream); //throw FileError, (X) - logFileStream->finalize(); //throw FileError, (X) - }, *this); //throw X! by ProcessCallback! + streamToLogFile(summary, errorLog_, *logFileStream); //throw FileError, (X) + logFileStream->finalize(); //throw FileError, (X) } - catch (...) {} + catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); } if (logfilesCountLimit_ > 0) - { - try { reportStatus(_("Cleaning up old log files...")); } - catch (...) {} - try { - tryReportingError([&] - { - limitLogfileCount(logFolderPath, jobName_, logfilesCountLimit_, requestUiRefreshNoThrow); //throw FileError - }, *this); //throw X + limitLogfileCount(logFolderPath, jobName_, logfilesCountLimit_, notifyStatusNoThrow); //throw FileError, (X) } - catch (...) {} - } + catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); } } - //write results into LastSyncs.log + try { - saveToLastSyncsLog(summary, errorLog_, lastSyncsLogFileSizeMax_, OnUpdateLogfileStatusNoThrow(*this, utfTo<std::wstring>(getLastSyncsLogfilePath()))); //throw FileError + saveToLastSyncsLog(summary, errorLog_, lastSyncsLogFileSizeMax_, notifyStatusNoThrow); //throw FileError, (X) } - catch (FileError&) { assert(false); } + catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); } //execute post sync command *after* writing log files, so that user can refer to the log via the command! if (!commandLine.empty()) try { - //use EXEC_TYPE_ASYNC until there is reason not to: https://www.freefilesync.org/forum/viewtopic.php?t=31 - tryReportingError([&] { shellExecute(expandMacros(commandLine), EXEC_TYPE_ASYNC); /*throw FileError*/ }, *this); //throw X + //use ExecutionType::ASYNC until there is reason not to: https://www.freefilesync.org/forum/viewtopic.php?t=31 + shellExecute(expandMacros(commandLine), ExecutionType::ASYNC); //throw FileError } - catch (...) {} + catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); } if (progressDlg_) { + auto mayRunAfterCountDown = [&](const std::wstring& operationName) + { + auto notifyStatusThrowOnCancel = [&](const std::wstring& msg) + { + try { reportStatus(msg); /*throw X*/ } + catch (...) + { + if (getAbortStatus() && *getAbortStatus() == AbortTrigger::USER) + throw; + } + }; + + if (progressDlg_->getWindowIfVisible()) + try + { + delayAndCountDown(operationName, 5 /*delayInSec*/, notifyStatusThrowOnCancel); //throw X + } + catch (...) { return false; } + + return true; + }; + //post sync action - bool showSummary = true; - bool triggerSleep = false; - if (!getAbortStatus() || *getAbortStatus() != AbortTrigger::USER) //user cancelled => don't run post sync action! + bool autoClose = false; + if (getAbortStatus() && *getAbortStatus() == AbortTrigger::USER) + ; //user cancelled => don't run post sync command! + else switch (progressDlg_->getOptionPostSyncAction()) { - case PostSyncAction::SUMMARY: + case PostSyncAction2::NONE: + autoClose = progressDlg_->getOptionAutoCloseDialog(); break; - case PostSyncAction::EXIT: - showSummary = false; + case PostSyncAction2::EXIT: + assert(false); break; - case PostSyncAction::SLEEP: - triggerSleep = true; + case PostSyncAction2::SLEEP: + if (mayRunAfterCountDown(_("System: Sleep"))) + try + { + suspendSystem(); //throw FileError + autoClose = progressDlg_->getOptionAutoCloseDialog(); + } + catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); } break; - case PostSyncAction::SHUTDOWN: - showSummary = false; - try - { - tryReportingError([&] { shutdownSystem(); /*throw FileError*/ }, *this); //throw X - } - catch (...) {} + case PostSyncAction2::SHUTDOWN: + if (mayRunAfterCountDown(_("System: Shut down"))) + try + { + shutdownSystem(); //throw FileError + autoClose = true; + } + catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); } break; } if (switchToGuiRequested_) //-> avoid recursive yield() calls, thous switch not before ending batch mode - showSummary = false; + autoClose = true; //close progress dialog - if (showSummary) //warning: wxWindow::Show() is called within showSummary()! + if (autoClose) //warning: wxWindow::Show() is called within showSummary()! + progressDlg_->closeDirectly(true /*restoreParentFrame: n/a here*/); //progressDlg_ is main window => program will quit shortly after + else //notify about (logical) application main window => program won't quit, but stay on this dialog //setMainWindow(progressDlg_->getAsWindow()); -> not required anymore since we block waiting until dialog is closed below progressDlg_->showSummary(finalStatus, errorLog_); - else - progressDlg_->closeDirectly(true /*restoreParentFrame: n/a here*/); //progressDlg_ is main window => program will quit shortly after - - if (triggerSleep) //sleep *after* showing results dialog (consider total time!) - try - { - tryReportingError([&] { suspendSystem(); /*throw FileError*/ }, *this); //throw X - } - catch (...) {} //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_) + for (;;) { wxTheApp->Yield(); //*first* refresh GUI (removing flicker) before sleeping! - std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS)); + if (!progressDlg_) break; + std::this_thread::sleep_for(UI_UPDATE_INTERVAL); } } } @@ -365,7 +409,7 @@ void BatchStatusHandler::initNewPhase(int itemsTotal, int64_t bytesTotal, Proces 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 + forceUiRefresh(); //throw X; OS X needs a full yield to update GUI and get rid of "dummy" texts } @@ -381,7 +425,7 @@ void BatchStatusHandler::updateProcessedData(int itemsDelta, int64_t bytesDelta) void BatchStatusHandler::reportInfo(const std::wstring& text) { - errorLog_.logMsg(text, TYPE_INFO); //log first! + errorLog_.logMsg(text, MSG_TYPE_INFO); //log first! StatusHandler::reportInfo(text); //throw X } @@ -390,7 +434,7 @@ void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool& { if (!progressDlg_) abortProcessNow(); - errorLog_.logMsg(warningMessage, TYPE_WARNING); + errorLog_.logMsg(warningMessage, MSG_TYPE_WARNING); if (!warningActive) return; @@ -401,7 +445,7 @@ void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool& case BatchErrorDialog::SHOW: { PauseTimers dummy(*progressDlg_); - forceUiRefresh(); + forceUiRefreshNoThrow(); //noexcept! => don't throw here when error occurs during clean up! bool dontWarnAgain = false; switch (showQuestionDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::WARNING, PopupDialogCfg(). @@ -414,7 +458,7 @@ void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool& break; case QuestionButton2::NO: //switch - errorLog_.logMsg(_("Switching to FreeFileSync's main window"), TYPE_INFO); + errorLog_.logMsg(_("Switching to FreeFileSync's main window"), MSG_TYPE_INFO); switchToGuiRequested_ = true; //treat as a special kind of cancel userRequestAbort(); // throw BatchRequestSwitchToMainDialog(); @@ -440,23 +484,13 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er //auto-retry if (retryNumber < automaticRetryCount_) { - errorLog_.logMsg(errorMessage + L"\n-> " + - _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", automaticRetryDelay_), TYPE_INFO); - //delay - const int iterations = static_cast<int>(1000 * automaticRetryDelay_ / UI_UPDATE_INTERVAL_MS); //always round down: don't allow for negative remaining time below - for (int i = 0; i < iterations; ++i) - { - reportStatus(_("Error") + L": " + _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", - (1000 * automaticRetryDelay_ - i * UI_UPDATE_INTERVAL_MS + 999) / 1000)); //integer round up - std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS)); - } + errorLog_.logMsg(errorMessage + L"\n-> " + _("Automatic retry"), MSG_TYPE_INFO); + delayAndCountDown(_("Automatic retry"), automaticRetryDelay_, [&](const std::wstring& msg) { this->reportStatus(_("Error") + L": " + msg); }); return ProcessCallback::RETRY; } - //always, except for "retry": - auto guardWriteLog = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { errorLog_.logMsg(errorMessage, TYPE_ERROR); }); - + auto guardWriteLog = makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { errorLog_.logMsg(errorMessage, MSG_TYPE_ERROR); }); if (!progressDlg_->getOptionIgnoreErrors()) { @@ -465,7 +499,7 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er case BatchErrorDialog::SHOW: { PauseTimers dummy(*progressDlg_); - forceUiRefresh(); + forceUiRefreshNoThrow(); //noexcept! => don't throw here when error occurs during clean up! switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg(). setDetailInstructions(errorMessage), @@ -480,7 +514,7 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er case ConfirmationButton3::DECLINE: //retry guardWriteLog.dismiss(); - errorLog_.logMsg(errorMessage + L"\n-> " + _("Retrying operation..."), TYPE_INFO); + errorLog_.logMsg(errorMessage + L"\n-> " + _("Retrying operation..."), MSG_TYPE_INFO); return ProcessCallback::RETRY; case ConfirmationButton3::CANCEL: @@ -507,7 +541,7 @@ void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage) { if (!progressDlg_) abortProcessNow(); - errorLog_.logMsg(errorMessage, TYPE_FATAL_ERROR); + errorLog_.logMsg(errorMessage, MSG_TYPE_FATAL_ERROR); if (!progressDlg_->getOptionIgnoreErrors()) switch (batchErrorDialog_) @@ -515,7 +549,7 @@ void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage) case BatchErrorDialog::SHOW: { PauseTimers dummy(*progressDlg_); - forceUiRefresh(); + forceUiRefreshNoThrow(); //noexcept! => don't throw here when error occurs during clean up! switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg().setTitle(_("Serious Error")). @@ -543,7 +577,7 @@ void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage) } -void BatchStatusHandler::forceUiRefresh() +void BatchStatusHandler::forceUiRefreshNoThrow() { if (progressDlg_) progressDlg_->updateGui(); diff --git a/FreeFileSync/Source/ui/batch_status_handler.h b/FreeFileSync/Source/ui/batch_status_handler.h index 072d0f80..55ceccb6 100755 --- a/FreeFileSync/Source/ui/batch_status_handler.h +++ b/FreeFileSync/Source/ui/batch_status_handler.h @@ -9,41 +9,43 @@ #include <chrono> #include <zen/error_log.h> -//#include <zen/time.h> #include "progress_indicator.h" #include "../lib/status_handler.h" #include "../lib/process_xml.h" #include "../lib/return_codes.h" +namespace fff +{ class BatchRequestSwitchToMainDialog {}; //BatchStatusHandler(SyncProgressDialog) will internally process Window messages! disable GUI controls to avoid unexpected callbacks! -class BatchStatusHandler : public zen::StatusHandler //throw AbortProcess, BatchRequestSwitchToMainDialog +class BatchStatusHandler : public StatusHandler //throw AbortProcess, BatchRequestSwitchToMainDialog { public: BatchStatusHandler(bool showProgress, //defines: -start minimized and -quit immediately when finished + bool autoCloseDialog, const std::wstring& jobName, //should not be empty for a batch job! const Zstring& soundFileSyncComplete, - const std::chrono::system_clock::time_point& batchStartTime, + const std::chrono::system_clock::time_point& startTime, const Zstring& logFolderPathPhrase, int logfilesCountLimit, //0: logging inactive; < 0: no limit size_t lastSyncsLogFileSizeMax, bool ignoreErrors, - xmlAccess::BatchErrorDialog batchErrorDialog, + BatchErrorDialog batchErrorDialog, size_t automaticRetryCount, size_t automaticRetryDelay, - zen::FfsReturnCode& returnCode, + FfsReturnCode& returnCode, const Zstring& postSyncCommand, - zen::PostSyncCondition postSyncCondition, - xmlAccess::PostSyncAction postSyncAction); + PostSyncCondition postSyncCondition, + PostSyncAction postSyncAction); ~BatchStatusHandler(); void initNewPhase (int itemsTotal, int64_t bytesTotal, Phase phaseID) override; void updateProcessedData(int itemsDelta, int64_t bytesDelta) override; - void reportInfo (const std::wstring& text) override; - void forceUiRefresh () override; + void reportInfo (const std::wstring& text) override; + void forceUiRefreshNoThrow() override; void reportWarning (const std::wstring& warningMessage, bool& warningActive) override; Response reportError (const std::wstring& errorMessage, size_t retryNumber ) override; @@ -55,9 +57,9 @@ private: bool switchToGuiRequested_ = false; const int logfilesCountLimit_; const size_t lastSyncsLogFileSizeMax_; - const xmlAccess::BatchErrorDialog batchErrorDialog_; + const BatchErrorDialog batchErrorDialog_; zen::ErrorLog errorLog_; //list of non-resolved errors and warnings - zen::FfsReturnCode& returnCode_; + FfsReturnCode& returnCode_; const size_t automaticRetryCount_; const size_t automaticRetryDelay_; @@ -65,12 +67,12 @@ private: SyncProgressDialog* progressDlg_; //managed to have shorter lifetime than this handler! const std::wstring jobName_; - const std::chrono::system_clock::time_point batchStartTime_; - const time_t startTime_ = std::time(nullptr); //don't use wxStopWatch: may overflow after a few days due to ::QueryPerformanceCounter() + const std::chrono::system_clock::time_point startTime_; const Zstring logFolderPathPhrase_; const Zstring postSyncCommand_; - const zen::PostSyncCondition postSyncCondition_; + const PostSyncCondition postSyncCondition_; }; +} #endif //BATCH_STATUS_HANDLER_H_857390451451234566 diff --git a/FreeFileSync/Source/ui/cfg_grid.cpp b/FreeFileSync/Source/ui/cfg_grid.cpp index 3d6cc451..775a3f16 100755 --- a/FreeFileSync/Source/ui/cfg_grid.cpp +++ b/FreeFileSync/Source/ui/cfg_grid.cpp @@ -15,11 +15,12 @@ #include "../lib/ffs_paths.h" using namespace zen; +using namespace fff; -Zstring zen::getLastRunConfigPath() +Zstring fff::getLastRunConfigPath() { - return zen::getConfigDirPathPf() + Zstr("LastRun.ffs_gui"); + return getConfigDirPathPf() + Zstr("LastRun.ffs_gui"); } @@ -170,6 +171,25 @@ public: private: size_t getRowCount() const override { return cfgView_.getRowCount(); } + static int getDaysPast(time_t last) + { + time_t now = std::time(nullptr); + + const TimeComp tcNow = getLocalTime(now); + const TimeComp tcLast = getLocalTime(last); + if (tcNow == TimeComp() || tcLast == TimeComp()) + { + assert(false); + return 0; + } + + //truncate down to midnight => incorrect during DST switches, but doesn't matter due to numeric::round() below + now -= tcNow .hour * 3600 + tcNow .minute * 60 + tcNow .second; + last -= tcLast.hour * 3600 + tcLast.minute * 60 + tcLast.second; + + return numeric::round((now - last) / (24.0 * 3600)); + } + std::wstring getValue(size_t row, ColumnType colType) const override { if (const ConfigView::Details* item = cfgView_.getItem(row)) @@ -186,7 +206,7 @@ private: if (item->lastSyncTime == 0) return std::wstring(1, EN_DASH); - const int daysPast = numeric::round((std::time(nullptr) - item->lastSyncTime) / (24.0 * 3600)); + const int daysPast = getDaysPast(item->lastSyncTime); if (daysPast == 0) return _("Today"); @@ -240,11 +260,9 @@ private: { wxDCTextColourChanger dummy2(dc); if (syncOverdueDays_ > 0) - { - const int daysPast = numeric::round((std::time(nullptr) - item->lastSyncTime) / (24.0 * 3600)); - if (daysPast >= syncOverdueDays_) + if (getDaysPast(item->lastSyncTime) >= syncOverdueDays_) dummy2.Set(*wxRED); - } + drawCellText(dc, rectTmp, getValue(row, colType), wxALIGN_CENTER); } break; diff --git a/FreeFileSync/Source/ui/cfg_grid.h b/FreeFileSync/Source/ui/cfg_grid.h index 8a898aa7..7633d3c2 100755 --- a/FreeFileSync/Source/ui/cfg_grid.h +++ b/FreeFileSync/Source/ui/cfg_grid.h @@ -10,7 +10,8 @@ #include <wx+/grid.h> #include <zen/zstring.h> -namespace zen + +namespace fff { enum class ColumnTypeCfg { @@ -86,7 +87,7 @@ public: size_t getRowCount() const { assert(cfgList_.size() == cfgListView_.size()); return cfgListView_.size(); } void setSortDirection(ColumnTypeCfg colType, bool ascending); - std::pair<ColumnTypeCfg, bool> getSortDirection() { return std::make_pair(sortColumn_, sortAscending_); } + std::pair<ColumnTypeCfg, bool> getSortDirection() { return { sortColumn_, sortAscending_ }; } private: ConfigView (const ConfigView&) = delete; @@ -109,13 +110,13 @@ private: namespace cfggrid { -void init(Grid& grid); -ConfigView& getDataView(Grid& grid); //grid.Refresh() after making changes! +void init(zen::Grid& grid); +ConfigView& getDataView(zen::Grid& grid); //grid.Refresh() after making changes! -void addAndSelect(Grid& grid, const std::vector<Zstring>& filePaths, bool scrollToSelection); +void addAndSelect(zen::Grid& grid, const std::vector<Zstring>& filePaths, bool scrollToSelection); -int getSyncOverdueDays(Grid& grid); -void setSyncOverdueDays(Grid& grid, int syncOverdueDays); +int getSyncOverdueDays(zen::Grid& grid); +void setSyncOverdueDays(zen::Grid& grid, int syncOverdueDays); } } diff --git a/FreeFileSync/Source/ui/command_box.cpp b/FreeFileSync/Source/ui/command_box.cpp index fc848fa2..3468415a 100755 --- a/FreeFileSync/Source/ui/command_box.cpp +++ b/FreeFileSync/Source/ui/command_box.cpp @@ -12,6 +12,7 @@ #include <zen/utf.h> using namespace zen; +using namespace fff; namespace @@ -24,7 +25,7 @@ std::vector<std::pair<std::wstring, Zstring>> getDefaultCommands() //(gui name/c { return { - //{_("Sleep"), Zstr("rundll32.exe powrprof.dll,SetSuspendState Sleep")}, + //{_("System: Sleep"), Zstr("rundll32.exe powrprof.dll,SetSuspendState Sleep")}, }; } diff --git a/FreeFileSync/Source/ui/command_box.h b/FreeFileSync/Source/ui/command_box.h index 719a62ac..ddc6f7d0 100755 --- a/FreeFileSync/Source/ui/command_box.h +++ b/FreeFileSync/Source/ui/command_box.h @@ -14,9 +14,10 @@ #include <zen/string_tools.h> #include <zen/zstring.h> -//combobox with history function + functionality to delete items (DEL) - +//combobox with history function + functionality to delete items (DEL) +namespace fff +{ class CommandBox : public wxComboBox { public: @@ -54,6 +55,6 @@ private: const std::vector<std::pair<std::wstring, Zstring>> defaultCommands_; }; - +} #endif //COMMAND_BOX_H_18947773210473214 diff --git a/FreeFileSync/Source/ui/file_grid.cpp b/FreeFileSync/Source/ui/file_grid.cpp index 2a440adc..7e704ba1 100755 --- a/FreeFileSync/Source/ui/file_grid.cpp +++ b/FreeFileSync/Source/ui/file_grid.cpp @@ -21,11 +21,12 @@ #include "../file_hierarchy.h" using namespace zen; -using namespace filegrid; +using namespace fff; -const wxEventType zen::EVENT_GRID_CHECK_ROWS = wxNewEventType(); -const wxEventType zen::EVENT_GRID_SYNC_DIRECTION = wxNewEventType(); +const wxEventType fff::EVENT_GRID_CHECK_ROWS = wxNewEventType(); +const wxEventType fff::EVENT_GRID_SYNC_DIRECTION = wxNewEventType(); + namespace { @@ -67,10 +68,10 @@ std::pair<ptrdiff_t, ptrdiff_t> getVisibleRows(const Grid& grid) //returns range const ptrdiff_t rowFrom = grid.getRowAtPos(topLeft.y); //return -1 for invalid position, rowCount if out of range const ptrdiff_t rowTo = grid.getRowAtPos(bottom.y); if (rowFrom >= 0 && rowTo >= 0) - return std::make_pair(rowFrom, std::min(rowTo + 1, rowCount)); + return { rowFrom, std::min(rowTo + 1, rowCount) }; } assert(false); - return std::make_pair(0, 0); + return {}; } @@ -1752,7 +1753,7 @@ void filegrid::highlightSyncAction(Grid& gridCenter, bool value) } -wxBitmap zen::getSyncOpImage(SyncOperation syncOp) +wxBitmap fff::getSyncOpImage(SyncOperation syncOp) { switch (syncOp) //evaluate comparison result and sync direction { @@ -1792,7 +1793,7 @@ wxBitmap zen::getSyncOpImage(SyncOperation syncOp) } -wxBitmap zen::getCmpResultImage(CompareFilesResult cmpResult) +wxBitmap fff::getCmpResultImage(CompareFilesResult cmpResult) { switch (cmpResult) { diff --git a/FreeFileSync/Source/ui/file_grid.h b/FreeFileSync/Source/ui/file_grid.h index 40853ceb..3904786a 100755 --- a/FreeFileSync/Source/ui/file_grid.h +++ b/FreeFileSync/Source/ui/file_grid.h @@ -13,27 +13,27 @@ #include "../lib/icon_buffer.h" -namespace zen +namespace fff { //setup grid to show grid view within three components: namespace filegrid { -void init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight); -FileView& getDataView(Grid& grid); +void init(zen::Grid& gridLeft, zen::Grid& gridCenter, zen::Grid& gridRight); +FileView& getDataView(zen::Grid& grid); -void highlightSyncAction(Grid& gridCenter, bool value); +void highlightSyncAction(zen::Grid& gridCenter, bool value); -void setupIcons(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, bool show, IconBuffer::IconSize sz); +void setupIcons(zen::Grid& gridLeft, zen::Grid& gridCenter, zen::Grid& gridRight, bool show, IconBuffer::IconSize sz); -void setItemPathForm(Grid& grid, ItemPathFormat fmt); //only for left/right grid +void setItemPathForm(zen::Grid& grid, ItemPathFormat fmt); //only for left/right grid -void refresh(Grid& gridLeft, Grid& gridCenter, Grid& gridRight); +void refresh(zen::Grid& gridLeft, zen::Grid& gridCenter, zen::Grid& gridRight); -void setScrollMaster(Grid& grid); +void setScrollMaster(zen::Grid& grid); //mark rows selected in overview panel and navigate to leading object -void setNavigationMarker(Grid& gridLeft, +void setNavigationMarker(zen::Grid& gridLeft, std::unordered_set<const FileSystemObject*>&& markedFilesAndLinks,//mark files/symlinks directly within a container std::unordered_set<const ContainerObject*>&& markedContainer); //mark full container including child-objects } diff --git a/FreeFileSync/Source/ui/file_grid_attr.h b/FreeFileSync/Source/ui/file_grid_attr.h index 0257f268..ea3c6303 100755 --- a/FreeFileSync/Source/ui/file_grid_attr.h +++ b/FreeFileSync/Source/ui/file_grid_attr.h @@ -11,7 +11,7 @@ #include <cassert> -namespace zen +namespace fff { enum class ColumnTypeRim { diff --git a/FreeFileSync/Source/ui/file_view.cpp b/FreeFileSync/Source/ui/file_view.cpp index f2527520..c68357b0 100755 --- a/FreeFileSync/Source/ui/file_view.cpp +++ b/FreeFileSync/Source/ui/file_view.cpp @@ -11,6 +11,7 @@ #include <zen/perf.h> using namespace zen; +using namespace fff; template <class StatusResult> diff --git a/FreeFileSync/Source/ui/file_view.h b/FreeFileSync/Source/ui/file_view.h index ad399401..deaf763f 100755 --- a/FreeFileSync/Source/ui/file_view.h +++ b/FreeFileSync/Source/ui/file_view.h @@ -13,10 +13,9 @@ #include "../file_hierarchy.h" -namespace zen +namespace fff { -//grid view of FolderComparison -class FileView +class FileView //grid view of FolderComparison { public: FileView() {} @@ -101,11 +100,11 @@ public: void removeInvalidRows(); //remove references to rows that have been deleted meanwhile: call after manual deletion and synchronization! //sorting... - void sortView(zen::ColumnTypeRim type, zen::ItemPathFormat pathFmt, bool onLeft, bool ascending); //always call this method for sorting, never sort externally! + void sortView(ColumnTypeRim type, ItemPathFormat pathFmt, bool onLeft, bool ascending); //always call this method for sorting, never sort externally! struct SortInfo { - zen::ColumnTypeRim type = zen::ColumnTypeRim::ITEM_PATH; + ColumnTypeRim type = ColumnTypeRim::ITEM_PATH; bool onLeft = false; bool ascending = false; }; @@ -173,7 +172,7 @@ private: template <bool ascending> struct LessSyncDirection; - Opt<SortInfo> currentSort_; + zen::Opt<SortInfo> currentSort_; }; diff --git a/FreeFileSync/Source/ui/folder_history_box.cpp b/FreeFileSync/Source/ui/folder_history_box.cpp index ff434468..fc4902d9 100755 --- a/FreeFileSync/Source/ui/folder_history_box.cpp +++ b/FreeFileSync/Source/ui/folder_history_box.cpp @@ -11,6 +11,7 @@ #include <gtk/gtk.h> using namespace zen; +using namespace fff; FolderHistoryBox::FolderHistoryBox(wxWindow* parent, diff --git a/FreeFileSync/Source/ui/folder_history_box.h b/FreeFileSync/Source/ui/folder_history_box.h index 94b9f494..0252d7cd 100755 --- a/FreeFileSync/Source/ui/folder_history_box.h +++ b/FreeFileSync/Source/ui/folder_history_box.h @@ -13,10 +13,10 @@ #include <zen/stl_tools.h> #include <zen/utf.h> -//combobox with history function + functionality to delete items (DEL) - -class FolderHistory +namespace fff +{ +class FolderHistory //combobox with history function + functionality to delete items (DEL) { public: FolderHistory() {} @@ -87,6 +87,6 @@ private: std::shared_ptr<FolderHistory> sharedHistory_; }; - +} #endif //FOLDER_HISTORY_BOX_H_08170517045945 diff --git a/FreeFileSync/Source/ui/folder_pair.h b/FreeFileSync/Source/ui/folder_pair.h index 49a24c7c..5b71c09a 100755 --- a/FreeFileSync/Source/ui/folder_pair.h +++ b/FreeFileSync/Source/ui/folder_pair.h @@ -20,7 +20,7 @@ #include "../structures.h" -namespace zen +namespace fff { //basic functionality for handling alternate folder pair configuration: change sync-cfg/filter cfg, right-click context menu, button icons... @@ -33,15 +33,15 @@ public: void setConfig(AltCompCfgPtr compConfig, AltSyncCfgPtr syncCfg, const FilterConfig& filter) { - altCompConfig = compConfig; - altSyncConfig = syncCfg; - localFilter = filter; + altCompConfig_ = compConfig; + altSyncConfig_ = syncCfg; + localFilter_ = filter; refreshButtons(); } - AltCompCfgPtr getAltCompConfig () const { return altCompConfig; } - AltSyncCfgPtr getAltSyncConfig () const { return altSyncConfig; } - FilterConfig getAltFilterConfig() const { return localFilter; } + AltCompCfgPtr getAltCompConfig () const { return altCompConfig_; } + AltSyncCfgPtr getAltSyncConfig () const { return altSyncConfig_; } + FilterConfig getAltFilterConfig() const { return localFilter_; } FolderPairPanelBasic(GuiPanel& basicPanel) : //takes reference on basic panel to be enhanced @@ -52,16 +52,18 @@ public: basicPanel_.m_bpButtonAltSyncCfg ->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnAltSyncCfgContext ), nullptr, this); basicPanel_.m_bpButtonLocalFilter->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnLocalFilterCfgContext), nullptr, this); - basicPanel_.m_bpButtonRemovePair->SetBitmapLabel(getResourceImage(L"item_remove")); + basicPanel_.m_bpButtonRemovePair->SetBitmapLabel(zen::getResourceImage(L"item_remove")); } private: void refreshButtons() { - if (altCompConfig.get()) + using namespace zen; + + if (altCompConfig_.get()) { setImage(*basicPanel_.m_bpButtonAltCompCfg, getResourceImage(L"cfg_compare_small")); - basicPanel_.m_bpButtonAltCompCfg->SetToolTip(_("Local comparison settings") + L" (" + getVariantName(altCompConfig->compareVar) + L")"); + basicPanel_.m_bpButtonAltCompCfg->SetToolTip(_("Local comparison settings") + L" (" + getVariantName(altCompConfig_->compareVar) + L")"); } else { @@ -69,10 +71,10 @@ private: basicPanel_.m_bpButtonAltCompCfg->SetToolTip(_("Local comparison settings")); } - if (altSyncConfig.get()) + if (altSyncConfig_.get()) { setImage(*basicPanel_.m_bpButtonAltSyncCfg, getResourceImage(L"cfg_sync_small")); - basicPanel_.m_bpButtonAltSyncCfg->SetToolTip(_("Local synchronization settings") + L" (" + getVariantName(altSyncConfig->directionCfg.var) + L")"); + basicPanel_.m_bpButtonAltSyncCfg->SetToolTip(_("Local synchronization settings") + L" (" + getVariantName(altSyncConfig_->directionCfg.var) + L")"); } else { @@ -80,7 +82,7 @@ private: basicPanel_.m_bpButtonAltSyncCfg->SetToolTip(_("Local synchronization settings")); } - if (!isNullFilter(localFilter)) + if (!isNullFilter(localFilter_)) { setImage(*basicPanel_.m_bpButtonLocalFilter, getResourceImage(L"filter_small")); basicPanel_.m_bpButtonLocalFilter->SetToolTip(_("Local filter") + L" (" + _("Active") + L")"); @@ -96,13 +98,13 @@ private: { auto removeAltCompCfg = [&] { - this->altCompConfig.reset(); //"this->" galore: workaround GCC compiler bugs + this->altCompConfig_.reset(); //"this->" galore: workaround GCC compiler bugs this->refreshButtons(); this->onAltCompCfgChange(); }; - ContextMenu menu; - menu.addItem(_("Remove local settings"), removeAltCompCfg, nullptr, altCompConfig.get() != nullptr); + zen::ContextMenu menu; + menu.addItem(_("Remove local settings"), removeAltCompCfg, nullptr, altCompConfig_.get() != nullptr); menu.popup(basicPanel_); } @@ -110,13 +112,13 @@ private: { auto removeAltSyncCfg = [&] { - this->altSyncConfig.reset(); + this->altSyncConfig_.reset(); this->refreshButtons(); this->onAltSyncCfgChange(); }; - ContextMenu menu; - menu.addItem(_("Remove local settings"), removeAltSyncCfg, nullptr, altSyncConfig.get() != nullptr); + zen::ContextMenu menu; + menu.addItem(_("Remove local settings"), removeAltSyncCfg, nullptr, altSyncConfig_.get() != nullptr); menu.popup(basicPanel_); } @@ -124,28 +126,28 @@ private: { auto removeLocalFilterCfg = [&] { - this->localFilter = FilterConfig(); + this->localFilter_ = FilterConfig(); this->refreshButtons(); this->onLocalFilterCfgChange(); }; std::unique_ptr<FilterConfig>& filterCfgOnClipboard = getFilterCfgOnClipboardRef(); - auto copyFilter = [&] { filterCfgOnClipboard = std::make_unique<FilterConfig>(this->localFilter); }; + auto copyFilter = [&] { filterCfgOnClipboard = std::make_unique<FilterConfig>(this->localFilter_); }; auto pasteFilter = [&] { if (filterCfgOnClipboard) { - this->localFilter = *filterCfgOnClipboard; + this->localFilter_ = *filterCfgOnClipboard; this->refreshButtons(); this->onLocalFilterCfgChange(); } }; - ContextMenu menu; - menu.addItem(_("Clear local filter"), removeLocalFilterCfg, nullptr, !isNullFilter(localFilter)); + zen::ContextMenu menu; + menu.addItem(_("Clear local filter"), removeLocalFilterCfg, nullptr, !isNullFilter(localFilter_)); menu.addSeparator(); - menu.addItem( _("Copy"), copyFilter, nullptr, !isNullFilter(localFilter)); + menu.addItem( _("Copy"), copyFilter, nullptr, !isNullFilter(localFilter_)); menu.addItem( _("Paste"), pasteFilter, nullptr, filterCfgOnClipboard.get() != nullptr); menu.popup(basicPanel_); } @@ -162,9 +164,9 @@ private: GuiPanel& basicPanel_; //panel to be enhanced by this template //alternate configuration attached to it - AltCompCfgPtr altCompConfig; //optional - AltSyncCfgPtr altSyncConfig; // - FilterConfig localFilter; + AltCompCfgPtr altCompConfig_; //optional + AltSyncCfgPtr altSyncConfig_; // + FilterConfig localFilter_; }; } diff --git a/FreeFileSync/Source/ui/folder_selector.cpp b/FreeFileSync/Source/ui/folder_selector.cpp index 73d4d461..eed43dc6 100755 --- a/FreeFileSync/Source/ui/folder_selector.cpp +++ b/FreeFileSync/Source/ui/folder_selector.cpp @@ -20,6 +20,7 @@ using namespace zen; +using namespace fff; using AFS = AbstractFileSystem; @@ -49,8 +50,8 @@ void setFolderPathPhrase(const Zstring& folderPathPhrase, FolderHistoryBox* comb //############################################################################################################## -const wxEventType zen::EVENT_ON_FOLDER_SELECTED = wxNewEventType(); -const wxEventType zen::EVENT_ON_FOLDER_MANUAL_EDIT = wxNewEventType(); +const wxEventType fff::EVENT_ON_FOLDER_SELECTED = wxNewEventType(); +const wxEventType fff::EVENT_ON_FOLDER_MANUAL_EDIT = wxNewEventType(); FolderSelector::FolderSelector(wxWindow& dropWindow, diff --git a/FreeFileSync/Source/ui/folder_selector.h b/FreeFileSync/Source/ui/folder_selector.h index a0271f6f..a2154ac6 100755 --- a/FreeFileSync/Source/ui/folder_selector.h +++ b/FreeFileSync/Source/ui/folder_selector.h @@ -14,7 +14,7 @@ #include "folder_history_box.h" -namespace zen +namespace fff { //handle drag and drop, tooltip, label and manual input, coordinating a wxWindow, wxButton, and wxComboBox/wxTextCtrl /* @@ -46,11 +46,13 @@ public: Zstring getPath() const; void setPath(const Zstring& folderPathPhrase); + void setBackgroundText(const std::wstring& text) { folderComboBox_.SetHint(text); } + private: virtual bool shouldSetDroppedPaths(const std::vector<Zstring>& shellItemPaths) { return true; } //return true if drop should be processed void onMouseWheel (wxMouseEvent& event); - void onItemPathDropped(FileDropEvent& event); + void onItemPathDropped(zen::FileDropEvent& event); void onEditFolderPath (wxCommandEvent& event); void onSelectFolder (wxCommandEvent& event); void onSelectAltFolder(wxCommandEvent& event); diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp index cbe00bd7..747095d1 100755 --- a/FreeFileSync/Source/ui/gui_generated.cpp +++ b/FreeFileSync/Source/ui/gui_generated.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Nov 6 2017) +// C++ code generated with wxFormBuilder (version Jan 23 2018) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -280,7 +280,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer182; bSizer182 = new wxBoxSizer( wxHORIZONTAL ); - m_folderPathLeft = new FolderHistoryBox( m_panelTopLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + m_folderPathLeft = new fff::FolderHistoryBox( m_panelTopLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); bSizer182->Add( m_folderPathLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); m_buttonSelectFolderLeft = new wxButton( m_panelTopLeft, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); @@ -351,7 +351,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer179; bSizer179 = new wxBoxSizer( wxHORIZONTAL ); - m_folderPathRight = new FolderHistoryBox( m_panelTopRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + m_folderPathRight = new fff::FolderHistoryBox( m_panelTopRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); bSizer179->Add( m_folderPathRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); m_buttonSelectFolderRight = new wxButton( m_panelTopRight, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); @@ -402,7 +402,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer1711; bSizer1711 = new wxBoxSizer( wxVERTICAL ); - m_splitterMain = new zen::TripleSplitter( m_panelCenter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_splitterMain = new fff::TripleSplitter( m_panelCenter, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); wxBoxSizer* bSizer1781; bSizer1781 = new wxBoxSizer( wxHORIZONTAL ); @@ -703,10 +703,10 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_staticTextViewType->Wrap( -1 ); bSizerViewFilter->Add( m_staticTextViewType, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonViewTypeSyncAction = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 82, 42 ), wxBU_AUTODRAW ); + m_bpButtonViewTypeSyncAction = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 82, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonViewTypeSyncAction, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxRIGHT, 5 ); - m_bpButtonShowExcluded = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowExcluded = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowExcluded, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); @@ -716,46 +716,46 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_staticTextSelectView->Wrap( -1 ); bSizerViewFilter->Add( m_staticTextSelectView, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowDeleteLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowDeleteLeft = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowDeleteLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowUpdateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowUpdateLeft = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowCreateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowCreateLeft = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowLeftOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowLeftOnly = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowLeftOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowLeftNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowLeftNewer = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowLeftNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowEqual = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowEqual = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowEqual, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowDoNothing = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowDoNothing = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowDoNothing, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowDifferent = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowDifferent = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowDifferent, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowRightNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowRightNewer = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowRightNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowRightOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowRightOnly = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowRightOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowCreateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowCreateRight = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowUpdateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowUpdateRight = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowDeleteRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowDeleteRight = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowDeleteRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowConflict = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); + m_bpButtonShowConflict = new zen::ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowConflict, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); @@ -1769,7 +1769,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w wxBoxSizer* bSizer156; bSizer156 = new wxBoxSizer( wxHORIZONTAL ); - m_versioningFolderPath = new FolderHistoryBox( m_panelVersioning, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + m_versioningFolderPath = new fff::FolderHistoryBox( m_panelVersioning, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); bSizer156->Add( m_versioningFolderPath, 1, wxALIGN_CENTER_VERTICAL, 5 ); m_buttonSelectVersioningFolder = new wxButton( m_panelVersioning, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); @@ -1867,7 +1867,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w m_choicePostSyncCondition->SetSelection( 0 ); bSizer251->Add( m_choicePostSyncCondition, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_comboBoxPostSyncCommand = new CommandBox( m_panelSyncSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + m_comboBoxPostSyncCommand = new fff::CommandBox( m_panelSyncSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); bSizer251->Add( m_comboBoxPostSyncCommand, 1, wxALIGN_CENTER_VERTICAL, 5 ); @@ -1991,7 +1991,7 @@ FolderPairPanelGenerated::FolderPairPanelGenerated( wxWindow* parent, wxWindowID bSizer134->Add( m_bpButtonRemovePair, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_folderPathLeft = new FolderHistoryBox( m_panelLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + m_folderPathLeft = new fff::FolderHistoryBox( m_panelLeft, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); bSizer134->Add( m_folderPathLeft, 1, wxALIGN_CENTER_VERTICAL, 5 ); m_buttonSelectFolderLeft = new wxButton( m_panelLeft, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); @@ -2041,7 +2041,7 @@ FolderPairPanelGenerated::FolderPairPanelGenerated( wxWindow* parent, wxWindowID wxBoxSizer* bSizer135; bSizer135 = new wxBoxSizer( wxHORIZONTAL ); - m_folderPathRight = new FolderHistoryBox( m_panelRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + m_folderPathRight = new fff::FolderHistoryBox( m_panelRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); bSizer135->Add( m_folderPathRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); m_buttonSelectFolderRight = new wxButton( m_panelRight, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); @@ -3213,11 +3213,14 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + m_checkBoxAutoClose = new wxCheckBox( this, wxID_ANY, _("Auto-Close"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerStdButtons->Add( m_checkBoxAutoClose, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonClose->SetDefault(); m_buttonClose->Enable( false ); - bSizerStdButtons->Add( m_buttonClose, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + bSizerStdButtons->Add( m_buttonClose, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); m_buttonPause = new wxButton( this, wxID_ANY, _("&Pause"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonPause, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); @@ -3226,7 +3229,7 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind bSizerStdButtons->Add( m_buttonStop, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - bSizerRoot->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + bSizerRoot->Add( bSizerStdButtons, 0, wxEXPAND, 5 ); this->SetSizer( bSizerRoot ); @@ -3248,13 +3251,13 @@ LogPanelGenerated::LogPanelGenerated( wxWindow* parent, wxWindowID id, const wxP wxBoxSizer* bSizer154; bSizer154 = new wxBoxSizer( wxVERTICAL ); - m_bpButtonErrors = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49, 49 ), wxBU_AUTODRAW ); + m_bpButtonErrors = new zen::ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49, 49 ), wxBU_AUTODRAW ); bSizer154->Add( m_bpButtonErrors, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonWarnings = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49, 49 ), wxBU_AUTODRAW ); + m_bpButtonWarnings = new zen::ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49, 49 ), wxBU_AUTODRAW ); bSizer154->Add( m_bpButtonWarnings, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonInfo = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49, 49 ), wxBU_AUTODRAW ); + m_bpButtonInfo = new zen::ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49, 49 ), wxBU_AUTODRAW ); bSizer154->Add( m_bpButtonInfo, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); @@ -3315,17 +3318,35 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS wxBoxSizer* bSizer180; bSizer180 = new wxBoxSizer( wxHORIZONTAL ); - wxBoxSizer* bSizer236; - bSizer236 = new wxBoxSizer( wxHORIZONTAL ); + wxBoxSizer* bSizer2361; + bSizer2361 = new wxBoxSizer( wxVERTICAL ); + + m_staticText146 = new wxStaticText( m_panel35, wxID_ANY, _("Progress dialog:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText146->Wrap( -1 ); + bSizer2361->Add( m_staticText146, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); + + ffgSizer11 = new wxFlexGridSizer( 0, 2, 5, 5 ); + ffgSizer11->SetFlexibleDirection( wxBOTH ); + ffgSizer11->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); m_bitmapMinimizeToTray = new wxStaticBitmap( m_panel35, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); - bSizer236->Add( m_bitmapMinimizeToTray, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); + ffgSizer11->Add( m_bitmapMinimizeToTray, 0, wxALIGN_CENTER_VERTICAL, 5 ); m_checkBoxRunMinimized = new wxCheckBox( m_panel35, wxID_ANY, _("Run minimized"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer236->Add( m_checkBoxRunMinimized, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + ffgSizer11->Add( m_checkBoxRunMinimized, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); + + + ffgSizer11->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_checkBoxAutoClose = new wxCheckBox( m_panel35, wxID_ANY, _("Auto-Close"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxAutoClose->SetValue(true); + ffgSizer11->Add( m_checkBoxAutoClose, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + bSizer2361->Add( ffgSizer11, 0, wxEXPAND|wxALL, 5 ); - bSizer180->Add( bSizer236, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + bSizer180->Add( bSizer2361, 0, wxALL, 5 ); m_staticline26 = new wxStaticLine( m_panel35, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); bSizer180->Add( m_staticline26, 0, wxEXPAND, 5 ); @@ -3426,7 +3447,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS wxBoxSizer* bSizer1721; bSizer1721 = new wxBoxSizer( wxHORIZONTAL ); - m_logFolderPath = new FolderHistoryBox( m_panelLogfile, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + m_logFolderPath = new fff::FolderHistoryBox( m_panelLogfile, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); bSizer1721->Add( m_logFolderPath, 1, wxALIGN_CENTER_VERTICAL, 5 ); m_buttonSelectLogFolder = new wxButton( m_panelLogfile, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); @@ -3632,7 +3653,7 @@ CopyToDlgGenerated::CopyToDlgGenerated( wxWindow* parent, wxWindowID id, const w wxBoxSizer* bSizer182; bSizer182 = new wxBoxSizer( wxHORIZONTAL ); - m_targetFolderPath = new FolderHistoryBox( m_panel31, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + m_targetFolderPath = new fff::FolderHistoryBox( m_panel31, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); bSizer182->Add( m_targetFolderPath, 1, wxALIGN_CENTER_VERTICAL, 5 ); m_buttonSelectTargetFolder = new wxButton( m_panel31, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); @@ -3938,15 +3959,16 @@ OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const bSizer1881 = new wxBoxSizer( wxHORIZONTAL ); m_buttonResetDialogs = new zen::BitmapTextButton( m_panel39, wxID_ANY, _("Show hidden dialogs again"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); - m_buttonResetDialogs->SetToolTip( _("Show all permanently hidden dialogs and warning messages again") ); + bSizer1881->Add( m_buttonResetDialogs, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - bSizer1881->Add( m_buttonResetDialogs, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); + m_staticText923 = new wxStaticText( m_panel39, wxID_ANY, _("Show all permanently hidden dialogs and warning messages again"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText923->Wrap( 350 ); + m_staticText923->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); - m_staticline40 = new wxStaticLine( m_panel39, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); - bSizer1881->Add( m_staticline40, 0, wxEXPAND, 5 ); + bSizer1881->Add( m_staticText923, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - bSizer166->Add( bSizer1881, 0, 0, 5 ); + bSizer166->Add( bSizer1881, 0, wxALL, 5 ); m_panel39->SetSizer( bSizer166 ); diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h index 2272410e..3b2287e1 100755 --- a/FreeFileSync/Source/ui/gui_generated.h +++ b/FreeFileSync/Source/ui/gui_generated.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Nov 6 2017) +// C++ code generated with wxFormBuilder (version Jan 23 2018) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -11,13 +11,13 @@ #include <wx/artprov.h> #include <wx/xrc/xmlres.h> #include <wx/intl.h> -class CommandBox; -class FolderHistoryBox; -class ToggleButton; +namespace fff { class CommandBox; } +namespace fff { class FolderHistoryBox; } +namespace fff { class TripleSplitter; } namespace zen { class BitmapTextButton; } namespace zen { class Graph2D; } namespace zen { class Grid; } -namespace zen { class TripleSplitter; } +namespace zen { class ToggleButton; } #include <wx/string.h> #include <wx/bitmap.h> @@ -119,7 +119,7 @@ protected: wxBoxSizer* bSizerAddFolderPairs; zen::Grid* m_gridOverview; wxPanel* m_panelCenter; - zen::TripleSplitter* m_splitterMain; + fff::TripleSplitter* m_splitterMain; zen::Grid* m_gridMainL; zen::Grid* m_gridMainC; zen::Grid* m_gridMainR; @@ -166,23 +166,23 @@ protected: wxPanel* m_panelViewFilter; wxBoxSizer* bSizerViewFilter; wxStaticText* m_staticTextViewType; - ToggleButton* m_bpButtonViewTypeSyncAction; - ToggleButton* m_bpButtonShowExcluded; + zen::ToggleButton* m_bpButtonViewTypeSyncAction; + zen::ToggleButton* m_bpButtonShowExcluded; wxStaticText* m_staticTextSelectView; - ToggleButton* m_bpButtonShowDeleteLeft; - ToggleButton* m_bpButtonShowUpdateLeft; - ToggleButton* m_bpButtonShowCreateLeft; - ToggleButton* m_bpButtonShowLeftOnly; - ToggleButton* m_bpButtonShowLeftNewer; - ToggleButton* m_bpButtonShowEqual; - ToggleButton* m_bpButtonShowDoNothing; - ToggleButton* m_bpButtonShowDifferent; - ToggleButton* m_bpButtonShowRightNewer; - ToggleButton* m_bpButtonShowRightOnly; - ToggleButton* m_bpButtonShowCreateRight; - ToggleButton* m_bpButtonShowUpdateRight; - ToggleButton* m_bpButtonShowDeleteRight; - ToggleButton* m_bpButtonShowConflict; + zen::ToggleButton* m_bpButtonShowDeleteLeft; + zen::ToggleButton* m_bpButtonShowUpdateLeft; + zen::ToggleButton* m_bpButtonShowCreateLeft; + zen::ToggleButton* m_bpButtonShowLeftOnly; + zen::ToggleButton* m_bpButtonShowLeftNewer; + zen::ToggleButton* m_bpButtonShowEqual; + zen::ToggleButton* m_bpButtonShowDoNothing; + zen::ToggleButton* m_bpButtonShowDifferent; + zen::ToggleButton* m_bpButtonShowRightNewer; + zen::ToggleButton* m_bpButtonShowRightOnly; + zen::ToggleButton* m_bpButtonShowCreateRight; + zen::ToggleButton* m_bpButtonShowUpdateRight; + zen::ToggleButton* m_bpButtonShowDeleteRight; + zen::ToggleButton* m_bpButtonShowConflict; wxStaticText* m_staticText96; wxPanel* m_panelStatistics; wxBoxSizer* bSizer1801; @@ -244,13 +244,13 @@ protected: public: wxPanel* m_panelTopLeft; wxBitmapButton* m_bpButtonRemovePair; - FolderHistoryBox* m_folderPathLeft; + fff::FolderHistoryBox* m_folderPathLeft; wxBitmapButton* m_bpButtonSelectAltFolderLeft; wxBitmapButton* m_bpButtonAltCompCfg; wxBitmapButton* m_bpButtonLocalFilter; wxBitmapButton* m_bpButtonAltSyncCfg; wxPanel* m_panelTopRight; - FolderHistoryBox* m_folderPathRight; + fff::FolderHistoryBox* m_folderPathRight; wxBitmapButton* m_bpButtonSelectAltFolderRight; wxBoxSizer* bSizerStatistics; wxBoxSizer* bSizerData; @@ -379,7 +379,7 @@ protected: wxStaticText* m_staticTextDeletionTypeDescription; wxHyperlinkCtrl* m_hyperlinkVersioning; wxPanel* m_panelVersioning; - FolderHistoryBox* m_versioningFolderPath; + fff::FolderHistoryBox* m_versioningFolderPath; wxButton* m_buttonSelectVersioningFolder; wxStaticText* m_staticText93; wxChoice* m_choiceVersioningStyle; @@ -393,7 +393,7 @@ protected: wxStaticLine* m_staticline57; wxBoxSizer* bSizerOnCompletion; wxStaticText* m_staticText89; - CommandBox* m_comboBoxPostSyncCommand; + fff::CommandBox* m_comboBoxPostSyncCommand; wxBoxSizer* bSizerStdButtons; wxButton* m_buttonOkay; wxButton* m_buttonCancel; @@ -467,14 +467,14 @@ public: wxPanel* m_panelLeft; wxBitmapButton* m_bpButtonFolderPairOptions; wxBitmapButton* m_bpButtonRemovePair; - FolderHistoryBox* m_folderPathLeft; + fff::FolderHistoryBox* m_folderPathLeft; wxBitmapButton* m_bpButtonSelectAltFolderLeft; wxPanel* m_panel20; wxBitmapButton* m_bpButtonAltCompCfg; wxBitmapButton* m_bpButtonLocalFilter; wxBitmapButton* m_bpButtonAltSyncCfg; wxPanel* m_panelRight; - FolderHistoryBox* m_folderPathRight; + fff::FolderHistoryBox* m_folderPathRight; wxBitmapButton* m_bpButtonSelectAltFolderRight; FolderPairPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0 ); @@ -739,6 +739,7 @@ public: wxNotebook* m_notebookResult; wxStaticLine* m_staticlineFooter; wxBoxSizer* bSizerStdButtons; + wxCheckBox* m_checkBoxAutoClose; wxButton* m_buttonClose; wxButton* m_buttonPause; wxButton* m_buttonStop; @@ -756,9 +757,9 @@ class LogPanelGenerated : public wxPanel private: protected: - ToggleButton* m_bpButtonErrors; - ToggleButton* m_bpButtonWarnings; - ToggleButton* m_bpButtonInfo; + zen::ToggleButton* m_bpButtonErrors; + zen::ToggleButton* m_bpButtonWarnings; + zen::ToggleButton* m_bpButtonInfo; wxStaticLine* m_staticline13; zen::Grid* m_gridMessages; @@ -787,6 +788,8 @@ protected: wxStaticText* m_staticTextDescr; wxStaticLine* m_staticline18; wxPanel* m_panel35; + wxStaticText* m_staticText146; + wxFlexGridSizer* ffgSizer11; wxStaticBitmap* m_bitmapMinimizeToTray; wxCheckBox* m_checkBoxRunMinimized; wxStaticLine* m_staticline26; @@ -824,8 +827,9 @@ protected: public: + wxCheckBox* m_checkBoxAutoClose; wxChoice* m_choicePostSyncAction; - FolderHistoryBox* m_logFolderPath; + fff::FolderHistoryBox* m_logFolderPath; wxBitmapButton* m_bpButtonSelectAltLogFolder; BatchDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Save as a Batch Job"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); @@ -897,7 +901,7 @@ protected: public: - FolderHistoryBox* m_targetFolderPath; + fff::FolderHistoryBox* m_targetFolderPath; wxBitmapButton* m_bpButtonSelectAltTargetFolder; CopyToDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Copy items"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); @@ -944,7 +948,7 @@ protected: wxHyperlinkCtrl* m_hyperlink17; wxStaticLine* m_staticline192; zen::BitmapTextButton* m_buttonResetDialogs; - wxStaticLine* m_staticline40; + wxStaticText* m_staticText923; wxStaticLine* m_staticline36; wxBoxSizer* bSizerStdButtons; wxButton* m_buttonDefault; diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp index ac9f2479..ee14082a 100755 --- a/FreeFileSync/Source/ui/gui_status_handler.cpp +++ b/FreeFileSync/Source/ui/gui_status_handler.cpp @@ -17,7 +17,7 @@ #include "../lib/status_handler_impl.h" using namespace zen; -using namespace xmlAccess; +using namespace fff; StatusHandlerTemporaryPanel::StatusHandlerTemporaryPanel(MainDialog& dlg) : mainDlg_(dlg) @@ -122,13 +122,13 @@ void StatusHandlerTemporaryPanel::initNewPhase(int itemsTotal, int64_t bytesTota mainDlg_.compareStatus_->initNewPhase(); //call after "StatusHandler::initNewPhase" - forceUiRefresh(); //throw ?; OS X needs a full yield to update GUI and get rid of "dummy" texts + forceUiRefresh(); //throw X; OS X needs a full yield to update GUI and get rid of "dummy" texts } void StatusHandlerTemporaryPanel::reportInfo(const std::wstring& text) { - errorLog_.logMsg(text, TYPE_INFO); //log first! + errorLog_.logMsg(text, MSG_TYPE_INFO); //log first! StatusHandler::reportInfo(text); //throw X } @@ -139,11 +139,11 @@ 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, MSG_TYPE_ERROR); }); if (!mainDlg_.compareStatus_->getOptionIgnoreErrors()) { - forceUiRefresh(); + forceUiRefreshNoThrow(); //noexcept! => don't throw here when error occurs during clean up! switch (showConfirmationDialog(&mainDlg_, DialogInfoType::ERROR2, PopupDialogCfg(). setDetailInstructions(errorMessage), @@ -158,7 +158,7 @@ ProcessCallback::Response StatusHandlerTemporaryPanel::reportError(const std::ws 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..."), MSG_TYPE_INFO); //explain why there are duplicate "doing operation X" info messages in the log! return ProcessCallback::RETRY; case ConfirmationButton3::CANCEL: @@ -176,23 +176,23 @@ ProcessCallback::Response StatusHandlerTemporaryPanel::reportError(const std::ws void StatusHandlerTemporaryPanel::reportFatalError(const std::wstring& errorMessage) { - errorLog_.logMsg(errorMessage, TYPE_FATAL_ERROR); + errorLog_.logMsg(errorMessage, MSG_TYPE_FATAL_ERROR); - forceUiRefresh(); + forceUiRefreshNoThrow(); //noexcept! => don't throw here when error occurs during clean up! 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, MSG_TYPE_WARNING); if (!warningActive) //if errors are ignored, then warnings should also return; if (!mainDlg_.compareStatus_->getOptionIgnoreErrors()) { - forceUiRefresh(); + forceUiRefreshNoThrow(); //noexcept! => don't throw here when error occurs during clean up! bool dontWarnAgain = false; switch (showConfirmationDialog(&mainDlg_, DialogInfoType::WARNING, @@ -212,7 +212,7 @@ void StatusHandlerTemporaryPanel::reportWarning(const std::wstring& warningMessa } -void StatusHandlerTemporaryPanel::forceUiRefresh() +void StatusHandlerTemporaryPanel::forceUiRefreshNoThrow() { mainDlg_.compareStatus_->updateGui(); } @@ -226,6 +226,7 @@ void StatusHandlerTemporaryPanel::OnAbortCompare(wxCommandEvent& event) //######################################################################################################## StatusHandlerFloatingDialog::StatusHandlerFloatingDialog(wxFrame* parentDlg, + const std::chrono::system_clock::time_point& startTime, size_t lastSyncsLogFileSizeMax, bool ignoreErrors, size_t automaticRetryCount, @@ -234,23 +235,19 @@ StatusHandlerFloatingDialog::StatusHandlerFloatingDialog(wxFrame* parentDlg, const Zstring& soundFileSyncComplete, const Zstring& postSyncCommand, PostSyncCondition postSyncCondition, - bool& exitAfterSync) : - progressDlg_(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, -*this, -parentDlg, -true, /*showProgress*/ -jobName, -soundFileSyncComplete, -ignoreErrors, -PostSyncAction::SUMMARY)), - lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax), - automaticRetryCount_(automaticRetryCount), - automaticRetryDelay_(automaticRetryDelay), - jobName_(jobName), - startTime_(std::time(nullptr)), - postSyncCommand_(postSyncCommand), - postSyncCondition_(postSyncCondition), - exitAfterSync_(exitAfterSync) + bool& exitAfterSync, + bool& autoCloseDialog) : + progressDlg_(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, parentDlg, true /*showProgress*/, autoCloseDialog, +jobName, soundFileSyncComplete, ignoreErrors, PostSyncAction2::NONE)), + lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax), + automaticRetryCount_(automaticRetryCount), + automaticRetryDelay_(automaticRetryDelay), + jobName_(jobName), + startTime_(startTime), + postSyncCommand_(postSyncCommand), + postSyncCondition_(postSyncCondition), + exitAfterSync_(exitAfterSync), + autoCloseDialogOut_(autoCloseDialog) { assert(!exitAfterSync); } @@ -258,8 +255,8 @@ PostSyncAction::SUMMARY)), StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog() { - const int totalErrors = errorLog_.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log - const int totalWarnings = errorLog_.getItemCount(TYPE_WARNING); + const int totalErrors = errorLog_.getItemCount(MSG_TYPE_ERROR | MSG_TYPE_FATAL_ERROR); //evaluate before finalizing log + const int totalWarnings = errorLog_.getItemCount(MSG_TYPE_WARNING); //finalize error log SyncProgressDialog::SyncResult finalStatus = SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS; @@ -267,20 +264,20 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog() if (getAbortStatus()) { finalStatus = SyncProgressDialog::RESULT_ABORTED; - finalStatusMsg = _("Synchronization stopped"); - errorLog_.logMsg(finalStatusMsg, TYPE_ERROR); + finalStatusMsg = _("Stopped"); + errorLog_.logMsg(finalStatusMsg, MSG_TYPE_ERROR); } else if (totalErrors > 0) { finalStatus = SyncProgressDialog::RESULT_FINISHED_WITH_ERROR; - finalStatusMsg = _("Synchronization completed with errors"); - errorLog_.logMsg(finalStatusMsg, TYPE_ERROR); + finalStatusMsg = _("Completed with errors"); + errorLog_.logMsg(finalStatusMsg, MSG_TYPE_ERROR); } else if (totalWarnings > 0) { finalStatus = SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS; - finalStatusMsg = _("Synchronization completed with warnings"); - errorLog_.logMsg(finalStatusMsg, TYPE_WARNING); //give status code same warning priority as display category! + finalStatusMsg = _("Completed with warnings"); + errorLog_.logMsg(finalStatusMsg, MSG_TYPE_WARNING); //give status code same warning priority as display category! } else { @@ -288,14 +285,16 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog() getBytesTotal(PHASE_SYNCHRONIZING) == 0) finalStatusMsg = _("Nothing to synchronize"); //even if "ignored conflicts" occurred! else - finalStatusMsg = _("Synchronization completed successfully"); - errorLog_.logMsg(finalStatusMsg, TYPE_INFO); + finalStatusMsg = _("Completed"); + errorLog_.logMsg(finalStatusMsg, MSG_TYPE_INFO); } //post sync command Zstring commandLine = [&] { - if (!getAbortStatus() || *getAbortStatus() != AbortTrigger::USER) //user cancelled => don't run post sync command! + if (getAbortStatus() && *getAbortStatus() == AbortTrigger::USER) + ; //user cancelled => don't run post sync command! + else switch (postSyncCondition_) { case PostSyncCondition::COMPLETION: @@ -316,7 +315,7 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog() trim(commandLine); if (!commandLine.empty()) - errorLog_.logMsg(replaceCpy(_("Executing command %x"), L"%x", fmtPath(commandLine)), TYPE_INFO); + errorLog_.logMsg(replaceCpy(_("Executing command %x"), L"%x", fmtPath(commandLine)), MSG_TYPE_INFO); //----------------- write results into LastSyncs.log------------------------ const SummaryInfo summary = @@ -324,72 +323,103 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog() jobName_, finalStatusMsg, getItemsCurrent(PHASE_SYNCHRONIZING), getBytesCurrent(PHASE_SYNCHRONIZING), getItemsTotal (PHASE_SYNCHRONIZING), getBytesTotal (PHASE_SYNCHRONIZING), - std::time(nullptr) - startTime_ + std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - startTime_).count() + }; + if (progressDlg_) progressDlg_->timerSetStatus(false /*active*/); //keep correct summary window stats considering count down timer, system sleep + + //do NOT use tryReportingError()! saving log files should not be cancellable! + auto notifyStatusNoThrow = [&](const std::wstring& msg) + { + try { reportStatus(msg); /*throw X*/ } + catch (...) {} }; try { - saveToLastSyncsLog(summary, errorLog_, lastSyncsLogFileSizeMax_, OnUpdateLogfileStatusNoThrow(*this, utfTo<std::wstring>(getLastSyncsLogfilePath()))); //throw FileError + saveToLastSyncsLog(summary, errorLog_, lastSyncsLogFileSizeMax_, notifyStatusNoThrow); //throw FileError, (X) } - catch (FileError&) { assert(false); } + catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); } //execute post sync command *after* writing log files, so that user can refer to the log via the command! if (!commandLine.empty()) try { - //use EXEC_TYPE_ASYNC until there is reason not to: https://www.freefilesync.org/forum/viewtopic.php?t=31 - tryReportingError([&] { shellExecute(expandMacros(commandLine), EXEC_TYPE_ASYNC); /*throw FileError*/ }, *this); //throw X + //use ExecutionType::ASYNC until there is reason not to: https://www.freefilesync.org/forum/viewtopic.php?t=31 + shellExecute(expandMacros(commandLine), ExecutionType::ASYNC); //throw FileError } - catch (...) {} + catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); } if (progressDlg_) { + auto mayRunAfterCountDown = [&](const std::wstring& operationName) + { + auto notifyStatusThrowOnCancel = [&](const std::wstring& msg) + { + try { reportStatus(msg); /*throw X*/ } + catch (...) + { + if (getAbortStatus() && *getAbortStatus() == AbortTrigger::USER) + throw; + } + }; + + if (progressDlg_->getWindowIfVisible()) + try + { + delayAndCountDown(operationName, 5 /*delayInSec*/, notifyStatusThrowOnCancel); //throw X + } + catch (...) { return false; } + + return true; + }; + //post sync action - bool showSummary = true; - bool triggerSleep = false; - if (!getAbortStatus() || *getAbortStatus() != AbortTrigger::USER) //user cancelled => don't run post sync action! + bool autoClose = false; + if (getAbortStatus() && *getAbortStatus() == AbortTrigger::USER) + ; //user cancelled => don't run post sync command! + else switch (progressDlg_->getOptionPostSyncAction()) { - case PostSyncAction::SUMMARY: + case PostSyncAction2::NONE: + autoClose = progressDlg_->getOptionAutoCloseDialog(); break; - case PostSyncAction::EXIT: - showSummary = false; - exitAfterSync_ = true; //program shutdown must be handled by calling context! + case PostSyncAction2::EXIT: + autoClose = exitAfterSync_ = true; //program exit must be handled by calling context! break; - case PostSyncAction::SLEEP: - triggerSleep = true; + case PostSyncAction2::SLEEP: + if (mayRunAfterCountDown(_("System: Sleep"))) + try + { + suspendSystem(); //throw FileError + autoClose = progressDlg_->getOptionAutoCloseDialog(); + } + catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); } break; - case PostSyncAction::SHUTDOWN: - showSummary = false; - exitAfterSync_ = true; - try - { - tryReportingError([&] { shutdownSystem(); /*throw FileError*/ }, *this); //throw X - } - catch (...) {} + case PostSyncAction2::SHUTDOWN: + if (mayRunAfterCountDown(_("System: Shut down"))) + try + { + shutdownSystem(); //throw FileError + autoClose = exitAfterSync_ = true; + } + catch (const FileError& e) { errorLog_.logMsg(e.toString(), MSG_TYPE_ERROR); } break; } //close progress dialog - if (showSummary) - progressDlg_->showSummary(finalStatus, errorLog_); + if (autoClose) + progressDlg_->closeDirectly(!exitAfterSync_ /*restoreParentFrame*/); else - progressDlg_->closeDirectly(false /*restoreParentFrame*/); - - if (triggerSleep) //sleep *after* showing results dialog (consider total time!) - try - { - tryReportingError([&] { suspendSystem(); /*throw FileError*/ }, *this); //throw X - } - catch (...) {} + progressDlg_->showSummary(finalStatus, errorLog_); //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_) + for (;;) { wxTheApp->Yield(); //*first* refresh GUI (removing flicker) before sleeping! - std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS)); + if (!progressDlg_) break; + std::this_thread::sleep_for(UI_UPDATE_INTERVAL); } } } @@ -402,7 +432,7 @@ void StatusHandlerFloatingDialog::initNewPhase(int itemsTotal, int64_t bytesTota 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 + forceUiRefresh(); //throw X; OS X needs a full yield to update GUI and get rid of "dummy" texts } @@ -417,7 +447,7 @@ void StatusHandlerFloatingDialog::updateProcessedData(int itemsDelta, int64_t by void StatusHandlerFloatingDialog::reportInfo(const std::wstring& text) { - errorLog_.logMsg(text, TYPE_INFO); //log first! + errorLog_.logMsg(text, MSG_TYPE_INFO); //log first! StatusHandler::reportInfo(text); //throw X } @@ -429,26 +459,18 @@ ProcessCallback::Response StatusHandlerFloatingDialog::reportError(const std::ws //auto-retry if (retryNumber < automaticRetryCount_) { - errorLog_.logMsg(errorMessage + L"\n-> " + - _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", automaticRetryDelay_), TYPE_INFO); - //delay - const int iterations = static_cast<int>(1000 * automaticRetryDelay_ / UI_UPDATE_INTERVAL_MS); //always round down: don't allow for negative remaining time below - for (int i = 0; i < iterations; ++i) - { - reportStatus(_("Error") + L": " + _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", - (1000 * automaticRetryDelay_ - i * UI_UPDATE_INTERVAL_MS + 999) / 1000)); //integer round up - std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS)); - } + errorLog_.logMsg(errorMessage + L"\n-> " + _("Automatic retry"), MSG_TYPE_INFO); + delayAndCountDown(_("Automatic retry"), automaticRetryDelay_, [&](const std::wstring& msg) { this->reportStatus(_("Error") + L": " + msg); }); return ProcessCallback::RETRY; } //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, MSG_TYPE_ERROR); }); if (!progressDlg_->getOptionIgnoreErrors()) { PauseTimers dummy(*progressDlg_); - forceUiRefresh(); + forceUiRefreshNoThrow(); //noexcept! => don't throw here when error occurs during clean up! switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg(). setDetailInstructions(errorMessage), @@ -463,7 +485,7 @@ ProcessCallback::Response StatusHandlerFloatingDialog::reportError(const std::ws 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..."), MSG_TYPE_INFO); //explain why there are duplicate "doing operation X" info messages in the log! return ProcessCallback::RETRY; case ConfirmationButton3::CANCEL: @@ -483,12 +505,12 @@ void StatusHandlerFloatingDialog::reportFatalError(const std::wstring& errorMess { if (!progressDlg_) abortProcessNow(); - errorLog_.logMsg(errorMessage, TYPE_FATAL_ERROR); + errorLog_.logMsg(errorMessage, MSG_TYPE_FATAL_ERROR); if (!progressDlg_->getOptionIgnoreErrors()) { PauseTimers dummy(*progressDlg_); - forceUiRefresh(); + forceUiRefreshNoThrow(); //noexcept! => don't throw here when error occurs during clean up! switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg().setTitle(_("Serious Error")). @@ -514,7 +536,7 @@ void StatusHandlerFloatingDialog::reportWarning(const std::wstring& warningMessa { if (!progressDlg_) abortProcessNow(); - errorLog_.logMsg(warningMessage, TYPE_WARNING); + errorLog_.logMsg(warningMessage, MSG_TYPE_WARNING); if (!warningActive) return; @@ -522,7 +544,7 @@ void StatusHandlerFloatingDialog::reportWarning(const std::wstring& warningMessa if (!progressDlg_->getOptionIgnoreErrors()) { PauseTimers dummy(*progressDlg_); - forceUiRefresh(); + forceUiRefreshNoThrow(); //noexcept! => don't throw here when error occurs during clean up! bool dontWarnAgain = false; switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::WARNING, @@ -542,7 +564,7 @@ void StatusHandlerFloatingDialog::reportWarning(const std::wstring& warningMessa } -void StatusHandlerFloatingDialog::forceUiRefresh() +void StatusHandlerFloatingDialog::forceUiRefreshNoThrow() { if (progressDlg_) progressDlg_->updateGui(); @@ -551,5 +573,9 @@ void StatusHandlerFloatingDialog::forceUiRefresh() void StatusHandlerFloatingDialog::onProgressDialogTerminate() { + //output parameters owned by SyncProgressDialog + if (progressDlg_) + autoCloseDialogOut_ = progressDlg_->getOptionAutoCloseDialog(); + progressDlg_ = nullptr; } diff --git a/FreeFileSync/Source/ui/gui_status_handler.h b/FreeFileSync/Source/ui/gui_status_handler.h index e72ae864..c9d9eef3 100755 --- a/FreeFileSync/Source/ui/gui_status_handler.h +++ b/FreeFileSync/Source/ui/gui_status_handler.h @@ -12,13 +12,14 @@ #include "progress_indicator.h" #include "main_dlg.h" #include "../lib/status_handler.h" -//#include "../lib/process_xml.h" +namespace fff +{ //classes handling sync and compare errors as well as status feedback //StatusHandlerTemporaryPanel(CompareProgressDialog) will internally process Window messages! disable GUI controls to avoid unexpected callbacks! -class StatusHandlerTemporaryPanel : private wxEvtHandler, public zen::StatusHandler //throw AbortProcess +class StatusHandlerTemporaryPanel : private wxEvtHandler, public StatusHandler //throw AbortProcess { public: StatusHandlerTemporaryPanel(MainDialog& dlg); @@ -31,7 +32,7 @@ public: void reportFatalError(const std::wstring& errorMessage) override; void reportWarning (const std::wstring& warningMessage, bool& warningActive) override; - void forceUiRefresh() override; + void forceUiRefreshNoThrow() override; zen::ErrorLog getErrorLog() const { return errorLog_; } @@ -45,10 +46,11 @@ private: //StatusHandlerFloatingDialog(SyncProgressDialog) will internally process Window messages! disable GUI controls to avoid unexpected callbacks! -class StatusHandlerFloatingDialog : public zen::StatusHandler //throw AbortProcess +class StatusHandlerFloatingDialog : public StatusHandler //throw AbortProcess { public: StatusHandlerFloatingDialog(wxFrame* parentDlg, + const std::chrono::system_clock::time_point& startTime, size_t lastSyncsLogFileSizeMax, bool ignoreErrors, size_t automaticRetryCount, @@ -56,8 +58,9 @@ public: const std::wstring& jobName, const Zstring& soundFileSyncComplete, const Zstring& postSyncCommand, - zen::PostSyncCondition postSyncCondition, - bool& exitAfterSync); + PostSyncCondition postSyncCondition, + bool& exitAfterSync, + bool& autoCloseDialog); ~StatusHandlerFloatingDialog(); void initNewPhase (int itemsTotal, int64_t bytesTotal, Phase phaseID) override; @@ -68,7 +71,7 @@ public: void reportFatalError(const std::wstring& errorMessage ) override; void reportWarning (const std::wstring& warningMessage, bool& warningActive) override; - void forceUiRefresh() override; + void forceUiRefreshNoThrow() override; private: void onProgressDialogTerminate(); @@ -79,11 +82,12 @@ private: const size_t automaticRetryCount_; const size_t automaticRetryDelay_; const std::wstring jobName_; - const time_t startTime_; //don't use wxStopWatch: may overflow after a few days due to ::QueryPerformanceCounter() + const std::chrono::system_clock::time_point startTime_; //don't use wxStopWatch: may overflow after a few days due to ::QueryPerformanceCounter() const Zstring postSyncCommand_; - const zen::PostSyncCondition postSyncCondition_; + const PostSyncCondition postSyncCondition_; bool& exitAfterSync_; + bool& autoCloseDialogOut_; //owned by SyncProgressDialog }; - +} #endif //GUI_STATUS_HANDLER_H_0183247018545 diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp index 41ccf802..b20b4b08 100755 --- a/FreeFileSync/Source/ui/main_dlg.cpp +++ b/FreeFileSync/Source/ui/main_dlg.cpp @@ -49,17 +49,18 @@ using namespace zen; -using namespace std::rel_ops; +using namespace fff; + namespace { const size_t EXT_APP_MASS_INVOKE_THRESHOLD = 10; //more than this is likely a user mistake (Explorer uses limit of 15) +const int TOP_BUTTON_OPTIMAL_WIDTH = 180; -IconBuffer::IconSize convert(xmlAccess::FileIconSize isize) +IconBuffer::IconSize convert(FileIconSize isize) { - using namespace xmlAccess; switch (isize) { case ICON_SIZE_SMALL: @@ -85,7 +86,7 @@ bool acceptDialogFileDrop(const std::vector<Zstring>& shellItemPaths) } -class FolderSelectorImpl : public FolderSelector +class fff::FolderSelectorImpl : public FolderSelector { public: FolderSelectorImpl(MainDialog& mainDlg, @@ -132,7 +133,7 @@ private: */ template <class GuiPanel> -class FolderPairCallback : public FolderPairPanelBasic<GuiPanel> //implements callback functionality to MainDialog as imposed by FolderPairPanelBasic +class fff::FolderPairCallback : public FolderPairPanelBasic<GuiPanel> //implements callback functionality to MainDialog as imposed by FolderPairPanelBasic { public: FolderPairCallback(GuiPanel& basicPanel, MainDialog& mainDialog) : @@ -152,7 +153,7 @@ private: }; -class FolderPairPanel : +class fff::FolderPairPanel : public FolderPairPanelGenerated, //FolderPairPanel "owns" FolderPairPanelGenerated! public FolderPairCallback<FolderPairPanelGenerated> { @@ -191,7 +192,7 @@ private: }; -class FolderPairFirst : public FolderPairCallback<MainDialogGenerated> +class fff::FolderPairFirst : public FolderPairCallback<MainDialogGenerated> { public: FolderPairFirst(MainDialog& mainDialog) : @@ -247,9 +248,6 @@ private: namespace { -const int TOP_BUTTON_OPTIMAL_WIDTH = 180; - - void updateTopButton(wxBitmapButton& btn, const wxBitmap& bmp, const wxString& variantName, bool makeGrey) { wxImage labelImage = createImageFromText(btn.GetLabel(), btn.GetFont(), wxSystemSettings::GetColour(makeGrey ? wxSYS_COLOUR_GRAYTEXT : wxSYS_COLOUR_BTNTEXT)); @@ -274,10 +272,8 @@ void updateTopButton(wxBitmapButton& btn, const wxBitmap& bmp, const wxString& v //################################################################################################################################## -xmlAccess::XmlGlobalSettings tryLoadGlobalConfig(const Zstring& globalConfigFilePath) //blocks on GUI on errors! +XmlGlobalSettings tryLoadGlobalConfig(const Zstring& globalConfigFilePath) //blocks on GUI on errors! { - using namespace xmlAccess; - XmlGlobalSettings globalCfg; try { @@ -298,8 +294,6 @@ xmlAccess::XmlGlobalSettings tryLoadGlobalConfig(const Zstring& globalConfigFile void MainDialog::create(const Zstring& globalConfigFilePath) { - using namespace xmlAccess; - const XmlGlobalSettings globalSettings = tryLoadGlobalConfig(globalConfigFilePath); std::vector<Zstring> cfgFilePaths = globalSettings.gui.mainDlg.lastUsedConfigFiles; @@ -332,18 +326,16 @@ void MainDialog::create(const Zstring& globalConfigFilePath) //else: not-existing/access error? => user may click on <Last Session> later } - XmlGuiConfig guiCfg; //structure to receive gui settings with default values + XmlGuiConfig guiCfg; //contains default values - if (cfgFilePaths.empty()) - { - //add default exclusion filter: this is only ever relevant when creating new configurations! - //a default XmlGuiConfig does not need these user-specific exclusions! - Zstring& excludeFilter = guiCfg.mainCfg.globalFilter.excludeFilter; - if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr("\n"))) - excludeFilter += Zstr("\n"); - excludeFilter += globalSettings.gui.defaultExclusionFilter; - } - else + //add default exclusion filter: this is only ever relevant when creating new configurations! + //a default XmlGuiConfig does not need these user-specific exclusions! + Zstring& excludeFilter = guiCfg.mainCfg.globalFilter.excludeFilter; + if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr("\n"))) + excludeFilter += Zstr("\n"); + excludeFilter += globalSettings.gui.defaultExclusionFilter; + + if (!cfgFilePaths.empty()) try { std::wstring warningMsg; @@ -365,12 +357,12 @@ void MainDialog::create(const Zstring& globalConfigFilePath) void MainDialog::create(const Zstring& globalConfigFilePath, - const xmlAccess::XmlGlobalSettings* globalSettings, - const xmlAccess::XmlGuiConfig& guiCfg, + const XmlGlobalSettings* globalSettings, + const XmlGuiConfig& guiCfg, const std::vector<Zstring>& referenceFiles, bool startComparison) { - const xmlAccess::XmlGlobalSettings globSett = globalSettings ? *globalSettings : tryLoadGlobalConfig(globalConfigFilePath); + const XmlGlobalSettings globSett = globalSettings ? *globalSettings : tryLoadGlobalConfig(globalConfigFilePath); try { @@ -389,9 +381,9 @@ void MainDialog::create(const Zstring& globalConfigFilePath, MainDialog::MainDialog(const Zstring& globalConfigFilePath, - const xmlAccess::XmlGuiConfig& guiCfg, + const XmlGuiConfig& guiCfg, const std::vector<Zstring>& referenceFiles, - const xmlAccess::XmlGlobalSettings& globalSettings, + const XmlGlobalSettings& globalSettings, bool startComparison) : MainDialogGenerated(nullptr), globalConfigFilePath_(globalConfigFilePath), @@ -637,7 +629,7 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath, } //notify about (logical) application main window => program won't quit, but stay on this dialog - zen::setMainWindow(this); + setMainWindow(this); //init handling of first folder pair firstFolderPair_ = std::make_unique<FolderPairFirst>(*this); @@ -715,7 +707,7 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath, //some convenience: if FFS is started with a *.ffs_gui file as commandline parameter AND all directories contained exist, comparison shall be started right away if (startComparison) { - const zen::MainConfiguration currMainCfg = getConfig().mainCfg; + const MainConfiguration currMainCfg = getConfig().mainCfg; //------------------------------------------------------------------------------------------ //harmonize checks with comparison.cpp:: checkForIncompleteInput() @@ -779,8 +771,6 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath, MainDialog::~MainDialog() { - using namespace xmlAccess; - Opt<FileError> firstError; try //save "GlobalSettings.xml" { @@ -814,8 +804,6 @@ MainDialog::~MainDialog() void MainDialog::onQueryEndSession() { - using namespace xmlAccess; - //we try our best to do something useful in this extreme situation - no reason to notify or even log errors here! try { writeConfig(getGlobalCfgBeforeExit(), globalConfigFilePath_); } catch (const FileError&) {} @@ -853,7 +841,7 @@ void MainDialog::OnClose(wxCloseEvent& event) } -void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSettings) +void MainDialog::setGlobalCfgOnInit(const XmlGlobalSettings& globalSettings) { globalCfg_ = globalSettings; @@ -914,28 +902,27 @@ void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSe //load list of configuration files std::vector<Zstring> cfgFilePaths; std::vector<std::pair<Zstring, time_t>> lastSyncTimes; - //list is stored with last used files first in XML, however m_gridCfgHistory needs them last!!! + //list is stored with last used files first in XML, however m_gridCfgHistory expects them last!!! std::for_each(globalSettings.gui.mainDlg.cfgFileHistory.crbegin(), globalSettings.gui.mainDlg.cfgFileHistory.crend(), - [&](const xmlAccess::ConfigFileItem& item) + [&](const ConfigFileItem& item) { cfgFilePaths.push_back(item.filePath); lastSyncTimes.emplace_back(item.filePath, item.lastSyncTime); }); - warn_static("finish") cfgFilePaths.push_back(lastRunConfigPath_); //make sure <Last session> is always part of history list (if existing) + cfggrid::getDataView(*m_gridCfgHistory).addCfgFiles(cfgFilePaths); cfggrid::getDataView(*m_gridCfgHistory).setLastSyncTime(lastSyncTimes); m_gridCfgHistory->Refresh(); + //globalSettings.gui.mainDlg.cfgGridTopRowPos => defer evaluation until later within MainDialog constructor m_gridCfgHistory->setColumnConfig(convertColAttributes(globalSettings.gui.mainDlg.cfgGridColumnAttribs, getCfgGridDefaultColAttribs())); cfggrid::getDataView(*m_gridCfgHistory).setSortDirection(globalSettings.gui.mainDlg.cfgGridLastSortColumn, globalSettings.gui.mainDlg.cfgGridLastSortAscending); cfggrid::setSyncOverdueDays(*m_gridCfgHistory, globalSettings.gui.mainDlg.cfgGridSyncOverdueDays); //m_gridCfgHistory->Refresh(); <- implicit in last call cfgHistoryRemoveObsolete(cfgFilePaths); //remove non-existent items (we need this only on startup) - - //globalSettings.gui.cfgFileHistFirstItemPos => defer evaluation until later within MainDialog constructor //-------------------------------------------------------------------------------- //load list of last used folders @@ -975,11 +962,11 @@ void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSe } -xmlAccess::XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit() +XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit() { Freeze(); //no need to Thaw() again!! - xmlAccess::XmlGlobalSettings globalSettings = globalCfg_; + XmlGlobalSettings globalSettings = globalCfg_; globalSettings.programLanguage = getLanguage(); @@ -996,18 +983,16 @@ xmlAccess::XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit() //-------------------------------------------------------------------------------- //write list of configuration files - std::map<int, xmlAccess::ConfigFileItem> cfgItemsSorted; //(last use index/cfg file path) + std::map<int, ConfigFileItem, std::greater<>> cfgItemsSorted; //sort by last use; put most recent items *first* (looks better in XML than reverted) for (size_t i = 0; i < m_gridCfgHistory->getRowCount(); ++i) if (const ConfigView::Details* cfg = cfggrid::getDataView(*m_gridCfgHistory).getItem(i)) - cfgItemsSorted.emplace(cfg->lastUseIndex, xmlAccess::ConfigFileItem{ cfg->filePath, cfg->lastSyncTime }); + cfgItemsSorted.emplace(cfg->lastUseIndex, ConfigFileItem{ cfg->filePath, cfg->lastSyncTime }); else assert(false); - //sort by last use; put most recent items *first* (looks better in XML than reverted) - std::vector<xmlAccess::ConfigFileItem> cfgHistory; - std::for_each(cfgItemsSorted.crbegin(), - cfgItemsSorted.crend(), - [&](const auto& item) { cfgHistory.emplace_back(item.second); }); + std::vector<ConfigFileItem> cfgHistory; + for (const auto& item : cfgItemsSorted) + cfgHistory.emplace_back(item.second); if (cfgHistory.size() > globalSettings.gui.mainDlg.cfgHistItemsMax) //erase oldest elements cfgHistory.resize(globalSettings.gui.mainDlg.cfgHistItemsMax); @@ -1066,7 +1051,7 @@ void MainDialog::setSyncDirManually(const std::vector<FileSystemObject*>& select for (FileSystemObject* fsObj : selection) { setSyncDirectionRec(direction, *fsObj); //set new direction (recursively) - zen::setActiveStatus(true, *fsObj); //works recursively for directories + setActiveStatus(true, *fsObj); //works recursively for directories } updateGui(); } @@ -1081,7 +1066,7 @@ void MainDialog::setFilterManually(const std::vector<FileSystemObject*>& selecti if (!selection.empty()) { for (FileSystemObject* fsObj : selection) - zen::setActiveStatus(setIncluded, *fsObj); //works recursively for directories + setActiveStatus(setIncluded, *fsObj); //works recursively for directories updateGuiDelayedIf(!m_bpButtonShowExcluded->isActive()); //show update GUI before removing rows } @@ -1188,8 +1173,8 @@ std::vector<FileSystemObject*> MainDialog::getTreeSelection() const } -void MainDialog::copyToAlternateFolder(const std::vector<zen::FileSystemObject*>& selectionLeft, - const std::vector<zen::FileSystemObject*>& selectionRight) +void MainDialog::copyToAlternateFolder(const std::vector<FileSystemObject*>& selectionLeft, + const std::vector<FileSystemObject*>& selectionRight) { std::vector<const FileSystemObject*> rowsLeftTmp; std::vector<const FileSystemObject*> rowsRightTmp; @@ -1201,13 +1186,13 @@ void MainDialog::copyToAlternateFolder(const std::vector<zen::FileSystemObject*> FocusPreserver fp; - if (zen::showCopyToDialog(this, - rowsLeftTmp, rowsRightTmp, - globalCfg_.gui.mainDlg.copyToCfg.lastUsedPath, - globalCfg_.gui.mainDlg.copyToCfg.folderHistory, - globalCfg_.gui.mainDlg.copyToCfg.historySizeMax, - globalCfg_.gui.mainDlg.copyToCfg.keepRelPaths, - globalCfg_.gui.mainDlg.copyToCfg.overwriteIfExists) != ReturnSmallDlg::BUTTON_OKAY) + if (showCopyToDialog(this, + rowsLeftTmp, rowsRightTmp, + globalCfg_.gui.mainDlg.copyToCfg.lastUsedPath, + globalCfg_.gui.mainDlg.copyToCfg.folderHistory, + globalCfg_.gui.mainDlg.copyToCfg.historySizeMax, + globalCfg_.gui.mainDlg.copyToCfg.keepRelPaths, + globalCfg_.gui.mainDlg.copyToCfg.overwriteIfExists) != ReturnSmallDlg::BUTTON_OKAY) return; disableAllElements(true); //StatusHandlerTemporaryPanel will internally process Window messages, so avoid unexpected callbacks! @@ -1218,11 +1203,11 @@ void MainDialog::copyToAlternateFolder(const std::vector<zen::FileSystemObject*> { StatusHandlerTemporaryPanel statusHandler(*this); //handle status display and error messages - zen::copyToAlternateFolder(rowsLeftTmp, rowsRightTmp, + fff::copyToAlternateFolder(rowsLeftTmp, rowsRightTmp, globalCfg_.gui.mainDlg.copyToCfg.lastUsedPath, globalCfg_.gui.mainDlg.copyToCfg.keepRelPaths, globalCfg_.gui.mainDlg.copyToCfg.overwriteIfExists, - globalCfg_.optDialogs, + globalCfg_.warnDlgs, statusHandler); //"clearSelection" not needed/desired } @@ -1245,8 +1230,8 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec FocusPreserver fp; //sigh: do senseless vector<FileSystemObject*> -> vector<const FileSystemObject*> conversion: - if (zen::showDeleteDialog(this, { rowsLeftTmp.begin(), rowsLeftTmp.end() }, { rowsRightTmp.begin(), rowsRightTmp.end() }, - moveToRecycler) != ReturnSmallDlg::BUTTON_OKAY) + if (showDeleteDialog(this, { rowsLeftTmp.begin(), rowsLeftTmp.end() }, { rowsRightTmp.begin(), rowsRightTmp.end() }, + moveToRecycler) != ReturnSmallDlg::BUTTON_OKAY) return; disableAllElements(true); //StatusHandlerTemporaryPanel will internally process Window messages, so avoid unexpected callbacks! @@ -1258,12 +1243,12 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec { StatusHandlerTemporaryPanel statusHandler(*this); //handle status display and error messages - zen::deleteFromGridAndHD(rowsLeftTmp, rowsRightTmp, - folderCmp_, - extractDirectionCfg(getConfig().mainCfg), - moveToRecycler, - globalCfg_.optDialogs.warnRecyclerMissing, - statusHandler); + deleteFromGridAndHD(rowsLeftTmp, rowsRightTmp, + folderCmp_, + extractDirectionCfg(getConfig().mainCfg), + moveToRecycler, + globalCfg_.warnDlgs.warnRecyclerMissing, + statusHandler); m_gridMainL->clearSelection(ALLOW_GRID_EVENT); m_gridMainC->clearSelection(ALLOW_GRID_EVENT); @@ -1369,7 +1354,7 @@ void invokeCommandLine(const Zstring& commandLinePhrase, //throw FileError replace(command, Zstr("%local_path%"), localPath); replace(command, Zstr("%local_path2%"), localPath2); - shellExecute(command, selection.size() > EXT_APP_MASS_INVOKE_THRESHOLD ? EXEC_TYPE_SYNC : EXEC_TYPE_ASYNC); //throw FileError + shellExecute(command, selection.size() > EXT_APP_MASS_INVOKE_THRESHOLD ? ExecutionType::SYNC : ExecutionType::ASYNC); //throw FileError } } } @@ -1379,8 +1364,8 @@ void MainDialog::openExternalApplication(const Zstring& commandLinePhrase, bool const std::vector<FileSystemObject*>& selectionLeft, const std::vector<FileSystemObject*>& selectionRight) { - const xmlAccess::XmlGlobalSettings::Gui defaultCfg; - const bool openFileBrowserRequested = !defaultCfg.externelApplications.empty() && defaultCfg.externelApplications[0].second == commandLinePhrase; + const XmlGlobalSettings::Gui defaultCfg; + const bool openFileBrowserRequested = !defaultCfg.externalApps.empty() && defaultCfg.externalApps[0].cmdLine == commandLinePhrase; //support fallback instead of an error in this special case if (openFileBrowserRequested) @@ -1398,7 +1383,7 @@ void MainDialog::openExternalApplication(const Zstring& commandLinePhrase, bool { try { - shellExecute("xdg-open \"" + utfTo<Zstring>(AFS::getDisplayPath(folderPath)) + "\"", EXEC_TYPE_ASYNC); // + shellExecute("xdg-open \"" + utfTo<Zstring>(AFS::getDisplayPath(folderPath)) + "\"", ExecutionType::ASYNC); // } catch (const FileError& e) { showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); } }; @@ -1423,7 +1408,7 @@ void MainDialog::openExternalApplication(const Zstring& commandLinePhrase, bool //regular command evaluation: const size_t invokeCount = selectionLeft.size() + selectionRight.size(); if (invokeCount > EXT_APP_MASS_INVOKE_THRESHOLD) - if (globalCfg_.optDialogs.confirmExternalCommandMassInvoke) + if (globalCfg_.confirmDlgs.confirmExternalCommandMassInvoke) { bool dontAskAgain = false; switch (showConfirmationDialog(this, DialogInfoType::WARNING, PopupDialogCfg(). @@ -1435,7 +1420,7 @@ void MainDialog::openExternalApplication(const Zstring& commandLinePhrase, bool _("&Execute"))) { case ConfirmationButton::ACCEPT: - globalCfg_.optDialogs.confirmExternalCommandMassInvoke = !dontAskAgain; + globalCfg_.confirmDlgs.confirmExternalCommandMassInvoke = !dontAskAgain; break; case ConfirmationButton::CANCEL: return; @@ -1875,9 +1860,9 @@ void MainDialog::onGridButtonEvent(wxKeyEvent& event, Grid& grid, bool leftSide) return static_cast<size_t>(-1); }(); - if (extAppPos < globalCfg_.gui.externelApplications.size()) + if (extAppPos < globalCfg_.gui.externalApps.size()) { - openExternalApplication(globalCfg_.gui.externelApplications[extAppPos].second, leftSide, selectionLeft, selectionRight); + openExternalApplication(globalCfg_.gui.externalApps[extAppPos].cmdLine, leftSide, selectionLeft, selectionRight); return; } @@ -2064,7 +2049,7 @@ void MainDialog::onTreeGridContext(GridClickEvent& event) //---------------------------------------------------------------------------------------------------- if (!selection.empty()) - //std::any_of(selection.begin(), selection.end(), [](const FileSystemObject* fsObj){ return fsObj->getSyncOperation() != SO_EQUAL; })) -> doesn't consider directories + //std::any_of(selection.begin(), selection.end(), [](const FileSystemObject* fsObj) { return fsObj->getSyncOperation() != SO_EQUAL; })) -> doesn't consider directories { auto getImage = [&](SyncDirection dir, SyncOperation soDefault) { @@ -2156,13 +2141,13 @@ void MainDialog::onMainGridContextC(GridClickEvent& event) menu.addItem(_("Include all"), [&] { - zen::setActiveStatus(true, folderCmp_); + setActiveStatus(true, folderCmp_); updateGui(); }, nullptr, filegrid::getDataView(*m_gridMainC).rowsTotal() > 0); menu.addItem(_("Exclude all"), [&] { - zen::setActiveStatus(false, folderCmp_); + setActiveStatus(false, folderCmp_); updateGuiDelayedIf(!m_bpButtonShowExcluded->isActive()); //show update GUI before removing rows }, nullptr, filegrid::getDataView(*m_gridMainC).rowsTotal() > 0); @@ -2271,22 +2256,22 @@ void MainDialog::onMainGridContextRim(bool leftSide) //---------------------------------------------------------------------------------------------------- - if (!globalCfg_.gui.externelApplications.empty()) + if (!globalCfg_.gui.externalApps.empty()) { menu.addSeparator(); - for (auto it = globalCfg_.gui.externelApplications.begin(); - it != globalCfg_.gui.externelApplications.end(); + for (auto it = globalCfg_.gui.externalApps.begin(); + it != globalCfg_.gui.externalApps.end(); ++it) { //translate default external apps on the fly: 1. "open in explorer" 2. "start directly" - wxString description = zen::translate(it->first); + wxString description = translate(it->description); if (description.empty()) description = L" "; //wxWidgets doesn't like empty items - auto openApp = [this, command = it->second, leftSide, &selectionLeft, &selectionRight] { openExternalApplication(command, leftSide, selectionLeft, selectionRight); }; + auto openApp = [this, command = it->cmdLine, leftSide, &selectionLeft, &selectionRight] { openExternalApplication(command, leftSide, selectionLeft, selectionRight); }; - const size_t pos = it - globalCfg_.gui.externelApplications.begin(); + const size_t pos = it - globalCfg_.gui.externalApps.begin(); if (pos == 0) description += L"\tEnter, D-Click"; @@ -2489,7 +2474,7 @@ void MainDialog::onGridLabelContextRim(Grid& grid, ColumnTypeRim type, bool left //---------------------------------------------------------------------------------------------- menu.addSeparator(); - auto setIconSize = [&](xmlAccess::FileIconSize sz, bool showIcons) + auto setIconSize = [&](FileIconSize sz, bool showIcons) { globalCfg_.gui.mainDlg.iconSize = sz; globalCfg_.gui.mainDlg.showIcons = showIcons; @@ -2498,7 +2483,7 @@ void MainDialog::onGridLabelContextRim(Grid& grid, ColumnTypeRim type, bool left auto setDefault = [&] { - const xmlAccess::XmlGlobalSettings defaultCfg; + const XmlGlobalSettings defaultCfg; grid.setColumnConfig(convertColAttributes(left ? defaultCfg.gui.mainDlg.columnAttribLeft : defaultCfg.gui.mainDlg.columnAttribRight, defaultCfg.gui.mainDlg.columnAttribLeft)); @@ -2511,13 +2496,13 @@ void MainDialog::onGridLabelContextRim(Grid& grid, ColumnTypeRim type, bool left menu.addSeparator(); menu.addCheckBox(_("Show icons:"), [&] { setIconSize(globalCfg_.gui.mainDlg.iconSize, !globalCfg_.gui.mainDlg.showIcons); }, globalCfg_.gui.mainDlg.showIcons); - auto addSizeEntry = [&](const wxString& label, xmlAccess::FileIconSize sz) + auto addSizeEntry = [&](const wxString& label, FileIconSize sz) { menu.addRadio(label, [sz, &setIconSize] { setIconSize(sz, true /*showIcons*/); }, globalCfg_.gui.mainDlg.iconSize == sz, globalCfg_.gui.mainDlg.showIcons); }; - addSizeEntry(L" " + _("Small" ), xmlAccess::ICON_SIZE_SMALL ); - addSizeEntry(L" " + _("Medium"), xmlAccess::ICON_SIZE_MEDIUM); - addSizeEntry(L" " + _("Large" ), xmlAccess::ICON_SIZE_LARGE ); + addSizeEntry(L" " + _("Small" ), ICON_SIZE_SMALL ); + addSizeEntry(L" " + _("Medium"), ICON_SIZE_MEDIUM); + addSizeEntry(L" " + _("Large" ), ICON_SIZE_LARGE ); //---------------------------------------------------------------------------------------------- // if (type == ColumnTypeRim::DATE) { @@ -2693,7 +2678,7 @@ void MainDialog::cfgHistoryRemoveObsolete(const std::vector<Zstring>& filePaths) std::list<std::future<bool>> availableFiles; //check existence of all config files in parallel! for (const Zstring& filePath : filePaths) - availableFiles.push_back(zen::runAsync([=] { return fileAvailable(filePath); })); + availableFiles.push_back(runAsync([=] { return fileAvailable(filePath); })); //potentially slow network access => limit maximum wait time! wait_for_all_timed(availableFiles.begin(), availableFiles.end(), std::chrono::milliseconds(1000)); @@ -2746,11 +2731,11 @@ void MainDialog::updateUnsavedCfgStatus() title += utfTo<wxString>(activeCfgFilePath); else if (activeConfigFiles_.size() > 1) { - title += xmlAccess::extractJobName(activeConfigFiles_[0]); - std::for_each(activeConfigFiles_.begin() + 1, activeConfigFiles_.end(), [&](const Zstring& filepath) { title += SPACED_DASH + xmlAccess::extractJobName(filepath); }); + title += extractJobName(activeConfigFiles_[0]); + std::for_each(activeConfigFiles_.begin() + 1, activeConfigFiles_.end(), [&](const Zstring& filepath) { title += SPACED_DASH + extractJobName(filepath); }); } else - title += wxString(L"FreeFileSync ") + zen::ffsVersion + SPACED_DASH + _("Folder Comparison and Synchronization"); + title += wxString(L"FreeFileSync ") + ffsVersion + SPACED_DASH + _("Folder Comparison and Synchronization"); SetTitle(title); } @@ -2760,8 +2745,6 @@ void MainDialog::OnConfigSave(wxCommandEvent& event) { const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalFilePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); - using namespace xmlAccess; - //if we work on a single named configuration document: save directly if changed //else: always show file dialog if (activeCfgFilePath.empty()) @@ -2814,16 +2797,16 @@ bool MainDialog::trySaveConfig(const Zstring* guiFilename) //return true if save } else { - Zstring defaultFileName = activeConfigFiles_.size() == 1 && !equalFilePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstr("SyncSettings.ffs_gui"); + Zstring defaultFilePath = activeConfigFiles_.size() == 1 && !equalFilePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstr("SyncSettings.ffs_gui"); //attention: activeConfigFiles may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config! - if (endsWith(defaultFileName, Zstr(".ffs_batch"), CmpFilePath())) - defaultFileName = beforeLast(defaultFileName, Zstr("."), IF_MISSING_RETURN_NONE) + Zstr(".ffs_gui"); + if (endsWith(defaultFilePath, Zstr(".ffs_batch"), CmpFilePath())) + defaultFilePath = beforeLast(defaultFilePath, Zstr("."), IF_MISSING_RETURN_NONE) + Zstr(".ffs_gui"); wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak! wxString(), //OS X really needs dir/file separated like this: - utfTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir - utfTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file + utfTo<wxString>(beforeLast(defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir + utfTo<wxString>(afterLast (defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file wxString(L"FreeFileSync (*.ffs_gui)|*.ffs_gui") + L"|" +_("All files") + L" (*.*)|*", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (filePicker.ShowModal() != wxID_OK) @@ -2831,7 +2814,7 @@ bool MainDialog::trySaveConfig(const Zstring* guiFilename) //return true if save targetFilename = utfTo<Zstring>(filePicker.GetPath()); } - const xmlAccess::XmlGuiConfig guiCfg = getConfig(); + const XmlGuiConfig guiCfg = getConfig(); try { @@ -2851,8 +2834,6 @@ bool MainDialog::trySaveConfig(const Zstring* guiFilename) //return true if save bool MainDialog::trySaveBatchConfig(const Zstring* batchFileToUpdate) { - using namespace xmlAccess; - //essentially behave like trySaveConfig(): the collateral damage of not saving GUI-only settings "m_bpButtonViewTypeSyncAction" is negligible const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalFilePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); @@ -2899,16 +2880,16 @@ bool MainDialog::trySaveBatchConfig(const Zstring* batchFileToUpdate) return false; updateUnsavedCfgStatus(); //nothing else to update on GUI! - Zstring defaultFileName = !activeCfgFilePath.empty() ? activeCfgFilePath : Zstr("BatchRun.ffs_batch"); + Zstring defaultFilePath = !activeCfgFilePath.empty() ? activeCfgFilePath : Zstr("BatchRun.ffs_batch"); //attention: activeConfigFiles may be a *.ffs_gui file! We don't want to overwrite it with a BATCH config! - if (endsWith(defaultFileName, Zstr(".ffs_gui"), CmpFilePath())) - defaultFileName = beforeLast(defaultFileName, Zstr("."), IF_MISSING_RETURN_NONE) + Zstr(".ffs_batch"); + if (endsWith(defaultFilePath, Zstr(".ffs_gui"), CmpFilePath())) + defaultFilePath = beforeLast(defaultFilePath, Zstr("."), IF_MISSING_RETURN_NONE) + Zstr(".ffs_batch"); wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak! wxString(), //OS X really needs dir/file separated like this: - utfTo<wxString>(beforeLast(defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir - utfTo<wxString>(afterLast (defaultFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file + utfTo<wxString>(beforeLast(defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir + utfTo<wxString>(afterLast (defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)), //default file _("FreeFileSync batch") + L" (*.ffs_batch)|*.ffs_batch" + L"|" +_("All files") + L" (*.*)|*", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (filePicker.ShowModal() != wxID_OK) @@ -2942,7 +2923,7 @@ bool MainDialog::saveOldConfig() //return false on user abort const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalFilePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); //notify user about changed settings - if (globalCfg_.optDialogs.popupOnConfigChange) + if (globalCfg_.confirmDlgs.popupOnConfigChange) if (!activeCfgFilePath.empty()) //only if check is active and non-default config file loaded { @@ -2955,8 +2936,6 @@ bool MainDialog::saveOldConfig() //return false on user abort _("&Save"), _("Do&n't save"))) { case QuestionButton2::YES: //save - using namespace xmlAccess; - try { switch (getXmlType(activeCfgFilePath)) //throw FileError @@ -2980,7 +2959,7 @@ bool MainDialog::saveOldConfig() //return false on user abort break; case QuestionButton2::NO: //don't save - globalCfg_.optDialogs.popupOnConfigChange = !neverSaveChanges; + globalCfg_.confirmDlgs.popupOnConfigChange = !neverSaveChanges; break; case QuestionButton2::CANCEL: @@ -3022,27 +3001,6 @@ void MainDialog::OnConfigLoad(wxCommandEvent& event) } -void MainDialog::OnConfigNew(wxCommandEvent& event) -{ - warn_static("replace by loadConfiguration({});") - warn_static("replace excludeFilter handling below with: cfgGrid context menu /new default configuration/") - - if (!saveOldConfig()) //notify user about changed settings - return; - - xmlAccess::XmlGuiConfig newConfig; - - //add default exclusion filter: this is only ever relevant when creating new configurations! - //a default XmlGuiConfig does not need these user-specific exclusions! - Zstring& excludeFilter = newConfig.mainCfg.globalFilter.excludeFilter; - if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr("\n"))) - excludeFilter += Zstr("\n"); - excludeFilter += globalCfg_.gui.defaultExclusionFilter; - - setConfig(newConfig, std::vector<Zstring>()); -} - - void MainDialog::onCfgGridSelection(GridSelectEvent& event) { if (event.mouseSelect_ && !event.mouseSelect_->complete) @@ -3056,15 +3014,12 @@ void MainDialog::onCfgGridSelection(GridSelectEvent& event) filePaths.push_back(cfg->filePath); else assert(false); -#if 1 + if (!loadConfiguration(filePaths)) //user changed m_gridCfgHistory selection so it's this method's responsibility to synchronize with activeConfigFiles: //- if user cancelled saving old config //- there's an error loading new config - //- filePaths is empty and user tried to unselect the current config cfggrid::addAndSelect(*m_gridCfgHistory, activeConfigFiles_, false /*scrollToSelection*/); -#endif - warn_static("support the last one??? does NOT support newConfig.mainCfg.globalFilter.excludeFilter!!!") } @@ -3078,25 +3033,38 @@ void MainDialog::onCfgGridDoubleClick(GridClickEvent& event) } +void MainDialog::OnConfigNew(wxCommandEvent& event) +{ + loadConfiguration({}); +} + + bool MainDialog::loadConfiguration(const std::vector<Zstring>& filePaths) { if (!saveOldConfig()) return false; //cancelled by user - xmlAccess::XmlGuiConfig newGuiCfg; //contains default values + XmlGuiConfig newGuiCfg; //contains default values + + //add default exclusion filter: this is only ever relevant when creating new configurations! + //a default XmlGuiConfig does not need these user-specific exclusions! + Zstring& excludeFilter = newGuiCfg.mainCfg.globalFilter.excludeFilter; + if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr("\n"))) + excludeFilter += Zstr("\n"); + excludeFilter += globalCfg_.gui.defaultExclusionFilter; if (!filePaths.empty()) //empty cfg file list means "use default" try { //allow reading batch configurations also std::wstring warningMsg; - xmlAccess::readAnyConfig(filePaths, newGuiCfg, warningMsg); //throw FileError + readAnyConfig(filePaths, newGuiCfg, warningMsg); //throw FileError if (!warningMsg.empty()) { showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(warningMsg)); setConfig(newGuiCfg, filePaths); - setLastUsedConfig(filePaths, xmlAccess::XmlGuiConfig()); //simulate changed config due to parsing errors + setLastUsedConfig(filePaths, XmlGuiConfig()); //simulate changed config due to parsing errors return true; } } @@ -3207,7 +3175,7 @@ void MainDialog::onCfgGridLabelContext(GridLabelClickEvent& event) auto setDefault = [&] { - const xmlAccess::XmlGlobalSettings defaultCfg; + const XmlGlobalSettings defaultCfg; m_gridCfgHistory->setColumnConfig(convertColAttributes(defaultCfg.gui.mainDlg.cfgGridColumnAttribs, getCfgGridDefaultColAttribs())); }; menu.addItem(_("&Default"), setDefault); //'&' -> reuse text from "default" buttons elsewhere @@ -3279,7 +3247,7 @@ void MainDialog::onSetSyncDirection(SyncDirectionEvent& event) void MainDialog::setLastUsedConfig(const std::vector<Zstring>& cfgFilePaths, - const xmlAccess::XmlGuiConfig& guiConfig) + const XmlGuiConfig& guiConfig) { activeConfigFiles_ = cfgFilePaths; lastSavedCfg_ = guiConfig; @@ -3290,7 +3258,7 @@ void MainDialog::setLastUsedConfig(const std::vector<Zstring>& cfgFilePaths, } -void MainDialog::setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std::vector<Zstring>& referenceFiles) +void MainDialog::setConfig(const XmlGuiConfig& newGuiCfg, const std::vector<Zstring>& referenceFiles) { currentCfg_ = newGuiCfg; @@ -3317,9 +3285,9 @@ void MainDialog::setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std:: } -xmlAccess::XmlGuiConfig MainDialog::getConfig() const +XmlGuiConfig MainDialog::getConfig() const { - xmlAccess::XmlGuiConfig guiCfg = currentCfg_; + XmlGuiConfig guiCfg = currentCfg_; //load settings whose ownership lies not in currentCfg: @@ -3561,7 +3529,7 @@ inline wxBitmap buttonReleased(const std::string& name) { wxImage output = getResourceImage(utfTo<wxString>(name)).ConvertToImage().ConvertToGreyscale(1.0/3, 1.0/3, 1.0/3); //treat all channels equally! - //zen::moveImage(output, 1, 0); //move image right one pixel + //moveImage(output, 1, 0); //move image right one pixel brighten(output, 80); return mirrorIfRtl(output); @@ -3703,13 +3671,13 @@ void MainDialog::OnCompare(wxCommandEvent& event) //handle status display and error messages StatusHandlerTemporaryPanel statusHandler(*this); - const std::vector<zen::FolderPairCfg> cmpConfig = extractCompareCfg(getConfig().mainCfg); + const std::vector<FolderPairCfg> cmpConfig = extractCompareCfg(getConfig().mainCfg); //GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization std::unique_ptr<LockHolder> dirLocks; //COMPARE DIRECTORIES - folderCmp_ = compare(globalCfg_.optDialogs, + folderCmp_ = compare(globalCfg_.warnDlgs, globalCfg_.fileTimeTolerance, true, //allowUserInteraction globalCfg_.runWithBackgroundPriority, @@ -3756,7 +3724,12 @@ void MainDialog::OnCompare(wxCommandEvent& event) //prepare status information if (allElementsEqual(folderCmp_)) + { flashStatusInformation(_("All files are in sync")); + + //update last sync date for selected cfg files https://www.freefilesync.org/forum/viewtopic.php?t=4991 + updateLastSyncTimesToNow(); + } } @@ -3865,17 +3838,17 @@ void MainDialog::OnStartSync(wxCommandEvent& event) } //show sync preview/confirmation dialog - if (globalCfg_.optDialogs.confirmSyncStart) + if (globalCfg_.confirmDlgs.confirmSyncStart) { bool dontShowAgain = false; - if (zen::showSyncConfirmationDlg(this, - getConfig().mainCfg.getSyncVariantName(), - zen::SyncStatistics(folderCmp_), - dontShowAgain) != ReturnSmallDlg::BUTTON_OKAY) + if (showSyncConfirmationDlg(this, + getConfig().mainCfg.getSyncVariantName(), + SyncStatistics(folderCmp_), + dontShowAgain) != ReturnSmallDlg::BUTTON_OKAY) return; - globalCfg_.optDialogs.confirmSyncStart = !dontShowAgain; + globalCfg_.confirmDlgs.confirmSyncStart = !dontShowAgain; } bool exitAfterSync = false; @@ -3893,15 +3866,17 @@ void MainDialog::OnStartSync(wxCommandEvent& event) //class handling status updates and error messages StatusHandlerFloatingDialog statusHandler(this, //throw AbortProcess + syncStartTime, globalCfg_.lastSyncsLogFileSizeMax, currentCfg_.mainCfg.ignoreErrors, globalCfg_.automaticRetryCount, globalCfg_.automaticRetryDelay, - xmlAccess::extractJobName(activeCfgFilePath), + extractJobName(activeCfgFilePath), globalCfg_.soundFileSyncFinished, guiCfg.mainCfg.postSyncCommand, guiCfg.mainCfg.postSyncCondition, - exitAfterSync); + exitAfterSync, + globalCfg_.autoCloseProgressDialog); //inform about (important) non-default global settings logNonDefaultSettings(globalCfg_, statusHandler); //let's report here rather than before comparison (user might have changed global settings in the meantime!) @@ -3923,11 +3898,11 @@ void MainDialog::OnStartSync(wxCommandEvent& event) if (Opt<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<RIGHT_SIDE>())) availableDirPaths.insert(*nativeFolderPath); } - dirLocks = std::make_unique<LockHolder>(availableDirPaths, globalCfg_.optDialogs.warnDirectoryLockFailed, statusHandler); + dirLocks = std::make_unique<LockHolder>(availableDirPaths, globalCfg_.warnDlgs.warnDirectoryLockFailed, statusHandler); } //START SYNCHRONIZATION - const std::vector<zen::FolderPairSyncCfg> syncProcessCfg = zen::extractSyncCfg(guiCfg.mainCfg); + const std::vector<FolderPairSyncCfg> syncProcessCfg = extractSyncCfg(guiCfg.mainCfg); if (syncProcessCfg.size() != folderCmp_.size()) throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); //should never happen: sync button is deactivated if they are not in sync @@ -3941,18 +3916,11 @@ void MainDialog::OnStartSync(wxCommandEvent& event) globalCfg_.folderAccessTimeout, syncProcessCfg, folderCmp_, - globalCfg_.optDialogs, + globalCfg_.warnDlgs, statusHandler); //not cancelled? => update last sync date for selected cfg files - std::vector<std::pair<Zstring, time_t>> lastSyncTimes; - for (const Zstring& cfgPath : activeConfigFiles_) - lastSyncTimes.emplace_back(cfgPath, std::time(nullptr)); - - cfggrid::getDataView(*m_gridCfgHistory).setLastSyncTime(lastSyncTimes); - //re-apply selection: sort order changed if sorted by last sync time - cfggrid::addAndSelect(*m_gridCfgHistory, activeConfigFiles_, false /*scrollToSelection*/); - //m_gridCfgHistory->Refresh(); <- implicit in last call + updateLastSyncTimesToNow(); } catch (AbortProcess&) {} @@ -3966,6 +3934,22 @@ void MainDialog::OnStartSync(wxCommandEvent& event) } +void MainDialog::updateLastSyncTimesToNow() +{ + const time_t now = std::time(nullptr); + + std::vector<std::pair<Zstring, time_t>> lastSyncTimes; + for (const Zstring& cfgPath : activeConfigFiles_) + lastSyncTimes.emplace_back(cfgPath, now); + + cfggrid::getDataView(*m_gridCfgHistory).setLastSyncTime(lastSyncTimes); + + //re-apply selection: sort order changed if sorted by last sync time + cfggrid::addAndSelect(*m_gridCfgHistory, activeConfigFiles_, false /*scrollToSelection*/); + //m_gridCfgHistory->Refresh(); <- implicit in last call +} + + void MainDialog::onGridDoubleClickL(GridClickEvent& event) { onGridDoubleClickRim(event.row_, true); @@ -3980,14 +3964,14 @@ void MainDialog::onGridDoubleClickR(GridClickEvent& event) void MainDialog::onGridDoubleClickRim(size_t row, bool leftSide) { - if (!globalCfg_.gui.externelApplications.empty()) + if (!globalCfg_.gui.externalApps.empty()) { std::vector<FileSystemObject*> selectionLeft; std::vector<FileSystemObject*> selectionRight; if (FileSystemObject* fsObj = filegrid::getDataView(*m_gridMainC).getObject(row)) //selection must be a list of BOUND pointers! (leftSide ? selectionLeft : selectionRight) = { fsObj }; - openExternalApplication(globalCfg_.gui.externelApplications[0].second, leftSide, selectionLeft, selectionRight); + openExternalApplication(globalCfg_.gui.externalApps[0].cmdLine, leftSide, selectionLeft, selectionRight); } } @@ -4069,7 +4053,7 @@ void MainDialog::OnSwapSides(wxCommandEvent& event) try { - zen::swapGrids(getConfig().mainCfg, folderCmp_); //throw FileError + swapGrids(getConfig().mainCfg, folderCmp_); //throw FileError } catch (const FileError& e) { @@ -4243,7 +4227,7 @@ void MainDialog::applySyncConfig() { try { - zen::redetermineSyncDirection(getConfig().mainCfg, folderCmp_, nullptr /*notifyStatus*/); //throw FileError + redetermineSyncDirection(getConfig().mainCfg, folderCmp_, nullptr /*notifyStatus*/); //throw FileError } catch (const FileError& e) { @@ -4664,7 +4648,7 @@ void MainDialog::removeAddFolderPair(size_t pos) } -void MainDialog::setAddFolderPairs(const std::vector<zen::FolderPairEnh>& newPairs) +void MainDialog::setAddFolderPairs(const std::vector<FolderPairEnh>& newPairs) { additionalFolderPairs_.clear(); @@ -4681,7 +4665,7 @@ void MainDialog::setAddFolderPairs(const std::vector<zen::FolderPairEnh>& newPai //menu events void MainDialog::OnMenuOptions(wxCommandEvent& event) { - zen::showOptionsDlg(this, globalCfg_); + showOptionsDlg(this, globalCfg_); } @@ -4818,13 +4802,13 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) void MainDialog::OnMenuCheckVersion(wxCommandEvent& event) { - zen::checkForUpdateNow(this, globalCfg_.gui.lastOnlineVersion); + checkForUpdateNow(this, globalCfg_.gui.lastOnlineVersion); } void MainDialog::OnMenuUpdateAvailable(wxCommandEvent& event) { - zen::checkForUpdateNow(this, globalCfg_.gui.lastOnlineVersion); //show changelog + handle Donation Edition auto-updater (including expiration) + checkForUpdateNow(this, globalCfg_.gui.lastOnlineVersion); //show changelog + handle Donation Edition auto-updater (including expiration) } @@ -4886,13 +4870,13 @@ void MainDialog::OnLayoutWindowAsync(wxIdleEvent& event) void MainDialog::OnMenuAbout(wxCommandEvent& event) { - zen::showAboutDialog(this); + showAboutDialog(this); } void MainDialog::OnShowHelp(wxCommandEvent& event) { - zen::displayHelpEntry(L"freefilesync", this); + displayHelpEntry(L"freefilesync", this); } //######################################################################################################### @@ -4901,7 +4885,7 @@ void MainDialog::OnShowHelp(wxCommandEvent& event) void MainDialog::switchProgramLanguage(wxLanguage langId) { //create new dialog with respect to new language - xmlAccess::XmlGlobalSettings newGlobalCfg = getGlobalCfgBeforeExit(); + XmlGlobalSettings newGlobalCfg = getGlobalCfgBeforeExit(); newGlobalCfg.programLanguage = langId; //show new dialog, then delete old one diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h index ffc0ec52..646bfd58 100755 --- a/FreeFileSync/Source/ui/main_dlg.h +++ b/FreeFileSync/Source/ui/main_dlg.h @@ -9,7 +9,6 @@ #include <map> #include <list> -#include <stack> #include <memory> #include <wx+/async_task.h> #include <wx+/file_drop.h> @@ -21,9 +20,16 @@ #include "folder_history_box.h" #include "../algorithm.h" + +namespace fff +{ class FolderPairFirst; class FolderPairPanel; class CompareProgressDialog; +class FolderSelectorImpl; +template <class GuiPanel> +class FolderPairCallback; +class PanelMoveWindow; class MainDialog : public MainDialogGenerated @@ -36,8 +42,8 @@ public: //when switching language, //or switching from batch run to GUI on warnings static void create(const Zstring& globalConfigFilePath, - const xmlAccess::XmlGlobalSettings* globalSettings, //optional: take over ownership => save on exit - const xmlAccess::XmlGuiConfig& guiCfg, + const XmlGlobalSettings* globalSettings, //optional: take over ownership => save on exit + const XmlGuiConfig& guiCfg, const std::vector<Zstring>& referenceFiles, bool startComparison); @@ -48,15 +54,14 @@ public: private: MainDialog(const Zstring& globalConfigFilePath, - const xmlAccess::XmlGuiConfig& guiCfg, + const XmlGuiConfig& guiCfg, const std::vector<Zstring>& referenceFiles, - const xmlAccess::XmlGlobalSettings& globalSettings, //take over ownership => save on exit + const XmlGlobalSettings& globalSettings, //take over ownership => save on exit bool startComparison); ~MainDialog(); friend class StatusHandlerTemporaryPanel; friend class StatusHandlerFloatingDialog; - friend class ManualDeletionHandler; friend class FolderPairFirst; friend class FolderPairPanel; friend class FolderSelectorImpl; @@ -65,14 +70,14 @@ private: friend class PanelMoveWindow; //configuration load/save - void setLastUsedConfig(const Zstring& cfgFilePath, const xmlAccess::XmlGuiConfig& guiConfig) { setLastUsedConfig(std::vector<Zstring>({ cfgFilePath }), guiConfig); } - void setLastUsedConfig(const std::vector<Zstring>& cfgFilePaths, const xmlAccess::XmlGuiConfig& guiConfig); + void setLastUsedConfig(const Zstring& cfgFilePath, const XmlGuiConfig& guiConfig) { setLastUsedConfig(std::vector<Zstring>({ cfgFilePath }), guiConfig); } + void setLastUsedConfig(const std::vector<Zstring>& cfgFilePaths, const XmlGuiConfig& guiConfig); - xmlAccess::XmlGuiConfig getConfig() const; - void setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std::vector<Zstring>& referenceFiles); + XmlGuiConfig getConfig() const; + void setConfig(const XmlGuiConfig& newGuiCfg, const std::vector<Zstring>& referenceFiles); - void setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSettings); //messes with Maximize(), window sizes, so call just once! - xmlAccess::XmlGlobalSettings getGlobalCfgBeforeExit(); //destructive "get" thanks to "Iconize(false), Maximize(false)" + void setGlobalCfgOnInit(const XmlGlobalSettings& globalSettings); //messes with Maximize(), window sizes, so call just once! + XmlGlobalSettings getGlobalCfgBeforeExit(); //destructive "get" thanks to "Iconize(false), Maximize(false)" bool loadConfiguration(const std::vector<Zstring>& filepaths); //return true if loaded successfully @@ -87,10 +92,10 @@ private: void cfgHistoryRemoveObsolete(const std::vector<Zstring>& filepaths); - void insertAddFolderPair(const std::vector<zen::FolderPairEnh>& newPairs, size_t pos); + void insertAddFolderPair(const std::vector<FolderPairEnh>& newPairs, size_t pos); void moveAddFolderPairUp(size_t pos); void removeAddFolderPair(size_t pos); - void setAddFolderPairs(const std::vector<zen::FolderPairEnh>& newPairs); + void setAddFolderPairs(const std::vector<FolderPairEnh>& newPairs); void updateGuiForFolderPair(); //helper method: add usability by showing/hiding buttons related to folder pairs @@ -103,22 +108,22 @@ private: void updateUnsavedCfgStatus(); // //context menu functions - std::vector<zen::FileSystemObject*> getGridSelection(bool fromLeft = true, bool fromRight = true) const; - std::vector<zen::FileSystemObject*> getTreeSelection() const; + std::vector<FileSystemObject*> getGridSelection(bool fromLeft = true, bool fromRight = true) const; + std::vector<FileSystemObject*> getTreeSelection() const; - void setSyncDirManually(const std::vector<zen::FileSystemObject*>& selection, zen::SyncDirection direction); - void setFilterManually(const std::vector<zen::FileSystemObject*>& selection, bool setIncluded); + void setSyncDirManually(const std::vector<FileSystemObject*>& selection, SyncDirection direction); + void setFilterManually (const std::vector<FileSystemObject*>& selection, bool setIncluded); void copySelectionToClipboard(const std::vector<const zen::Grid*>& gridRefs); - void copyToAlternateFolder(const std::vector<zen::FileSystemObject*>& selectionLeft, - const std::vector<zen::FileSystemObject*>& selectionRight); + void copyToAlternateFolder(const std::vector<FileSystemObject*>& selectionLeft, + const std::vector<FileSystemObject*>& selectionRight); - void deleteSelectedFiles(const std::vector<zen::FileSystemObject*>& selectionLeft, - const std::vector<zen::FileSystemObject*>& selectionRight, bool moveToRecycler); + void deleteSelectedFiles(const std::vector<FileSystemObject*>& selectionLeft, + const std::vector<FileSystemObject*>& selectionRight, bool moveToRecycler); void openExternalApplication(const Zstring& commandLinePhrase, bool leftSide, - const std::vector<zen::FileSystemObject*>& selectionLeft, - const std::vector<zen::FileSystemObject*>& selectionRight); //selection may be empty + const std::vector<FileSystemObject*>& selectionLeft, + const std::vector<FileSystemObject*>& selectionRight); //selection may be empty //status bar supports one of the following two states at a time: void setStatusBarFileStatistics(size_t filesOnLeftView, size_t foldersOnLeftView, size_t filesOnRightView, size_t foldersOnRightView, uint64_t filesizeLeftView, uint64_t filesizeRightView); @@ -167,8 +172,8 @@ private: void onDirSelected(wxCommandEvent& event); void onDirManualCorrection(wxCommandEvent& event); - void onCheckRows (zen::CheckRowsEvent& event); - void onSetSyncDirection(zen::SyncDirectionEvent& event); + void onCheckRows (CheckRowsEvent& event); + void onSetSyncDirection(SyncDirectionEvent& event); void onGridDoubleClickL(zen::GridClickEvent& event); void onGridDoubleClickR(zen::GridClickEvent& event); @@ -177,12 +182,12 @@ private: void onGridLabelLeftClickL(zen::GridLabelClickEvent& event); void onGridLabelLeftClickC(zen::GridLabelClickEvent& event); void onGridLabelLeftClickR(zen::GridLabelClickEvent& event); - void onGridLabelLeftClick(bool onLeft, zen::ColumnTypeRim type); + void onGridLabelLeftClick(bool onLeft, ColumnTypeRim type); void onGridLabelContextL(zen::GridLabelClickEvent& event); void onGridLabelContextC(zen::GridLabelClickEvent& event); void onGridLabelContextR(zen::GridLabelClickEvent& event); - void onGridLabelContextRim(zen::Grid& grid, zen::ColumnTypeRim type, bool left); + void onGridLabelContextRim(zen::Grid& grid, ColumnTypeRim type, bool left); void OnToggleViewType (wxCommandEvent& event) override; void OnToggleViewButton(wxCommandEvent& event) override; @@ -214,15 +219,17 @@ private: void OnSwapSides (wxCommandEvent& event) override; void OnClose (wxCloseEvent& event) override; - void OnCmpSettings (wxCommandEvent& event) override { showConfigDialog(zen::SyncConfigPanel::COMPARISON, -1); } - void OnConfigureFilter(wxCommandEvent& event) override { showConfigDialog(zen::SyncConfigPanel::FILTER, -1); } - void OnSyncSettings (wxCommandEvent& event) override { showConfigDialog(zen::SyncConfigPanel::SYNC, -1); } + void OnCmpSettings (wxCommandEvent& event) override { showConfigDialog(SyncConfigPanel::COMPARISON, -1); } + void OnConfigureFilter(wxCommandEvent& event) override { showConfigDialog(SyncConfigPanel::FILTER, -1); } + void OnSyncSettings (wxCommandEvent& event) override { showConfigDialog(SyncConfigPanel::SYNC, -1); } + + void showConfigDialog(SyncConfigPanel panelToShow, int localPairIndexToShow); - void showConfigDialog(zen::SyncConfigPanel panelToShow, int localPairIndexToShow); + void updateLastSyncTimesToNow(); void filterExtension(const Zstring& extension, bool include); - void filterShortname(const zen::FileSystemObject& fsObj, bool include); - void filterItems(const std::vector<zen::FileSystemObject*>& selection, bool include); + void filterShortname(const FileSystemObject& fsObj, bool include); + void filterItems(const std::vector<FileSystemObject*>& selection, bool include); void addFilterPhrase(const Zstring& phrase, bool include, bool requireNewLine); void OnTopFolderPairAdd (wxCommandEvent& event) override; @@ -230,9 +237,9 @@ private: void OnRemoveFolderPair (wxCommandEvent& event); void OnShowFolderPairOptions(wxEvent& event); - void OnTopLocalCompCfg (wxCommandEvent& event) override { showConfigDialog(zen::SyncConfigPanel::COMPARISON, 0); } - void OnTopLocalSyncCfg (wxCommandEvent& event) override { showConfigDialog(zen::SyncConfigPanel::SYNC, 0); } - void OnTopLocalFilterCfg(wxCommandEvent& event) override { showConfigDialog(zen::SyncConfigPanel::FILTER, 0); } + void OnTopLocalCompCfg (wxCommandEvent& event) override { showConfigDialog(SyncConfigPanel::COMPARISON, 0); } + void OnTopLocalSyncCfg (wxCommandEvent& event) override { showConfigDialog(SyncConfigPanel::SYNC, 0); } + void OnTopLocalFilterCfg(wxCommandEvent& event) override { showConfigDialog(SyncConfigPanel::FILTER, 0); } void OnLocalCompCfg (wxCommandEvent& event); void OnLocalSyncCfg (wxCommandEvent& event); @@ -278,24 +285,24 @@ private: //application variables are stored here: //global settings shared by GUI and batch mode - xmlAccess::XmlGlobalSettings globalCfg_; + XmlGlobalSettings globalCfg_; const Zstring globalConfigFilePath_; //------------------------------------- //program configuration - xmlAccess::XmlGuiConfig currentCfg_; + XmlGuiConfig currentCfg_; //used when saving configuration std::vector<Zstring> activeConfigFiles_; //name of currently loaded config files: NOT owned by m_gridCfgHistory, see onCfgGridSelection() - xmlAccess::XmlGuiConfig lastSavedCfg_; //support for: "Save changed configuration?" dialog + XmlGuiConfig lastSavedCfg_; //support for: "Save changed configuration?" dialog const Zstring lastRunConfigPath_; //let's not use another static... //------------------------------------- //the prime data structure of this tool *bling*: - zen::FolderComparison folderCmp_; //optional!: sync button not available if empty + FolderComparison folderCmp_; //optional!: sync button not available if empty //folder pairs: std::unique_ptr<FolderPairFirst> firstFolderPair_; //always bound!!! @@ -326,14 +333,15 @@ private: zen::AsyncGuiQueue guiQueue_; //schedule and run long-running tasks asynchronously, but process results on GUI queue - std::unique_ptr<zen::FilterConfig> filterCfgOnClipboard_; //copy/paste of filter config + std::unique_ptr<FilterConfig> filterCfgOnClipboard_; //copy/paste of filter config wxWindow* focusWindowAfterSearch_ = nullptr; //used to restore focus after search panel is closed bool localKeyEventsEnabled_ = true; bool allowMainDialogClose_ = true; //e.g. do NOT allow close while sync is running => crash!!! - zen::TempFileBuffer tempFileBuf_; //buffer temporary copies of non-native files for %local_path% + TempFileBuffer tempFileBuf_; //buffer temporary copies of non-native files for %local_path% }; +} #endif //MAIN_DLG_H_8910481324545644545 diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp index bd97f8d5..10e9664b 100755 --- a/FreeFileSync/Source/ui/progress_indicator.cpp +++ b/FreeFileSync/Source/ui/progress_indicator.cpp @@ -38,14 +38,14 @@ using namespace zen; -using namespace xmlAccess; +using namespace fff; namespace { -//window size used for statistics in milliseconds -const int WINDOW_REMAINING_TIME_MS = 60000; //USB memory stick scenario can have drop outs of 40 seconds => 60 sec. window size handles it -const int WINDOW_BYTES_PER_SEC = 5000; // +//window size used for statistics +const std::chrono::seconds WINDOW_REMAINING_TIME(60); //USB memory stick scenario can have drop outs of 40 seconds => 60 sec. window size handles it +const std::chrono::seconds WINDOW_BYTES_PER_SEC (5); // inline wxColor getColorGridLine() { return { 192, 192, 192 }; } //light grey @@ -63,12 +63,15 @@ inline wxColor getColorItemsBackgroundRim() { return { 53, 25, 255 }; } //dark //don't use wxStopWatch for long-running measurements: internally it uses ::QueryPerformanceCounter() which can overflow after only a few days: -// std::chrono::system_clock is not a steady clock, but at least doesn't overflow! //https://www.freefilesync.org/forum/viewtopic.php?t=1426 +// std::chrono::system_clock is not a steady clock, but at least doesn't overflow! (wraps ::GetSystemTimePreciseAsFileTime()) +// std::chrono::steady_clock also wraps ::QueryPerformanceCounter() => same flaw like wxStopWatch??? class StopWatch { public: + bool isPaused() const { return paused_; } + void pause() { if (!paused_) @@ -94,18 +97,17 @@ public: elapsedUntilPause_ = std::chrono::nanoseconds::zero(); } - int64_t timeMs() const + std::chrono::nanoseconds elapsed() const { auto elapsedTotal = elapsedUntilPause_; if (!paused_) elapsedTotal += std::chrono::system_clock::now() - startTime_; - - return std::chrono::duration_cast<std::chrono::milliseconds>(elapsedTotal).count(); + return elapsedTotal; } private: bool paused_ = false; - std::chrono::system_clock::time_point startTime_ = std::chrono::system_clock::now(); //uses ::GetSystemTimePreciseAsFileTime() + std::chrono::system_clock::time_point startTime_ = std::chrono::system_clock::now(); std::chrono::nanoseconds elapsedUntilPause_{}; //std::chrono::duration is uninitialized by default! WTF! When will this stupidity end??? }; @@ -116,6 +118,9 @@ std::wstring getDialogPhaseText(const Statistics* syncStat, bool paused, SyncPro { if (paused) return _("Paused"); + + if (syncStat->getAbortStatus()) + return _("Stop requested..."); else switch (syncStat->currentPhase()) { @@ -135,7 +140,9 @@ std::wstring getDialogPhaseText(const Statistics* syncStat, bool paused, SyncPro case SyncProgressDialog::RESULT_ABORTED: return _("Stopped"); case SyncProgressDialog::RESULT_FINISHED_WITH_ERROR: + return _("Completed with errors"); case SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS: + return _("Completed with warnings"); case SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS: return _("Completed"); } @@ -209,15 +216,15 @@ private: wxFrame& parentWindow_; wxString parentTitleBackup_; - StopWatch timeElapsed_; - int64_t binCompStartMs_ = 0; //begin of binary comparison phase in [ms] + StopWatch stopWatch_; + std::chrono::nanoseconds binCompStart_{}; //begin of binary comparison phase const Statistics* syncStat_ = nullptr; //only bound while sync is running std::unique_ptr<Taskbar> taskbar_; std::unique_ptr<PerfCheck> perf_; //estimate remaining time - int64_t timeLastSpeedEstimateMs_ = -1000000; //used for calculating intervals between showing and collecting perf samples + std::chrono::nanoseconds timeLastSpeedEstimate_ = std::chrono::seconds(-100); //used for calculating intervals between showing and collecting perf samples //initial value: just some big number std::shared_ptr<CurveDataProgressBar> curveDataBytes_{ std::make_shared<CurveDataProgressBar>(true /*drawTop*/) }; @@ -268,7 +275,7 @@ void CompareProgressDialog::Impl::init(const Statistics& syncStat, bool ignoreEr bSizerProgressGraph->Show(false); perf_.reset(); - timeElapsed_.restart(); //measure total time + stopWatch_.restart(); //measure total time //initially hide status that's relevant for comparing bytewise only m_staticTextItemsFoundLabel->Show(); @@ -311,10 +318,10 @@ void CompareProgressDialog::Impl::initNewPhase() case ProcessCallback::PHASE_COMPARING_CONTENT: case ProcessCallback::PHASE_SYNCHRONIZING: //start to measure perf - perf_ = std::make_unique<PerfCheck>(WINDOW_REMAINING_TIME_MS, WINDOW_BYTES_PER_SEC); - timeLastSpeedEstimateMs_ = -1000000; //some big number + perf_ = std::make_unique<PerfCheck>(WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC); + timeLastSpeedEstimate_ = std::chrono::seconds(-100); //make sure estimate is updated upon next check - binCompStartMs_ = timeElapsed_.timeMs(); + binCompStart_ = stopWatch_.elapsed(); bSizerProgressGraph->Show(true); @@ -355,7 +362,7 @@ void CompareProgressDialog::Impl::updateProgressGui() }; bool layoutChanged = false; //avoid screen flicker by calling layout() only if necessary - const int64_t timeNowMs = timeElapsed_.timeMs(); + const std::chrono::nanoseconds timeElapsed = stopWatch_.elapsed(); //status texts setText(*m_staticTextStatus, replaceCpy(syncStat_->currentStatusText(), L'\n', L' ')); //no layout update for status texts! @@ -410,12 +417,12 @@ void CompareProgressDialog::Impl::updateProgressGui() //remaining time and speed: only visible during binary comparison assert(perf_); if (perf_) - if (numeric::dist(timeLastSpeedEstimateMs_, timeNowMs) >= 500) + if (numeric::dist(timeLastSpeedEstimate_, timeElapsed) >= std::chrono::milliseconds(500)) { - timeLastSpeedEstimateMs_ = timeNowMs; + timeLastSpeedEstimate_ = timeElapsed; - if (numeric::dist(binCompStartMs_, timeNowMs) >= 1000) //discard stats for first second: probably messy - perf_->addSample(itemsCurrent, bytesCurrent, timeNowMs); + if (numeric::dist(binCompStart_, timeElapsed) >= std::chrono::seconds(1)) //discard stats for first second: probably messy + perf_->addSample(timeElapsed, itemsCurrent, bytesCurrent); //current speed -> Win 7 copy uses 1 sec update interval instead Opt<std::wstring> bps = perf_->getBytesPerSecond(); @@ -434,8 +441,8 @@ void CompareProgressDialog::Impl::updateProgressGui() break; } - //time elapsed - const int64_t timeElapSec = timeNowMs / 1000; + const int64_t timeElapSec = std::chrono::duration_cast<std::chrono::seconds>(timeElapsed).count(); + setText(*m_staticTextTimeElapsed, timeElapSec < 3600 ? wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") : @@ -508,7 +515,7 @@ public: struct LogEntryView { time_t time = 0; - MessageType type = TYPE_INFO; + MessageType type = MSG_TYPE_INFO; MsgString messageLine; bool firstLine = false; //if LogEntry::message spans multiple rows }; @@ -529,7 +536,7 @@ public: return NoValue(); } - void updateView(int includedTypes) //TYPE_INFO | TYPE_WARNING, ect. see error_log.h + void updateView(int includedTypes) //MSG_TYPE_INFO | MSG_TYPE_WARNING, ect. see error_log.h { viewRef_.clear(); @@ -626,13 +633,13 @@ public: if (entry->firstLine) switch (entry->type) { - case TYPE_INFO: + case MSG_TYPE_INFO: return _("Info"); - case TYPE_WARNING: + case MSG_TYPE_WARNING: return _("Warning"); - case TYPE_ERROR: + case MSG_TYPE_ERROR: return _("Error"); - case TYPE_FATAL_ERROR: + case MSG_TYPE_FATAL_ERROR: return _("Serious Error"); } break; @@ -676,14 +683,14 @@ public: if (entry->firstLine) switch (entry->type) { - case TYPE_INFO: + case MSG_TYPE_INFO: drawBitmapRtlNoMirror(dc, getResourceImage(L"msg_info_small"), rectTmp, wxALIGN_CENTER); break; - case TYPE_WARNING: + case MSG_TYPE_WARNING: drawBitmapRtlNoMirror(dc, getResourceImage(L"msg_warning_small"), rectTmp, wxALIGN_CENTER); break; - case TYPE_ERROR: - case TYPE_FATAL_ERROR: + case MSG_TYPE_ERROR: + case MSG_TYPE_FATAL_ERROR: drawBitmapRtlNoMirror(dc, getResourceImage(L"msg_error_small"), rectTmp, wxALIGN_CENTER); break; } @@ -760,9 +767,9 @@ class LogPanel : public LogPanelGenerated public: LogPanel(wxWindow* parent, const ErrorLog& log) : LogPanelGenerated(parent) { - const int errorCount = log.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); - const int warningCount = log.getItemCount(TYPE_WARNING); - const int infoCount = log.getItemCount(TYPE_INFO); + const int errorCount = log.getItemCount(MSG_TYPE_ERROR | MSG_TYPE_FATAL_ERROR); + const int warningCount = log.getItemCount(MSG_TYPE_WARNING); + const int infoCount = log.getItemCount(MSG_TYPE_INFO); auto initButton = [](ToggleButton& btn, const wchar_t* imgName, const wxString& tooltip) { @@ -840,13 +847,13 @@ private: { int includedTypes = 0; if (m_bpButtonErrors->isActive()) - includedTypes |= TYPE_ERROR | TYPE_FATAL_ERROR; + includedTypes |= MSG_TYPE_ERROR | MSG_TYPE_FATAL_ERROR; if (m_bpButtonWarnings->isActive()) - includedTypes |= TYPE_WARNING; + includedTypes |= MSG_TYPE_WARNING; if (m_bpButtonInfo->isActive()) - includedTypes |= TYPE_INFO; + includedTypes |= MSG_TYPE_INFO; getDataView().updateView(includedTypes); //update MVC "model" m_gridMessages->Refresh(); //update MVC "view" @@ -1009,27 +1016,20 @@ class CurveDataStatistics : public SparseCurveData public: CurveDataStatistics() : SparseCurveData(true /*addSteps*/) {} - void clear() { samples_.clear(); lastSample_ = std::make_pair(0, 0); } + void clear() { samples_.clear(); lastSample_ = {}; } - void addRecord(int64_t timeNowMs, double value) + void addRecord(std::chrono::nanoseconds timeElapsed, double value) { - assert((!samples_.empty() || lastSample_ == std::pair<int64_t, double>(0, 0))); - - //samples.clear(); - //samples.emplace(-1000, 0); - //samples.emplace(0, 0); - //samples.emplace(1, 1); - //samples.emplace(1000, 0); - //return; + assert(!samples_.empty() || (lastSample_ == std::pair<std::chrono::nanoseconds, double>())); - lastSample_ = std::make_pair(timeNowMs, value); + lastSample_ = { timeElapsed, value }; - //allow for at most one sample per 100ms (handles duplicate inserts, too!) => this is unrelated to UI_UPDATE_INTERVAL_MS! + //allow for at most one sample per 100ms (handles duplicate inserts, too!) => this is unrelated to UI_UPDATE_INTERVAL! if (!samples_.empty()) //always unconditionally insert first sample! - if (timeNowMs / 100 == samples_.rbegin()->first / 100) + if (numeric::dist(timeElapsed, samples_.rbegin()->first) < std::chrono::milliseconds(100)) return; - samples_.insert(samples_.end(), std::make_pair(timeNowMs, value)); //time is "expected" to be monotonously ascending + samples_.insert(samples_.end(), { timeElapsed, 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 @@ -1041,9 +1041,9 @@ private: std::pair<double, double> getRangeX() const override { if (samples_.empty()) - return std::make_pair(0.0, 0.0); + return {}; - const double upperEndMs = std::max(samples_.rbegin()->first, lastSample_.first); + const std::chrono::nanoseconds upperEnd = std::max(samples_.rbegin()->first, lastSample_.first); /* //report some additional width by 5% elapsed time to make graph recalibrate before hitting the right border @@ -1052,47 +1052,49 @@ private: upperEndMs += 0.05 *(upperEndMs - samples.begin()->first); */ - return { samples_.begin()->first / 1000.0, //need not start with 0, e.g. "binary comparison, graph reset, followed by sync" - upperEndMs / 1000.0}; + return { std::chrono::duration<double>(samples_.begin()->first).count(), //need not start with 0, e.g. "binary comparison, graph reset, followed by sync" + std::chrono::duration<double>(upperEnd).count() }; } Opt<CurvePoint> getLessEq(double x) const override //x: seconds since begin { - const int64_t timex = std::floor(x * 1000); + const auto timeX = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::duration<double>(x)); //round down + //------ 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 (lastSample_.first <= timeX) + return CurvePoint(std::chrono::duration<double>(lastSample_.first).count(), 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); + auto it = samples_.upper_bound(timeX); if (it == samples_.begin()) return NoValue(); //=> samples not empty in this context --it; - return CurvePoint(it->first / 1000.0, it->second); + return CurvePoint(std::chrono::duration<double>(it->first).count(), it->second); } Opt<CurvePoint> getGreaterEq(double x) const override { - const int64_t timex = std::ceil(x * 1000); + const std::chrono::nanoseconds timeX(static_cast<std::chrono::nanoseconds::rep>(std::ceil(x * (1000 * 1000 * 1000)))); //round up! + //------ 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_.rbegin()->first < timeX && timeX <= lastSample_.first) + return CurvePoint(std::chrono::duration<double>(lastSample_.first).count(), lastSample_.second); //-------------------------------------------------- - auto it = samples_.lower_bound(timex); + auto it = samples_.lower_bound(timeX); if (it == samples_.end()) return NoValue(); - return CurvePoint(it->first / 1000.0, it->second); + return CurvePoint(std::chrono::duration<double>(it->first).count(), 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 <std::chrono::nanoseconds, double> samples_; //[!] don't use std::multimap, see getLessEq() + std::pair<std::chrono::nanoseconds, double> lastSample_; //artificial most current record at the end of samples to visualize current time! }; @@ -1247,10 +1249,11 @@ public: const Statistics& syncStat, wxFrame* parentFrame, bool showProgress, + bool autoCloseDialog, const wxString& jobName, const Zstring& soundFileSyncComplete, bool ignoreErrors, - PostSyncAction postSyncAction); + PostSyncAction2 postSyncAction); ~SyncProgressDialogImpl() override; //call this in StatusUpdater derived class destructor at the LATEST(!) to prevent access to currentStatusUpdater @@ -1266,17 +1269,20 @@ public: bool getOptionIgnoreErrors() const override { return pnl_.m_checkBoxIgnoreErrors->GetValue(); } void setOptionIgnoreErrors(bool ignoreErrors) override { pnl_.m_checkBoxIgnoreErrors->SetValue(ignoreErrors); updateStaticGui(); } - PostSyncAction getOptionPostSyncAction() const override { return getEnumVal(enumPostSyncAction_, *pnl_.m_choicePostSyncAction); } + PostSyncAction2 getOptionPostSyncAction() const override { return getEnumVal(enumPostSyncAction_, *pnl_.m_choicePostSyncAction); } + bool getOptionAutoCloseDialog() const override { return pnl_.m_checkBoxAutoClose->GetValue(); } - void stopTimer() override //halt all internal counters! + void timerSetStatus(bool active) override { - //pnl.m_animCtrlSyncing->Stop(); - timeElapsed_.pause(); + if (active) + stopWatch_.resume(); + else + stopWatch_.pause(); } - void resumeTimer() override + + bool timerIsRunning() const override { - //pnl.m_animCtrlSyncing->Play(); - timeElapsed_.resume(); + return !stopWatch_.isPaused(); } private: @@ -1302,7 +1308,7 @@ private: const wxString jobName_; const Zstring soundFileSyncComplete_; - StopWatch timeElapsed_; + StopWatch stopWatch_; wxFrame* parentFrame_; //optional @@ -1318,10 +1324,10 @@ private: //remaining time std::unique_ptr<PerfCheck> perf_; - int64_t timeLastSpeedEstimateMs_ = -1000000; //used for calculating intervals between collecting perf samples + std::chrono::nanoseconds timeLastSpeedEstimate_ = std::chrono::seconds(-100); //used for calculating intervals between collecting perf samples //help calculate total speed - int64_t phaseStartMs_ = 0; //begin of current phase in [ms] + std::chrono::nanoseconds phaseStart_{}; //begin of current phase std::shared_ptr<CurveDataStatistics > curveDataBytes_ { std::make_shared<CurveDataStatistics>() }; std::shared_ptr<CurveDataStatistics > curveDataItems_ { std::make_shared<CurveDataStatistics>() }; @@ -1334,7 +1340,7 @@ private: std::unique_ptr<FfsTrayIcon> trayIcon_; //optional: if filled all other windows should be hidden and conversely std::unique_ptr<Taskbar> taskbar_; - EnumDescrList<PostSyncAction> enumPostSyncAction_; + EnumDescrList<PostSyncAction2> enumPostSyncAction_; }; @@ -1346,10 +1352,11 @@ SyncProgressDialogImpl<TopLevelDialog>::SyncProgressDialogImpl(long style, //wxF const Statistics& syncStat, wxFrame* parentFrame, bool showProgress, + bool autoCloseDialog, const wxString& jobName, const Zstring& soundFileSyncComplete, bool ignoreErrors, - PostSyncAction postSyncAction) : + PostSyncAction2 postSyncAction) : TopLevelDialog(parentFrame, wxID_ANY, wxString(), wxDefaultPosition, wxDefaultSize, style), //title is overwritten anyway in setExternalStatus() pnl_(*new SyncProgressPanelGenerated(this)), //ownership passed to "this" jobName_ (jobName), @@ -1395,7 +1402,7 @@ SyncProgressDialogImpl<TopLevelDialog>::SyncProgressDialogImpl(long style, //wxF //this->EnableCloseButton(false); //this is NOT honored on OS X or with ALT+F4 on Windows! -> why disable button at all?? - timeElapsed_.restart(); //measure total time + stopWatch_.restart(); //measure total time if (wxFrame* frame = getTaskbarFrame(*this)) try //try to get access to Windows 7/Ubuntu taskbar @@ -1455,14 +1462,16 @@ SyncProgressDialogImpl<TopLevelDialog>::SyncProgressDialogImpl(long style, //wxF //allow changing a few options dynamically during sync pnl_.m_checkBoxIgnoreErrors->SetValue(ignoreErrors); - enumPostSyncAction_. - add(PostSyncAction::SUMMARY, _("Show summary")). - add(PostSyncAction::EXIT, replaceCpy(_("E&xit"), L"&", L"")). //reuse translation - add(PostSyncAction::SLEEP, _("Sleep")). - add(PostSyncAction::SHUTDOWN, _("Shut down")); + enumPostSyncAction_.add(PostSyncAction2::NONE, L""); + if (parentFrame_) //enable EXIT option for gui mode sync + enumPostSyncAction_.add(PostSyncAction2::EXIT, replaceCpy(_("E&xit"), L"&", L"")); //reuse translation + enumPostSyncAction_.add(PostSyncAction2::SLEEP, _("System: Sleep")); + enumPostSyncAction_.add(PostSyncAction2::SHUTDOWN, _("System: Shut down")); setEnumVal(enumPostSyncAction_, *pnl_.m_choicePostSyncAction, postSyncAction); + pnl_.m_checkBoxAutoClose->SetValue(autoCloseDialog); + updateStaticGui(); //null-status will be shown while waiting for dir locks this->GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() @@ -1553,10 +1562,10 @@ void SyncProgressDialogImpl<TopLevelDialog>::initNewPhase() notifyProgressChange(); //make sure graphs get initial values //start new measurement - perf_ = std::make_unique<PerfCheck>(WINDOW_REMAINING_TIME_MS, WINDOW_BYTES_PER_SEC); - timeLastSpeedEstimateMs_ = -1000000; //some big number + perf_ = std::make_unique<PerfCheck>(WINDOW_REMAINING_TIME, WINDOW_BYTES_PER_SEC); + timeLastSpeedEstimate_ = std::chrono::seconds(-100); //make sure estimate is updated upon next check - phaseStartMs_ = timeElapsed_.timeMs(); + phaseStart_ = stopWatch_.elapsed(); updateProgressGui(false /*allowYield*/); } @@ -1578,8 +1587,8 @@ void SyncProgressDialogImpl<TopLevelDialog>::notifyProgressChange() //noexcept! const int64_t bytesCurrent = syncStat_->getBytesCurrent(syncStat_->currentPhase()); const int itemsCurrent = syncStat_->getItemsCurrent(syncStat_->currentPhase()); - curveDataBytes_->addRecord(timeElapsed_.timeMs(), bytesCurrent); - curveDataItems_->addRecord(timeElapsed_.timeMs(), itemsCurrent); + curveDataBytes_->addRecord(stopWatch_.elapsed(), bytesCurrent); + curveDataItems_->addRecord(stopWatch_.elapsed(), itemsCurrent); } break; } @@ -1625,8 +1634,15 @@ void SyncProgressDialogImpl<TopLevelDialog>::updateProgressGui(bool allowYield) if (!syncStat_) //sync not running return; + //normally we don't update the "static" GUI components here, but we have to make an exception + //if sync is cancelled (by user or error handling option) + if (syncStat_->getAbortStatus()) + updateStaticGui(); //called more than once after cancel... ok + + bool layoutChanged = false; //avoid screen flicker by calling layout() only if necessary - const int64_t timeNowMs = timeElapsed_.timeMs(); + const std::chrono::nanoseconds timeElapsed = stopWatch_.elapsed(); + const double timeElapsedDouble = std::chrono::duration<double>(timeElapsed).count(); //sync status text setText(*pnl_.m_staticTextStatus, replaceCpy(syncStat_->currentStatusText(), L'\n', L' ')); //no layout update for status texts! @@ -1670,13 +1686,13 @@ void SyncProgressDialogImpl<TopLevelDialog>::updateProgressGui(bool allowYield) //progress indicators if (trayIcon_.get()) trayIcon_->setProgress(fractionTotal); - if (taskbar_.get()) taskbar_->setProgress(fractionTotal); + if (taskbar_ .get()) taskbar_ ->setProgress(fractionTotal); - const double timeTotalSecTentative = bytesTotal == bytesCurrent ? timeNowMs / 1000.0 : std::max(curveDataBytesTotal_->getValueX(), timeNowMs / 1000.0); + const double timeTotalSecTentative = bytesTotal == bytesCurrent ? timeElapsedDouble : std::max(curveDataBytesTotal_->getValueX(), timeElapsedDouble); //constant line graph - curveDataBytesCurrent_->setValue(timeNowMs / 1000.0, timeTotalSecTentative, bytesCurrent); - curveDataItemsCurrent_->setValue(timeNowMs / 1000.0, timeTotalSecTentative, itemsCurrent); + curveDataBytesCurrent_->setValue(timeElapsedDouble, timeTotalSecTentative, bytesCurrent); + curveDataItemsCurrent_->setValue(timeElapsedDouble, timeTotalSecTentative, itemsCurrent); //tentatively update total time, may be improved on below: curveDataBytesTotal_->setValue(timeTotalSecTentative, bytesTotal); @@ -1684,8 +1700,8 @@ void SyncProgressDialogImpl<TopLevelDialog>::updateProgressGui(bool allowYield) //even though notifyProgressChange() already set the latest data, let's add another sample to have all curves consider "timeNowMs" //no problem with adding too many records: CurveDataStatistics will remove duplicate entries! - curveDataBytes_->addRecord(timeNowMs, bytesCurrent); - curveDataItems_->addRecord(timeNowMs, itemsCurrent); + curveDataBytes_->addRecord(timeElapsed, bytesCurrent); + curveDataItems_->addRecord(timeElapsed, itemsCurrent); //remaining item and byte count setText(*pnl_.m_staticTextItemsRemaining, formatNumber(itemsTotal - itemsCurrent), &layoutChanged); @@ -1695,12 +1711,12 @@ void SyncProgressDialogImpl<TopLevelDialog>::updateProgressGui(bool allowYield) //remaining time and speed assert(perf_); if (perf_) - if (numeric::dist(timeLastSpeedEstimateMs_, timeNowMs) >= 500) + if (numeric::dist(timeLastSpeedEstimate_, timeElapsed) >= std::chrono::milliseconds(500)) { - timeLastSpeedEstimateMs_ = timeNowMs; + timeLastSpeedEstimate_ = timeElapsed; - if (numeric::dist(phaseStartMs_, timeNowMs) >= 1000) //discard stats for first second: probably messy - perf_->addSample(itemsCurrent, bytesCurrent, timeNowMs); + if (numeric::dist(phaseStart_, timeElapsed) >= std::chrono::seconds(1)) //discard stats for first second: probably messy + perf_->addSample(timeElapsed, itemsCurrent, bytesCurrent); //current speed -> Win 7 copy uses 1 sec update interval instead Opt<std::wstring> bps = perf_->getBytesPerSecond(); @@ -1715,17 +1731,16 @@ void SyncProgressDialogImpl<TopLevelDialog>::updateProgressGui(bool allowYield) //update estimated total time marker with precision of "10% remaining time" only to avoid needless jumping around: const double timeRemainingSec = remTimeSec ? *remTimeSec : 0; - const double timeTotalSec = timeNowMs / 1000.0 + timeRemainingSec; + const double timeTotalSec = timeElapsedDouble + timeRemainingSec; if (numeric::dist(curveDataBytesTotal_->getValueX(), timeTotalSec) > 0.1 * timeRemainingSec) { curveDataBytesTotal_->setValueX(timeTotalSec); curveDataItemsTotal_->setValueX(timeTotalSec); //don't forget to update these, too: - curveDataBytesCurrent_->setValue(timeNowMs / 1000.0, timeTotalSec, bytesCurrent); - curveDataItemsCurrent_->setValue(timeNowMs / 1000.0, timeTotalSec, itemsCurrent); + curveDataBytesCurrent_->setValue(timeElapsedDouble, timeTotalSec, bytesCurrent); + curveDataItemsCurrent_->setValue(timeElapsedDouble, timeTotalSec, itemsCurrent); } } - break; } } @@ -1733,8 +1748,8 @@ void SyncProgressDialogImpl<TopLevelDialog>::updateProgressGui(bool allowYield) pnl_.m_panelGraphBytes->Refresh(); pnl_.m_panelGraphItems->Refresh(); - //time elapsed - const int64_t timeElapSec = timeNowMs / 1000; + const int64_t timeElapSec = std::chrono::duration_cast<std::chrono::seconds>(timeElapsed).count(); + setText(*pnl_.m_staticTextTimeElapsed, timeElapSec < 3600 ? wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") : @@ -1765,20 +1780,19 @@ void SyncProgressDialogImpl<TopLevelDialog>::updateProgressGui(bool allowYield) 2. no crash on Ubuntu GCC 3. following makes GCC crash already during compilation: auto dfd = zen::makeGuard([this]{ resumeTimer(); }); */ - - stopTimer(); + timerSetStatus(false /*active*/); while (paused_) { wxTheApp->Yield(); //receive UI message that end pause OR forceful termination! //*first* refresh GUI (removing flicker) before sleeping! - std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS)); + std::this_thread::sleep_for(UI_UPDATE_INTERVAL); } //after SyncProgressDialogImpl::OnClose() called wxWindow::Destroy() on OS X this instance is instantly toast! if (wereDead_) - return; //GTFO and don't call this->resumeTimer() + return; //GTFO and don't call this->timerSetStatus() - resumeTimer(); + timerSetStatus(true /*active*/); } else /* @@ -1796,65 +1810,64 @@ void SyncProgressDialogImpl<TopLevelDialog>::updateProgressGui(bool allowYield) template <class TopLevelDialog> void SyncProgressDialogImpl<TopLevelDialog>::updateStaticGui() //depends on "syncStat_, paused_, finalResult" { - auto setStatusBitmap = [&](const wchar_t* bmpName, const wxString& tooltip) + const wxString dlgPhaseTxt = getDialogPhaseText(syncStat_, paused_, finalResult_); + + pnl_.m_staticTextPhase->SetLabel(dlgPhaseTxt); + //pnl_.m_bitmapStatus->SetToolTip(dlgPhaseTxt); -> redundant + + auto setStatusBitmap = [&](const wchar_t* bmpName) { pnl_.m_bitmapStatus->SetBitmap(getResourceImage(bmpName)); - pnl_.m_bitmapStatus->SetToolTip(tooltip); pnl_.m_bitmapStatus->Show(); - //pnl.m_animCtrlSyncing->Hide(); }; - const wxString dlgStatusTxt = getDialogPhaseText(syncStat_, paused_, finalResult_); - - pnl_.m_staticTextPhase->SetLabel(dlgStatusTxt); - //status bitmap if (syncStat_) //sync running { if (paused_) - setStatusBitmap(L"status_pause", dlgStatusTxt); + setStatusBitmap(L"status_pause"); else - switch (syncStat_->currentPhase()) - { - case ProcessCallback::PHASE_NONE: - //pnl.m_animCtrlSyncing->Hide(); - pnl_.m_bitmapStatus->Hide(); - break; + { + if (syncStat_->getAbortStatus()) + setStatusBitmap(L"status_aborted"); + else + switch (syncStat_->currentPhase()) + { + case ProcessCallback::PHASE_NONE: + pnl_.m_bitmapStatus->Hide(); + break; - case ProcessCallback::PHASE_SCANNING: - setStatusBitmap(L"status_scanning", dlgStatusTxt); - break; + case ProcessCallback::PHASE_SCANNING: + setStatusBitmap(L"status_scanning"); + break; - case ProcessCallback::PHASE_COMPARING_CONTENT: - setStatusBitmap(L"status_binary_compare", dlgStatusTxt); - break; + case ProcessCallback::PHASE_COMPARING_CONTENT: + setStatusBitmap(L"status_binary_compare"); + break; - case ProcessCallback::PHASE_SYNCHRONIZING: - pnl_.m_bitmapStatus->SetBitmap(getResourceImage(L"status_syncing")); - pnl_.m_bitmapStatus->SetToolTip(dlgStatusTxt); - pnl_.m_bitmapStatus->Show(); - //pnl.m_animCtrlSyncing->Show(); - //pnl.m_animCtrlSyncing->SetToolTip(dlgStatusTxt); - break; - } + case ProcessCallback::PHASE_SYNCHRONIZING: + setStatusBitmap(L"status_syncing"); + break; + } + } } else //sync finished switch (finalResult_) { case RESULT_ABORTED: - setStatusBitmap(L"status_aborted", _("Synchronization stopped")); + setStatusBitmap(L"status_aborted"); break; case RESULT_FINISHED_WITH_ERROR: - setStatusBitmap(L"status_finished_errors", _("Synchronization completed with errors")); + setStatusBitmap(L"status_finished_errors"); break; case RESULT_FINISHED_WITH_WARNINGS: - setStatusBitmap(L"status_finished_warnings", _("Synchronization completed with warnings")); + setStatusBitmap(L"status_finished_warnings"); break; case RESULT_FINISHED_WITH_SUCCESS: - setStatusBitmap(L"status_finished_success", _("Synchronization completed successfully")); + setStatusBitmap(L"status_finished_success"); break; } @@ -1960,10 +1973,11 @@ void SyncProgressDialogImpl<TopLevelDialog>::showSummary(SyncResult resultId, co assert(bytesCurrent <= bytesTotal); //set overall speed (instead of current speed) - const int64_t timeDelta = timeElapsed_.timeMs() - phaseStartMs_; //we need to consider "time within current phase" not total "timeElapsed"! + const double timeDelta = std::chrono::duration<double>(stopWatch_.elapsed() - phaseStart_).count(); + //we need to consider "time within current phase" not total "timeElapsed"! - const wxString overallBytesPerSecond = timeDelta == 0 ? std::wstring() : formatFilesizeShort(bytesCurrent * 1000 / timeDelta) + _("/sec"); - const wxString overallItemsPerSecond = timeDelta == 0 ? std::wstring() : replaceCpy(_("%x items/sec"), L"%x", formatThreeDigitPrecision(itemsCurrent * 1000.0 / timeDelta)); + const wxString overallBytesPerSecond = numeric::isNull(timeDelta) ? std::wstring() : formatFilesizeShort(numeric::round(bytesCurrent / timeDelta)) + _("/sec"); + const wxString overallItemsPerSecond = numeric::isNull(timeDelta) ? std::wstring() : replaceCpy(_("%x items/sec"), L"%x", formatThreeDigitPrecision(itemsCurrent / timeDelta)); pnl_.m_panelGraphBytes->setAttributes(pnl_.m_panelGraphBytes->getAttributes().setCornerText(overallBytesPerSecond, Graph2D::CORNER_TOP_LEFT)); pnl_.m_panelGraphItems->setAttributes(pnl_.m_panelGraphItems->getAttributes().setCornerText(overallItemsPerSecond, Graph2D::CORNER_TOP_LEFT)); @@ -2007,6 +2021,9 @@ void SyncProgressDialogImpl<TopLevelDialog>::showSummary(SyncResult resultId, co pnl_.bSizerProgressFooter->Show(false); + if (!parentFrame_) //hide checkbox for batch mode sync (where value won't be retrieved after close) + pnl_.m_checkBoxAutoClose->Hide(); + //set std order after button visibility was set setStandardButtonLayout(*pnl_.bSizerStdButtons, StdButtons().setAffirmative(pnl_.m_buttonClose)); @@ -2039,7 +2056,7 @@ void SyncProgressDialogImpl<TopLevelDialog>::showSummary(SyncResult resultId, co //bSizerHoldStretch->Insert(0, logPanel, 1, wxEXPAND); //show log instead of graph if errors occurred! (not required for ignored warnings) - if (log.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR) > 0) + if (log.getItemCount(MSG_TYPE_ERROR | MSG_TYPE_FATAL_ERROR) > 0) pnl_.m_notebookResult->ChangeSelection(pagePosLog); //fill image list to cope with wxNotebook image setting design desaster... @@ -2108,11 +2125,10 @@ void SyncProgressDialogImpl<TopLevelDialog>::OnOkay(wxCommandEvent& event) template <class TopLevelDialog> void SyncProgressDialogImpl<TopLevelDialog>::OnCancel(wxCommandEvent& event) { + if (abortCb_) abortCb_->userRequestAbort(); + paused_ = false; updateStaticGui(); //update status + pause button - - if (abortCb_) - abortCb_->userRequestAbort(); //no Layout() or UI-update here to avoid cascaded Yield()-call! } @@ -2225,15 +2241,16 @@ void SyncProgressDialogImpl<TopLevelDialog>::resumeFromSystray() //######################################################################################## -SyncProgressDialog* createProgressDialog(zen::AbortCallback& abortCb, - const std::function<void()>& notifyWindowTerminate, //note: user closing window cannot be prevented on OS X! (And neither on Windows during system shutdown!) - const zen::Statistics& syncStat, - wxFrame* parentWindow, //may be nullptr - bool showProgress, - const wxString& jobName, - const Zstring& soundFileSyncComplete, - bool ignoreErrors, - PostSyncAction postSyncAction) +SyncProgressDialog* fff::createProgressDialog(AbortCallback& abortCb, + const std::function<void()>& notifyWindowTerminate, //note: user closing window cannot be prevented on OS X! (And neither on Windows during system shutdown!) + const Statistics& syncStat, + wxFrame* parentWindow, //may be nullptr + bool showProgress, + bool autoCloseDialog, + const wxString& jobName, + const Zstring& soundFileSyncComplete, + bool ignoreErrors, + PostSyncAction2 postSyncAction) { if (parentWindow) //sync from GUI { @@ -2241,13 +2258,13 @@ SyncProgressDialog* createProgressDialog(zen::AbortCallback& abortCb, //https://groups.google.com/forum/#!topic/wx-users/J5SjjLaBOQE return new SyncProgressDialogImpl<wxDialog>(wxDEFAULT_DIALOG_STYLE | wxMAXIMIZE_BOX | wxMINIMIZE_BOX | wxRESIZE_BORDER, [&](wxDialog& progDlg) { return parentWindow; }, - abortCb, notifyWindowTerminate, syncStat, parentWindow, showProgress, jobName, soundFileSyncComplete, ignoreErrors, postSyncAction); + abortCb, notifyWindowTerminate, syncStat, parentWindow, showProgress, autoCloseDialog, jobName, soundFileSyncComplete, ignoreErrors, postSyncAction); } else //FFS batch job { auto dlg = new SyncProgressDialogImpl<wxFrame>(wxDEFAULT_FRAME_STYLE, [](wxFrame& progDlg) { return &progDlg; }, - abortCb, notifyWindowTerminate, syncStat, parentWindow, showProgress, jobName, soundFileSyncComplete, ignoreErrors, postSyncAction); + abortCb, notifyWindowTerminate, syncStat, parentWindow, showProgress, autoCloseDialog, jobName, soundFileSyncComplete, ignoreErrors, postSyncAction); //only top level windows should have an icon: dlg->SetIcon(getFfsIcon()); diff --git a/FreeFileSync/Source/ui/progress_indicator.h b/FreeFileSync/Source/ui/progress_indicator.h index 4d230429..3562db96 100755 --- a/FreeFileSync/Source/ui/progress_indicator.h +++ b/FreeFileSync/Source/ui/progress_indicator.h @@ -15,6 +15,8 @@ #include "../lib/process_xml.h" +namespace fff +{ class CompareProgressDialog { public: @@ -22,7 +24,7 @@ public: wxWindow* getAsWindow(); //convenience! don't abuse! - void init(const zen::Statistics& syncStat, bool ignoreErrors); //begin of sync: make visible, set pointer to "syncStat", initialize all status values + void init(const Statistics& syncStat, bool ignoreErrors); //begin of sync: make visible, set pointer to "syncStat", initialize all status values void teardown(); //end of sync: hide again, clear pointer to "syncStat" void initNewPhase(); //call after "StatusHandler::initNewPhase" @@ -41,6 +43,14 @@ private: //StatusHandlerFloatingDialog will internally process Window messages => disable GUI controls to avoid unexpected callbacks! +enum class PostSyncAction2 +{ + NONE, + EXIT, + SLEEP, + SHUTDOWN +}; + struct SyncProgressDialog { enum SyncResult @@ -66,36 +76,39 @@ struct SyncProgressDialog //allow changing a few options dynamically during sync virtual bool getOptionIgnoreErrors() const = 0; virtual void setOptionIgnoreErrors(bool ignoreError) = 0; - virtual xmlAccess::PostSyncAction getOptionPostSyncAction() const = 0; + virtual PostSyncAction2 getOptionPostSyncAction() const = 0; + virtual bool getOptionAutoCloseDialog() const = 0; - virtual void stopTimer () = 0; //halt all internal timers! - virtual void resumeTimer() = 0; // + virtual void timerSetStatus(bool active) = 0; //start/stop all internal timers! + virtual bool timerIsRunning() const = 0; protected: ~SyncProgressDialog() {} }; -SyncProgressDialog* createProgressDialog(zen::AbortCallback& abortCb, +SyncProgressDialog* createProgressDialog(AbortCallback& abortCb, const std::function<void()>& notifyWindowTerminate, //note: user closing window cannot be prevented on OS X! (And neither on Windows during system shutdown!) - const zen::Statistics& syncStat, + const Statistics& syncStat, wxFrame* parentWindow, //may be nullptr bool showProgress, + bool autoCloseDialog, const wxString& jobName, const Zstring& soundFileSyncComplete, bool ignoreErrors, - xmlAccess::PostSyncAction postSyncAction); + PostSyncAction2 postSyncAction); //DON'T delete the pointer! it will be deleted by the user clicking "OK/Cancel"/wxWindow::Destroy() after showSummary() or closeDirectly() class PauseTimers { public: - PauseTimers(SyncProgressDialog& ss) : ss_(ss) { ss_.stopTimer(); } - ~PauseTimers() { ss_.resumeTimer(); } + PauseTimers(SyncProgressDialog& ss) : ss_(ss), timerWasRunning_(ss.timerIsRunning()) { ss_.timerSetStatus(false); } + ~PauseTimers() { ss_.timerSetStatus(timerWasRunning_); } //restore previous state: support recursive calls private: SyncProgressDialog& ss_; + const bool timerWasRunning_; }; - +} #endif //PROGRESS_INDICATOR_H_8037493452348 diff --git a/FreeFileSync/Source/ui/search.cpp b/FreeFileSync/Source/ui/search.cpp index 6bcfed34..dd46c7f8 100755 --- a/FreeFileSync/Source/ui/search.cpp +++ b/FreeFileSync/Source/ui/search.cpp @@ -9,6 +9,7 @@ #include <zen/perf.h> using namespace zen; +using namespace fff; namespace @@ -72,7 +73,7 @@ ptrdiff_t findRow(const Grid& grid, //return -1 if no matching row found } -std::pair<const Grid*, ptrdiff_t> zen::findGridMatch(const Grid& grid1, const Grid& grid2, const std::wstring& searchString, bool respectCase, bool searchAscending) +std::pair<const Grid*, ptrdiff_t> fff::findGridMatch(const Grid& grid1, const Grid& grid2, const std::wstring& searchString, bool respectCase, bool searchAscending) { //PERF_START @@ -92,7 +93,7 @@ std::pair<const Grid*, ptrdiff_t> zen::findGridMatch(const Grid& grid1, const Gr findRow<false>(grid, searchString, searchAscending, rowFirst, rowLast); if (targetRow >= 0) { - result = std::make_pair(&grid, targetRow); + result = { &grid, targetRow }; return true; } return false; diff --git a/FreeFileSync/Source/ui/search.h b/FreeFileSync/Source/ui/search.h index 362460d1..81560f7c 100755 --- a/FreeFileSync/Source/ui/search.h +++ b/FreeFileSync/Source/ui/search.h @@ -9,9 +9,10 @@ #include <wx+/grid.h> -namespace zen + +namespace fff { -std::pair<const Grid*, ptrdiff_t> findGridMatch(const Grid& grid1, const Grid& grid2, const std::wstring& searchString, bool respectCase, bool searchAscending); +std::pair<const zen::Grid*, ptrdiff_t> findGridMatch(const zen::Grid& grid1, const zen::Grid& grid2, const std::wstring& searchString, bool respectCase, bool searchAscending); //returns (grid/row) where the value was found, (nullptr, -1) if not found } diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp index 9c4b9196..c3889a94 100755 --- a/FreeFileSync/Source/ui/small_dlgs.cpp +++ b/FreeFileSync/Source/ui/small_dlgs.cpp @@ -5,7 +5,6 @@ // ***************************************************************************** #include "small_dlgs.h" -//#include <chrono> #include <zen/time.h> #include <zen/format_unit.h> #include <zen/build_info.h> @@ -35,6 +34,7 @@ using namespace zen; +using namespace fff; class AboutDlg : public AboutDlgGenerated @@ -106,7 +106,7 @@ AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent) //generate logo: put *after* first Fit() Layout(); //make sure m_panelLogo has final width (required by wxGTK) - wxImage appnameImg = createImageFromText(wxString(L"FreeFileSync ") + zen::ffsVersion, + wxImage appnameImg = createImageFromText(wxString(L"FreeFileSync ") + ffsVersion, wxFont(wxNORMAL_FONT->GetPointSize() * 1.8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, L"Tahoma"), *wxBLACK); //accessibility: align foreground/background colors! wxImage buildImg = createImageFromText(replaceCpy(_("Build: %x"), L"%x", build), @@ -153,7 +153,7 @@ void AboutDlg::onLocalKeyEvent(wxKeyEvent& event) //process key events without e } -void zen::showAboutDialog(wxWindow* parent) +void fff::showAboutDialog(wxWindow* parent) { AboutDlg aboutDlg(parent); aboutDlg.ShowModal(); @@ -224,7 +224,7 @@ CopyToDialog::CopyToDialog(wxWindow* parent, http://trac.wxwidgets.org/ticket/14823 "Menu not disabled when showing modal dialogs in wxGTK under Unity" */ - const std::pair<std::wstring, int> selectionInfo = zen::getSelectedItemsAsString(rowsOnLeft, rowsOnRight); + const std::pair<std::wstring, int> selectionInfo = getSelectedItemsAsString(rowsOnLeft, rowsOnRight); const wxString header = _P("Copy the following item to another folder?", "Copy the following %x items to another folder?", selectionInfo.second); @@ -278,7 +278,7 @@ void CopyToDialog::OnOK(wxCommandEvent& event) } -ReturnSmallDlg::ButtonPressed zen::showCopyToDialog(wxWindow* parent, +ReturnSmallDlg::ButtonPressed fff::showCopyToDialog(wxWindow* parent, const std::vector<const FileSystemObject*>& rowsOnLeft, const std::vector<const FileSystemObject*>& rowsOnRight, Zstring& lastUsedPath, @@ -359,8 +359,8 @@ DeleteDialog::DeleteDialog(wxWindow* parent, void DeleteDialog::updateGui() { - const std::pair<std::wstring, int> delInfo = zen::getSelectedItemsAsString(rowsToDeleteOnLeft_, - rowsToDeleteOnRight_); + const std::pair<std::wstring, int> delInfo = getSelectedItemsAsString(rowsToDeleteOnLeft_, + rowsToDeleteOnRight_); wxString header; if (m_checkBoxUseRecycler->GetValue()) { @@ -411,7 +411,7 @@ void DeleteDialog::OnOK(wxCommandEvent& event) } -ReturnSmallDlg::ButtonPressed zen::showDeleteDialog(wxWindow* parent, +ReturnSmallDlg::ButtonPressed fff::showDeleteDialog(wxWindow* parent, const std::vector<const FileSystemObject*>& rowsOnLeft, const std::vector<const FileSystemObject*>& rowsOnRight, bool& useRecycleBin) @@ -427,7 +427,7 @@ class SyncConfirmationDlg : public SyncConfirmationDlgGenerated public: SyncConfirmationDlg(wxWindow* parent, const wxString& variantName, - const zen::SyncStatistics& st, + const SyncStatistics& st, bool& dontShowAgain); private: void OnStartSync(wxCommandEvent& event) override; @@ -509,9 +509,9 @@ void SyncConfirmationDlg::OnStartSync(wxCommandEvent& event) } -ReturnSmallDlg::ButtonPressed zen::showSyncConfirmationDlg(wxWindow* parent, +ReturnSmallDlg::ButtonPressed fff::showSyncConfirmationDlg(wxWindow* parent, const wxString& variantName, - const zen::SyncStatistics& statistics, + const SyncStatistics& statistics, bool& dontShowAgain) { SyncConfirmationDlg dlg(parent, @@ -526,7 +526,7 @@ ReturnSmallDlg::ButtonPressed zen::showSyncConfirmationDlg(wxWindow* parent, class OptionsDlg : public OptionsDlgGenerated { public: - OptionsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings); + OptionsDlg(wxWindow* parent, XmlGlobalSettings& globalCfg); private: void OnOkay (wxCommandEvent& event) override; @@ -545,19 +545,29 @@ private: void OnToggleAutoRetryCount(wxCommandEvent& event) override { updateGui(); } - void setExtApp(const xmlAccess::ExternalApps& extApp); - xmlAccess::ExternalApps getExtApp() const; + void setExtApp(const std::vector<ExternalApp>& extApp); + std::vector<ExternalApp> getExtApp() const; std::map<std::wstring, std::wstring> descriptionTransToEng_; //"translated description" -> "english" mapping for external application config + //parameters NOT owned by GUI: + ConfirmationDialogs confirmDlgs_; + WarningDialogs warnDlgs_; + bool autoCloseProgressDialog_; + + const XmlGlobalSettings defaultCfg_; + //output-only parameters: - xmlAccess::XmlGlobalSettings& globalSettingsOut_; + XmlGlobalSettings& globalCfgOut_; }; -OptionsDlg::OptionsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings) : +OptionsDlg::OptionsDlg(wxWindow* parent, XmlGlobalSettings& globalSettings) : OptionsDlgGenerated(parent), - globalSettingsOut_(globalSettings) + confirmDlgs_(globalSettings.confirmDlgs), + warnDlgs_ (globalSettings.warnDlgs), + autoCloseProgressDialog_(globalSettings.autoCloseProgressDialog), + globalCfgOut_(globalSettings) { setStandardButtonLayout(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOkay).setCancel(m_buttonCancel)); @@ -578,7 +588,7 @@ OptionsDlg::OptionsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSet m_spinCtrlAutoRetryCount->SetValue(globalSettings.automaticRetryCount); m_spinCtrlAutoRetryDelay->SetValue(globalSettings.automaticRetryDelay); - setExtApp(globalSettings.gui.externelApplications); + setExtApp(globalSettings.gui.externalApps); updateGui(); @@ -631,91 +641,90 @@ void OptionsDlg::updateGui() { const bool autoRetryActive = m_spinCtrlAutoRetryCount->GetValue() > 0; m_staticTextAutoRetryDelay->Enable(autoRetryActive); - m_spinCtrlAutoRetryDelay->Enable(autoRetryActive); + m_spinCtrlAutoRetryDelay ->Enable(autoRetryActive); + + m_buttonResetDialogs->Enable(confirmDlgs_ != defaultCfg_.confirmDlgs || + warnDlgs_ != defaultCfg_.warnDlgs || + autoCloseProgressDialog_ != defaultCfg_.autoCloseProgressDialog); } -void OptionsDlg::OnOkay(wxCommandEvent& event) +void OptionsDlg::OnResetDialogs(wxCommandEvent& event) { - //write settings only when okay-button is pressed (except hidden dialog reset)! - globalSettingsOut_.failSafeFileCopy = m_checkBoxFailSafe->GetValue(); - globalSettingsOut_.copyLockedFiles = m_checkBoxCopyLocked->GetValue(); - globalSettingsOut_.copyFilePermissions = m_checkBoxCopyPermissions->GetValue(); - - globalSettingsOut_.automaticRetryCount = m_spinCtrlAutoRetryCount->GetValue(); - globalSettingsOut_.automaticRetryDelay = m_spinCtrlAutoRetryDelay->GetValue(); - - globalSettingsOut_.gui.externelApplications = getExtApp(); - - EndModal(ReturnSmallDlg::BUTTON_OKAY); + confirmDlgs_ = defaultCfg_.confirmDlgs; + warnDlgs_ = defaultCfg_.warnDlgs; + autoCloseProgressDialog_ = defaultCfg_.autoCloseProgressDialog; + updateGui(); } -void OptionsDlg::OnResetDialogs(wxCommandEvent& event) +void OptionsDlg::OnDefault(wxCommandEvent& event) { - switch (showConfirmationDialog(this, DialogInfoType::INFO, - PopupDialogCfg().setMainInstructions(_("Show hidden dialogs and warning messages again?")), - _("&Show"))) - { - case ConfirmationButton::ACCEPT: - globalSettingsOut_.optDialogs = xmlAccess::OptionalDialogs(); - break; - case ConfirmationButton::CANCEL: - break; - } + m_checkBoxFailSafe ->SetValue(defaultCfg_.failSafeFileCopy); + m_checkBoxCopyLocked ->SetValue(defaultCfg_.copyLockedFiles); + m_checkBoxCopyPermissions->SetValue(defaultCfg_.copyFilePermissions); + + m_spinCtrlAutoRetryCount->SetValue(defaultCfg_.automaticRetryCount); + m_spinCtrlAutoRetryDelay->SetValue(defaultCfg_.automaticRetryDelay); + + setExtApp(defaultCfg_.gui.externalApps); + updateGui(); } -void OptionsDlg::OnDefault(wxCommandEvent& event) +void OptionsDlg::OnOkay(wxCommandEvent& event) { - const xmlAccess::XmlGlobalSettings defaultCfg; + //write settings only when okay-button is pressed (except hidden dialog reset)! + globalCfgOut_.failSafeFileCopy = m_checkBoxFailSafe->GetValue(); + globalCfgOut_.copyLockedFiles = m_checkBoxCopyLocked->GetValue(); + globalCfgOut_.copyFilePermissions = m_checkBoxCopyPermissions->GetValue(); - m_checkBoxFailSafe ->SetValue(defaultCfg.failSafeFileCopy); - m_checkBoxCopyLocked ->SetValue(defaultCfg.copyLockedFiles); - m_checkBoxCopyPermissions->SetValue(defaultCfg.copyFilePermissions); + globalCfgOut_.automaticRetryCount = m_spinCtrlAutoRetryCount->GetValue(); + globalCfgOut_.automaticRetryDelay = m_spinCtrlAutoRetryDelay->GetValue(); - m_spinCtrlAutoRetryCount->SetValue(defaultCfg.automaticRetryCount); - m_spinCtrlAutoRetryDelay->SetValue(defaultCfg.automaticRetryDelay); + globalCfgOut_.gui.externalApps = getExtApp(); - setExtApp(defaultCfg.gui.externelApplications); + globalCfgOut_.confirmDlgs = confirmDlgs_; + globalCfgOut_.warnDlgs = warnDlgs_; + globalCfgOut_.autoCloseProgressDialog = autoCloseProgressDialog_; - updateGui(); + EndModal(ReturnSmallDlg::BUTTON_OKAY); } -void OptionsDlg::setExtApp(const xmlAccess::ExternalApps& extApp) +void OptionsDlg::setExtApp(const std::vector<ExternalApp>& extApps) { - auto extAppTmp = extApp; - erase_if(extAppTmp, [](auto& entry) { return entry.first.empty() && entry.second.empty(); }); + auto extAppsTmp = extApps; + erase_if(extAppsTmp, [](auto& entry) { return entry.description.empty() && entry.cmdLine.empty(); }); - extAppTmp.emplace_back(); //append empty row to facilitate insertions by user + extAppsTmp.emplace_back(); //append empty row to facilitate insertions by user const int rowCount = m_gridCustomCommand->GetNumberRows(); if (rowCount > 0) m_gridCustomCommand->DeleteRows(0, rowCount); - m_gridCustomCommand->AppendRows(static_cast<int>(extAppTmp.size())); - for (auto it = extAppTmp.begin(); it != extAppTmp.end(); ++it) + m_gridCustomCommand->AppendRows(static_cast<int>(extAppsTmp.size())); + for (auto it = extAppsTmp.begin(); it != extAppsTmp.end(); ++it) { - const int row = it - extAppTmp.begin(); + const int row = it - extAppsTmp.begin(); - const std::wstring description = zen::translate(it->first); - if (description != it->first) //remember english description to save in GlobalSettings.xml later rather than hard-code translation - descriptionTransToEng_[description] = it->first; + const std::wstring description = zen::translate(it->description); + if (description != it->description) //remember english description to save in GlobalSettings.xml later rather than hard-code translation + descriptionTransToEng_[description] = it->description; m_gridCustomCommand->SetCellValue(row, 0, description); - m_gridCustomCommand->SetCellValue(row, 1, utfTo<wxString>(it->second)); //commandline + m_gridCustomCommand->SetCellValue(row, 1, utfTo<wxString>(it->cmdLine)); //commandline } } -xmlAccess::ExternalApps OptionsDlg::getExtApp() const +std::vector<ExternalApp> OptionsDlg::getExtApp() const { - xmlAccess::ExternalApps output; + std::vector<ExternalApp> output; for (int i = 0; i < m_gridCustomCommand->GetNumberRows(); ++i) { auto description = copyStringTo<std::wstring>(m_gridCustomCommand->GetCellValue(i, 0)); - auto commandline = utfTo<Zstring> (m_gridCustomCommand->GetCellValue(i, 1)); + auto commandline = utfTo<Zstring>(m_gridCustomCommand->GetCellValue(i, 1)); //try to undo translation of description for GlobalSettings.xml auto it = descriptionTransToEng_.find(description); @@ -723,7 +732,7 @@ xmlAccess::ExternalApps OptionsDlg::getExtApp() const description = it->second; if (!description.empty() || !commandline.empty()) - output.emplace_back(description, commandline); + output.push_back({ description, commandline }); } return output; } @@ -754,9 +763,9 @@ void OptionsDlg::OnRemoveRow(wxCommandEvent& event) } -ReturnSmallDlg::ButtonPressed zen::showOptionsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings) +ReturnSmallDlg::ButtonPressed fff::showOptionsDlg(wxWindow* parent, XmlGlobalSettings& globalCfg) { - OptionsDlg dlg(parent, globalSettings); + OptionsDlg dlg(parent, globalCfg); return static_cast<ReturnSmallDlg::ButtonPressed>(dlg.ShowModal()); } @@ -866,7 +875,7 @@ void SelectTimespanDlg::OnOkay(wxCommandEvent& event) } -ReturnSmallDlg::ButtonPressed zen::showSelectTimespanDlg(wxWindow* parent, time_t& timeFrom, time_t& timeTo) +ReturnSmallDlg::ButtonPressed fff::showSelectTimespanDlg(wxWindow* parent, time_t& timeFrom, time_t& timeTo) { SelectTimespanDlg timeSpanDlg(parent, timeFrom, timeTo); return static_cast<ReturnSmallDlg::ButtonPressed>(timeSpanDlg.ShowModal()); @@ -915,7 +924,7 @@ void CfgHighlightDlg::OnOkay(wxCommandEvent& event) } -ReturnSmallDlg::ButtonPressed zen::showCfgHighlightDlg(wxWindow* parent, int& cfgHistSyncOverdueDays) +ReturnSmallDlg::ButtonPressed fff::showCfgHighlightDlg(wxWindow* parent, int& cfgHistSyncOverdueDays) { CfgHighlightDlg cfgHighDlg(parent, cfgHistSyncOverdueDays); return static_cast<ReturnSmallDlg::ButtonPressed>(cfgHighDlg.ShowModal()); @@ -992,7 +1001,7 @@ void ActivationDlg::OnActivateOffline(wxCommandEvent& event) } -ReturnActivationDlg zen::showActivationDialog(wxWindow* parent, const std::wstring& lastErrorMsg, const std::wstring& manualActivationUrl, std::wstring& manualActivationKey) +ReturnActivationDlg fff::showActivationDialog(wxWindow* parent, const std::wstring& lastErrorMsg, const std::wstring& manualActivationUrl, std::wstring& manualActivationKey) { ActivationDlg dlg(parent, lastErrorMsg, manualActivationUrl, manualActivationKey); return static_cast<ReturnActivationDlg>(dlg.ShowModal()); @@ -1062,15 +1071,18 @@ DownloadProgressWindow::Impl::Impl(wxWindow* parent, int64_t fileSizeTotal) : Center(); //needs to be re-applied after a dialog size change! Show(); + //clear gui flicker: window must be visible to make this work! + ::wxSafeYield(); //at least on OS X a real Yield() is required to flush pending GUI updates; Update() is not enough + m_buttonCancel->SetFocus(); } -zen::DownloadProgressWindow::DownloadProgressWindow(wxWindow* parent, int64_t fileSizeTotal) : +DownloadProgressWindow::DownloadProgressWindow(wxWindow* parent, int64_t fileSizeTotal) : pimpl_(new DownloadProgressWindow::Impl(parent, fileSizeTotal)) {} -zen::DownloadProgressWindow::~DownloadProgressWindow() { pimpl_->Destroy(); } +DownloadProgressWindow::~DownloadProgressWindow() { pimpl_->Destroy(); } -void zen::DownloadProgressWindow::notifyNewFile(const Zstring& filePath) { pimpl_->notifyNewFile(filePath); } -void zen::DownloadProgressWindow::notifyProgress(int64_t delta) { pimpl_->notifyProgress(delta); } -void zen::DownloadProgressWindow::requestUiRefresh() { pimpl_->requestUiRefresh(); } //throw CancelPressed +void DownloadProgressWindow::notifyNewFile(const Zstring& filePath) { pimpl_->notifyNewFile(filePath); } +void DownloadProgressWindow::notifyProgress(int64_t delta) { pimpl_->notifyProgress(delta); } +void DownloadProgressWindow::requestUiRefresh() { pimpl_->requestUiRefresh(); } //throw CancelPressed diff --git a/FreeFileSync/Source/ui/small_dlgs.h b/FreeFileSync/Source/ui/small_dlgs.h index e6af0872..6d79b416 100755 --- a/FreeFileSync/Source/ui/small_dlgs.h +++ b/FreeFileSync/Source/ui/small_dlgs.h @@ -12,7 +12,7 @@ #include "../synchronization.h" -namespace zen +namespace fff { //parent window, optional: support correct dialog placement above parent on multiple monitor systems @@ -46,7 +46,7 @@ ReturnSmallDlg::ButtonPressed showSyncConfirmationDlg(wxWindow* parent, const SyncStatistics& statistics, bool& dontShowAgain); -ReturnSmallDlg::ButtonPressed showOptionsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings); +ReturnSmallDlg::ButtonPressed showOptionsDlg(wxWindow* parent, XmlGlobalSettings& globalCfg); ReturnSmallDlg::ButtonPressed showSelectTimespanDlg(wxWindow* parent, time_t& timeFrom, time_t& timeTo); diff --git a/FreeFileSync/Source/ui/sorting.h b/FreeFileSync/Source/ui/sorting.h index a2b598d9..8fdeca5b 100755 --- a/FreeFileSync/Source/ui/sorting.h +++ b/FreeFileSync/Source/ui/sorting.h @@ -11,7 +11,7 @@ #include "../file_hierarchy.h" -namespace zen +namespace fff { namespace { @@ -52,7 +52,7 @@ bool lessShortFileName(const FileSystemObject& a, const FileSystemObject& b) return true; //sort directories and files/symlinks by short name - return makeSortDirection(LessNaturalSort() /*even on Linux*/, Int2Type<ascending>())(a.getItemName<side>(), b.getItemName<side>()); + return makeSortDirection(LessNaturalSort() /*even on Linux*/, zen::Int2Type<ascending>())(a.getItemName<side>(), b.getItemName<side>()); } @@ -65,9 +65,9 @@ bool lessFullPath(const FileSystemObject& a, const FileSystemObject& b) else if (b.isEmpty<side>()) return true; - return makeSortDirection(LessNaturalSort() /*even on Linux*/, Int2Type<ascending>())( - utfTo<Zstring>(AFS::getDisplayPath(a.getAbstractPath<side>())), - utfTo<Zstring>(AFS::getDisplayPath(b.getAbstractPath<side>()))); + return makeSortDirection(LessNaturalSort() /*even on Linux*/, zen::Int2Type<ascending>())( + zen::utfTo<Zstring>(AFS::getDisplayPath(a.getAbstractPath<side>())), + zen::utfTo<Zstring>(AFS::getDisplayPath(b.getAbstractPath<side>()))); } @@ -88,7 +88,7 @@ bool lessRelativeFolder(const FileSystemObject& a, const FileSystemObject& b) const int rv = CmpNaturalSort()(relFolderA.c_str(), relFolderA.size(), relFolderB.c_str(), relFolderB.size()); if (rv != 0) - return makeSortDirection(std::less<int>(), Int2Type<ascending>())(rv, 0); + return makeSortDirection(std::less<int>(), zen::Int2Type<ascending>())(rv, 0); //make directories always appear before contained files if (isDirectoryB) @@ -96,7 +96,7 @@ bool lessRelativeFolder(const FileSystemObject& a, const FileSystemObject& b) else if (isDirectoryA) return true; - return makeSortDirection(LessNaturalSort(), Int2Type<ascending>())(a.getPairItemName(), b.getPairItemName()); + return makeSortDirection(LessNaturalSort(), zen::Int2Type<ascending>())(a.getPairItemName(), b.getPairItemName()); } @@ -125,7 +125,7 @@ bool lessFilesize(const FileSystemObject& a, const FileSystemObject& b) return true; //return list beginning with largest files first - return makeSortDirection(std::less<>(), Int2Type<ascending>())(fileA->getFileSize<side>(), fileB->getFileSize<side>()); + return makeSortDirection(std::less<>(), zen::Int2Type<ascending>())(fileA->getFileSize<side>(), fileB->getFileSize<side>()); } @@ -152,7 +152,7 @@ bool lessFiletime(const FileSystemObject& a, const FileSystemObject& b) const int64_t dateB = fileB ? fileB->getLastWriteTime<side>() : symlinkB->getLastWriteTime<side>(); //return list beginning with newest files first - return makeSortDirection(std::less<>(), Int2Type<ascending>())(dateA, dateB); + return makeSortDirection(std::less<>(), zen::Int2Type<ascending>())(dateA, dateB); } @@ -174,7 +174,7 @@ bool lessExtension(const FileSystemObject& a, const FileSystemObject& b) return afterLast(fsObj.getItemName<side>(), Zchar('.'), zen::IF_MISSING_RETURN_NONE); }; - return makeSortDirection(LessNaturalSort() /*even on Linux*/, Int2Type<ascending>())(getExtension(a), getExtension(b)); + return makeSortDirection(LessNaturalSort() /*even on Linux*/, zen::Int2Type<ascending>())(getExtension(a), getExtension(b)); } @@ -187,14 +187,14 @@ bool lessCmpResult(const FileSystemObject& a, const FileSystemObject& b) if (b.getCategory() == FILE_EQUAL) return true; - return makeSortDirection(std::less<CompareFilesResult>(), Int2Type<ascending>())(a.getCategory(), b.getCategory()); + return makeSortDirection(std::less<CompareFilesResult>(), zen::Int2Type<ascending>())(a.getCategory(), b.getCategory()); } template <bool ascending> inline bool lessSyncDirection(const FileSystemObject& a, const FileSystemObject& b) { - return makeSortDirection(std::less<>(), Int2Type<ascending>())(a.getSyncOperation(), b.getSyncOperation()); + return makeSortDirection(std::less<>(), zen::Int2Type<ascending>())(a.getSyncOperation(), b.getSyncOperation()); } } diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp index 887df0d6..3f73a263 100755 --- a/FreeFileSync/Source/ui/sync_cfg.cpp +++ b/FreeFileSync/Source/ui/sync_cfg.cpp @@ -25,7 +25,7 @@ using namespace zen; -using namespace xmlAccess; +using namespace fff; namespace @@ -1183,7 +1183,7 @@ void ConfigDialog::OnOkay(wxCommandEvent& event) //######################################################################################## -ReturnSyncConfig::ButtonPressed zen::showSyncConfigDlg(wxWindow* parent, +ReturnSyncConfig::ButtonPressed fff::showSyncConfigDlg(wxWindow* parent, SyncConfigPanel panelToShow, int localPairIndexToShow, diff --git a/FreeFileSync/Source/ui/sync_cfg.h b/FreeFileSync/Source/ui/sync_cfg.h index 087cbdd4..9d64ffcf 100755 --- a/FreeFileSync/Source/ui/sync_cfg.h +++ b/FreeFileSync/Source/ui/sync_cfg.h @@ -11,7 +11,7 @@ #include "../lib/process_xml.h" -namespace zen +namespace fff { struct ReturnSyncConfig { diff --git a/FreeFileSync/Source/ui/taskbar.cpp b/FreeFileSync/Source/ui/taskbar.cpp index d5ee9b16..66125d61 100755 --- a/FreeFileSync/Source/ui/taskbar.cpp +++ b/FreeFileSync/Source/ui/taskbar.cpp @@ -12,6 +12,7 @@ #endif using namespace zen; +using namespace fff; #if defined HAVE_UBUNTU_UNITY //Ubuntu unity diff --git a/FreeFileSync/Source/ui/taskbar.h b/FreeFileSync/Source/ui/taskbar.h index 81d1483b..667d8afd 100755 --- a/FreeFileSync/Source/ui/taskbar.h +++ b/FreeFileSync/Source/ui/taskbar.h @@ -20,7 +20,7 @@ Define HAVE_UBUNTU_UNITY and set: Linker flag: `pkg-config --libs unity` */ -namespace zen +namespace fff { class TaskbarNotAvailable {}; diff --git a/FreeFileSync/Source/ui/tray_icon.cpp b/FreeFileSync/Source/ui/tray_icon.cpp index b86c7af5..98412723 100755 --- a/FreeFileSync/Source/ui/tray_icon.cpp +++ b/FreeFileSync/Source/ui/tray_icon.cpp @@ -14,6 +14,7 @@ #include <wx+/image_resources.h> using namespace zen; +using namespace fff; namespace @@ -79,7 +80,7 @@ wxIcon FfsTrayIcon::ProgressIconGenerator::get(double fraction) return wxIcon(); const int pixelCount = logo_.GetWidth() * logo_.GetHeight(); - const int startFillPixel = numeric::clampCpy(numeric::round(fraction * pixelCount), 0, pixelCount); + const int startFillPixel = numeric::clampCpy<int>(numeric::round(fraction * pixelCount), 0, pixelCount); if (startPixBuf_ != startFillPixel) { @@ -196,16 +197,16 @@ private: FfsTrayIcon::FfsTrayIcon(const std::function<void()>& onRequestResume) : - trayIcon(new TaskBarImpl(onRequestResume)), - iconGenerator(std::make_unique<ProgressIconGenerator>(getResourceImage(L"FFS_tray_24x24").ConvertToImage())) + trayIcon_(new TaskBarImpl(onRequestResume)), + iconGenerator_(std::make_unique<ProgressIconGenerator>(getResourceImage(L"FFS_tray_24x24").ConvertToImage())) { - trayIcon->SetIcon(iconGenerator->get(activeFraction), activeToolTip); + trayIcon_->SetIcon(iconGenerator_->get(activeFraction_), activeToolTip_); } FfsTrayIcon::~FfsTrayIcon() { - trayIcon->dontCallbackAnymore(); //TaskBarImpl has longer lifetime than FfsTrayIcon: avoid callback! + trayIcon_->dontCallbackAnymore(); //TaskBarImpl has longer lifetime than FfsTrayIcon: avoid callback! /* This is not working correctly on OS X! It seems both wxTaskBarIcon::RemoveIcon() and ~wxTaskBarIcon() are broken and do NOT immediately @@ -219,21 +220,21 @@ FfsTrayIcon::~FfsTrayIcon() - if ~wxTaskBarIcon() ran from SyncProgressDialog::closeDirectly() => leaves the icon dangling until user closes this dialog and outter event loop runs! */ - trayIcon->RemoveIcon(); //required on Windows: unlike on OS X, wxPendingDelete does not kick in before main event loop! + trayIcon_->RemoveIcon(); //required on Windows: unlike on OS X, wxPendingDelete does not kick in before main event loop! //use wxWidgets delayed destruction: delete during next idle loop iteration (handle late window messages, e.g. when double-clicking) - wxPendingDelete.Append(trayIcon); //identical to wxTaskBarIconBase::Destroy() in wxWidgets 2.9.5 + wxPendingDelete.Append(trayIcon_); //identical to wxTaskBarIconBase::Destroy() in wxWidgets 2.9.5 } void FfsTrayIcon::setToolTip(const wxString& toolTip) { - activeToolTip = toolTip; - trayIcon->SetIcon(iconGenerator->get(activeFraction), activeToolTip); //another wxWidgets design bug: non-orthogonal method! + activeToolTip_ = toolTip; + trayIcon_->SetIcon(iconGenerator_->get(activeFraction_), activeToolTip_); //another wxWidgets design bug: non-orthogonal method! } void FfsTrayIcon::setProgress(double fraction) { - activeFraction = fraction; - trayIcon->SetIcon(iconGenerator->get(activeFraction), activeToolTip); + activeFraction_ = fraction; + trayIcon_->SetIcon(iconGenerator_->get(activeFraction_), activeToolTip_); } diff --git a/FreeFileSync/Source/ui/tray_icon.h b/FreeFileSync/Source/ui/tray_icon.h index bb1d0175..e99d5127 100755 --- a/FreeFileSync/Source/ui/tray_icon.h +++ b/FreeFileSync/Source/ui/tray_icon.h @@ -11,6 +11,7 @@ #include <memory> #include <wx/image.h> + /* show tray icon with progress during lifetime of this instance @@ -21,7 +22,8 @@ ATTENTION: wxWidgets never assumes that an object indirectly destroys itself whi => don't derive from wxEvtHandler or any other wxWidgets object here!!!!!! => use simple std::function as callback instead => instance may now be safely deleted in callback! */ - +namespace fff +{ class FfsTrayIcon { public: @@ -36,13 +38,14 @@ private: FfsTrayIcon& operator=(const FfsTrayIcon&) = delete; class TaskBarImpl; - TaskBarImpl* trayIcon; + TaskBarImpl* trayIcon_; class ProgressIconGenerator; - std::unique_ptr<ProgressIconGenerator> iconGenerator; + std::unique_ptr<ProgressIconGenerator> iconGenerator_; - wxString activeToolTip = L"FreeFileSync"; - double activeFraction = 1; + wxString activeToolTip_ = L"FreeFileSync"; + double activeFraction_ = 1; }; +} #endif //TRAY_ICON_H_84217830427534285 diff --git a/FreeFileSync/Source/ui/tree_grid.cpp b/FreeFileSync/Source/ui/tree_grid.cpp index c64a52cf..b802f294 100755 --- a/FreeFileSync/Source/ui/tree_grid.cpp +++ b/FreeFileSync/Source/ui/tree_grid.cpp @@ -19,6 +19,7 @@ #include "../lib/icon_buffer.h" using namespace zen; +using namespace fff; namespace @@ -156,7 +157,7 @@ void calcPercentage(std::vector<std::pair<uint64_t, int*>>& workList) } -Zstring zen::getShortDisplayNameForFolderPair(const AbstractPath& itemPathL, const AbstractPath& itemPathR) +Zstring fff::getShortDisplayNameForFolderPair(const AbstractPath& itemPathL, const AbstractPath& itemPathR) { Zstring commonTrail; AbstractPath tmpPathL = itemPathL; @@ -1244,7 +1245,7 @@ private: } -void zen::treegrid::init(Grid& grid) +void treegrid::init(Grid& grid) { grid.setDataProvider(std::make_shared<GridDataTree>(grid)); grid.showRowLabel(false); @@ -1254,7 +1255,7 @@ void zen::treegrid::init(Grid& grid) } -TreeView& zen::treegrid::getDataView(Grid& grid) +TreeView& treegrid::getDataView(Grid& grid) { if (auto* prov = dynamic_cast<GridDataTree*>(grid.getDataProvider())) return prov->getDataView(); @@ -1263,7 +1264,7 @@ TreeView& zen::treegrid::getDataView(Grid& grid) } -void zen::treegrid::setShowPercentage(Grid& grid, bool value) +void treegrid::setShowPercentage(Grid& grid, bool value) { if (auto* prov = dynamic_cast<GridDataTree*>(grid.getDataProvider())) prov->setShowPercentage(value); @@ -1272,7 +1273,7 @@ void zen::treegrid::setShowPercentage(Grid& grid, bool value) } -bool zen::treegrid::getShowPercentage(const Grid& grid) +bool treegrid::getShowPercentage(const Grid& grid) { if (auto* prov = dynamic_cast<const GridDataTree*>(grid.getDataProvider())) return prov->getShowPercentage(); diff --git a/FreeFileSync/Source/ui/tree_grid.h b/FreeFileSync/Source/ui/tree_grid.h index 0d69e820..af57070d 100755 --- a/FreeFileSync/Source/ui/tree_grid.h +++ b/FreeFileSync/Source/ui/tree_grid.h @@ -14,7 +14,7 @@ #include "../file_hierarchy.h" -namespace zen +namespace fff { //tree view of FolderComparison class TreeView @@ -99,7 +99,7 @@ public: ptrdiff_t getParent(size_t row) const; //return < 0 if none void setSortDirection(ColumnTypeTree colType, bool ascending); //apply permanently! - std::pair<ColumnTypeTree, bool> getSortDirection() { return std::make_pair(sortColumn_, sortAscending_); } + std::pair<ColumnTypeTree, bool> getSortDirection() { return { sortColumn_, sortAscending_ }; } private: struct DirNodeImpl; @@ -172,13 +172,14 @@ private: Zstring getShortDisplayNameForFolderPair(const AbstractPath& itemPathL, const AbstractPath& itemPathR); + namespace treegrid { -void init(Grid& grid); -TreeView& getDataView(Grid& grid); +void init(zen::Grid& grid); +TreeView& getDataView(zen::Grid& grid); -void setShowPercentage(Grid& grid, bool value); -bool getShowPercentage(const Grid& grid); +void setShowPercentage(zen::Grid& grid, bool value); +bool getShowPercentage(const zen::Grid& grid); } } diff --git a/FreeFileSync/Source/ui/tree_grid_attr.h b/FreeFileSync/Source/ui/tree_grid_attr.h index 0c5da30e..ce37e468 100755 --- a/FreeFileSync/Source/ui/tree_grid_attr.h +++ b/FreeFileSync/Source/ui/tree_grid_attr.h @@ -11,7 +11,7 @@ #include <cassert> -namespace zen +namespace fff { enum class ColumnTypeTree { diff --git a/FreeFileSync/Source/ui/triple_splitter.cpp b/FreeFileSync/Source/ui/triple_splitter.cpp index 6a288ca3..0bc43101 100755 --- a/FreeFileSync/Source/ui/triple_splitter.cpp +++ b/FreeFileSync/Source/ui/triple_splitter.cpp @@ -9,6 +9,7 @@ #include <zen/stl_tools.h> using namespace zen; +using namespace fff; namespace @@ -52,7 +53,7 @@ TripleSplitter::~TripleSplitter() {} //make sure correct destructor gets created void TripleSplitter::updateWindowSizes() { - if (windowL && windowC && windowR) + if (windowL_ && windowC_ && windowR_) { const int centerPosX = getCenterPosX(); const int centerWidth = getCenterWidth(); @@ -63,9 +64,9 @@ void TripleSplitter::updateWindowSizes() const int windowRposX = widthL + centerWidth; const int widthR = clientRect.width - windowRposX; - windowL->SetSize(0, 0, widthL, clientRect.height); - windowC->SetSize(widthL + SASH_SIZE, 0, windowC->GetSize().GetWidth(), clientRect.height); - windowR->SetSize(windowRposX, 0, widthR, clientRect.height); + windowL_->SetSize(0, 0, widthL, clientRect.height); + windowC_->SetSize(widthL + SASH_SIZE, 0, windowC_->GetSize().GetWidth(), clientRect.height); + windowR_->SetSize(windowRposX, 0, widthR, clientRect.height); wxClientDC dc(this); drawSash(dc); @@ -100,7 +101,7 @@ private: inline int TripleSplitter::getCenterWidth() const { - return 2 * SASH_SIZE + (windowC ? windowC->GetSize().GetWidth() : 0); + return 2 * SASH_SIZE + (windowC_ ? windowC_->GetSize().GetWidth() : 0); } @@ -124,7 +125,7 @@ int TripleSplitter::getCenterPosX() const return centerPosXOptimal + CHILD_WINDOW_MIN_SIZE - static_cast<int>(2 * CHILD_WINDOW_MIN_SIZE * SASH_GRAVITY); //avoid rounding error //make sure transition between conditional branches is continuous! return std::max(CHILD_WINDOW_MIN_SIZE, //make sure centerPosXOptimal + offset is within bounds - std::min(centerPosXOptimal + centerOffset, clientRect.width - CHILD_WINDOW_MIN_SIZE - centerWidth)); + std::min(centerPosXOptimal + centerOffset_, clientRect.width - CHILD_WINDOW_MIN_SIZE - centerWidth)); } @@ -168,32 +169,32 @@ bool TripleSplitter::hitOnSashLine(int posX) const void TripleSplitter::onMouseLeftDown(wxMouseEvent& event) { - activeMove.reset(); + activeMove_.reset(); const int posX = event.GetPosition().x; if (hitOnSashLine(posX)) - activeMove = std::make_unique<SashMove>(*this, posX, centerOffset); + activeMove_ = std::make_unique<SashMove>(*this, posX, centerOffset_); event.Skip(); } void TripleSplitter::onMouseLeftUp(wxMouseEvent& event) { - activeMove.reset(); //nothing else to do, actual work done by onMouseMovement() + activeMove_.reset(); //nothing else to do, actual work done by onMouseMovement() event.Skip(); } void TripleSplitter::onMouseMovement(wxMouseEvent& event) { - if (activeMove) + if (activeMove_) { - centerOffset = activeMove->getCenterOffsetStart() + event.GetPosition().x - activeMove->getMousePosXStart(); + centerOffset_ = activeMove_->getCenterOffsetStart() + event.GetPosition().x - activeMove_->getMousePosXStart(); //CAVEAT: function getCenterPosX() normalizes centerPosX *not* centerOffset! //This can lead to the strange effect of window not immediately resizing when centerOffset is extremely off limits //=> normalize centerOffset right here - centerOffset = getCenterPosX() - getCenterPosXOptimal(); + centerOffset_ = getCenterPosX() - getCenterPosXOptimal(); updateWindowSizes(); Update(); //no time to wait until idle event! @@ -214,7 +215,7 @@ void TripleSplitter::onMouseMovement(wxMouseEvent& event) void TripleSplitter::onLeaveWindow(wxMouseEvent& event) { //even called when moving from sash over to managed windows! - if (!activeMove) + if (!activeMove_) SetCursor(*wxSTANDARD_CURSOR); event.Skip(); } @@ -222,7 +223,7 @@ void TripleSplitter::onLeaveWindow(wxMouseEvent& event) void TripleSplitter::onMouseCaptureLost(wxMouseCaptureLostEvent& event) { - activeMove.reset(); + activeMove_.reset(); updateWindowSizes(); //event.Skip(); -> we DID handle it! } @@ -233,7 +234,7 @@ void TripleSplitter::onMouseLeftDouble(wxMouseEvent& event) const int posX = event.GetPosition().x; if (hitOnSashLine(posX)) { - centerOffset = 0; //reset sash according to gravity + centerOffset_ = 0; //reset sash according to gravity updateWindowSizes(); } event.Skip(); diff --git a/FreeFileSync/Source/ui/triple_splitter.h b/FreeFileSync/Source/ui/triple_splitter.h index c93e478e..f14e8039 100755 --- a/FreeFileSync/Source/ui/triple_splitter.h +++ b/FreeFileSync/Source/ui/triple_splitter.h @@ -12,7 +12,6 @@ #include <wx/window.h> #include <wx/dcclient.h> -//a not-so-crappy splitter window /* manage three contained windows: 1. left and right window are stretched @@ -24,8 +23,7 @@ | | | | ----------------- */ - -namespace zen +namespace fff { class TripleSplitter : public wxWindow { @@ -41,14 +39,14 @@ public: void setupWindows(wxWindow* winL, wxWindow* winC, wxWindow* winR) { assert(winL->GetParent() == this && winC->GetParent() == this && winR->GetParent() == this && !GetSizer()); - windowL = winL; - windowC = winC; - windowR = winR; + windowL_ = winL; + windowC_ = winC; + windowR_ = winR; updateWindowSizes(); } - int getSashOffset() const { return centerOffset; } - void setSashOffset(int off) { centerOffset = off; updateWindowSizes(); } + int getSashOffset() const { return centerOffset_; } + void setSashOffset(int off) { centerOffset_ = off; updateWindowSizes(); } private: void onEraseBackGround(wxEraseEvent& event) {} @@ -76,13 +74,13 @@ private: void onMouseLeftDouble(wxMouseEvent& event); class SashMove; - std::unique_ptr<SashMove> activeMove; + std::unique_ptr<SashMove> activeMove_; - int centerOffset = 0; //offset to add after "gravity" stretching + int centerOffset_ = 0; //offset to add after "gravity" stretching - wxWindow* windowL = nullptr; - wxWindow* windowC = nullptr; - wxWindow* windowR = nullptr; + wxWindow* windowL_ = nullptr; + wxWindow* windowC_ = nullptr; + wxWindow* windowR_ = nullptr; }; } diff --git a/FreeFileSync/Source/ui/version_check.cpp b/FreeFileSync/Source/ui/version_check.cpp index 265b6e38..9b241c7d 100755 --- a/FreeFileSync/Source/ui/version_check.cpp +++ b/FreeFileSync/Source/ui/version_check.cpp @@ -22,6 +22,7 @@ using namespace zen; +using namespace fff; namespace @@ -65,7 +66,7 @@ std::vector<std::pair<std::string, std::string>> geHttpPostParameters() assert(std::this_thread::get_id() == mainThreadId); //this function is not thread-safe, e.g. consider wxWidgets usage in isPortableVersion() std::vector<std::pair<std::string, std::string>> params; - params.emplace_back("ffs_version", zen::ffsVersion); + params.emplace_back("ffs_version", ffsVersion); params.emplace_back("installation_type", isPortableVersion() ? "Portable" : "Local"); @@ -102,10 +103,11 @@ void showUpdateAvailableDialog(wxWindow* parent, const std::string& onlineVersio std::wstring updateDetailsMsg; try { - try //harmonize with wxHTTP: get_latest_changes.php must be accessible without https!!! + try { + //consider wxHTTP limitation: URL must be accessible without https!!! const std::string buf = sendHttpPost(L"http://www.freefilesync.org/get_latest_changes.php", ffsUpdateCheckUserAgent, - nullptr /*notifyUnbufferedIO*/, { { "since", zen::ffsVersion } }).readAll(); //throw SysError + nullptr /*notifyUnbufferedIO*/, { { "since", ffsVersion } }).readAll(); //throw SysError updateDetailsMsg = utfTo<std::wstring>(buf); } catch (const zen::SysError& e) { throw FileError(_("Failed to retrieve update information."), e.toString()); } @@ -135,7 +137,7 @@ void showUpdateAvailableDialog(wxWindow* parent, const std::string& onlineVersio //access is thread-safe on Windows (WinInet), but not on Linux/OS X (wxWidgets) std::string getOnlineVersion(const std::vector<std::pair<std::string, std::string>>& postParams) //throw SysError { - //harmonize with wxHTTP: get_latest_version_number.php must be accessible without https!!! + //consider wxHTTP limitation: URL must be accessible without https!!! const std::string buffer = sendHttpPost(L"http://www.freefilesync.org/get_latest_version_number.php", ffsUpdateCheckUserAgent, nullptr /*notifyUnbufferedIO*/, postParams).readAll(); //throw SysError return trimCpy(buffer); @@ -152,9 +154,9 @@ std::vector<size_t> parseVersion(const std::string& version) } -bool zen::haveNewerVersionOnline(const std::string& onlineVersion) +bool fff::haveNewerVersionOnline(const std::string& onlineVersion) { - const std::vector<size_t> current = parseVersion(zen::ffsVersion); + const std::vector<size_t> current = parseVersion(ffsVersion); const std::vector<size_t> online = parseVersion(onlineVersion); if (online.empty() || online[0] == 0) //online version string may be "This website has been moved..." In this case better check for an update @@ -165,19 +167,19 @@ bool zen::haveNewerVersionOnline(const std::string& onlineVersion) } -bool zen::updateCheckActive(time_t lastUpdateCheck) +bool fff::updateCheckActive(time_t lastUpdateCheck) { return lastUpdateCheck != getVersionCheckInactiveId(); } -void zen::disableUpdateCheck(time_t& lastUpdateCheck) +void fff::disableUpdateCheck(time_t& lastUpdateCheck) { lastUpdateCheck = getVersionCheckInactiveId(); } -void zen::checkForUpdateNow(wxWindow* parent, std::string& lastOnlineVersion) +void fff::checkForUpdateNow(wxWindow* parent, std::string& lastOnlineVersion) { try { @@ -229,19 +231,19 @@ void zen::checkForUpdateNow(wxWindow* parent, std::string& lastOnlineVersion) } -struct zen::UpdateCheckResultPrep +struct fff::UpdateCheckResultPrep { const std::vector<std::pair<std::string, std::string>> postParameters { geHttpPostParameters() }; }; //run on main thread: -std::shared_ptr<UpdateCheckResultPrep> zen::automaticUpdateCheckPrepare() +std::shared_ptr<UpdateCheckResultPrep> fff::automaticUpdateCheckPrepare() { return nullptr; } -struct zen::UpdateCheckResult +struct fff::UpdateCheckResult { UpdateCheckResult() {} UpdateCheckResult(const std::string& ver, const Opt<zen::SysError>& err, bool alive) : onlineVersion(ver), error(err), internetIsAlive(alive) {} @@ -252,14 +254,14 @@ struct zen::UpdateCheckResult }; //run on worker thread: -std::shared_ptr<UpdateCheckResult> zen::automaticUpdateCheckRunAsync(const UpdateCheckResultPrep* resultPrep) +std::shared_ptr<UpdateCheckResult> fff::automaticUpdateCheckRunAsync(const UpdateCheckResultPrep* resultPrep) { return nullptr; } //run on main thread: -void zen::automaticUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, std::string& lastOnlineVersion, const UpdateCheckResult* resultAsync) +void fff::automaticUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, std::string& lastOnlineVersion, const UpdateCheckResult* resultAsync) { UpdateCheckResult result; try @@ -267,7 +269,7 @@ void zen::automaticUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, st result.onlineVersion = getOnlineVersion(geHttpPostParameters()); //throw SysError result.internetIsAlive = true; } - catch (const SysError& e) + catch (const zen::SysError& e) { result.error = e; result.internetIsAlive = internetIsAlive(); diff --git a/FreeFileSync/Source/ui/version_check.h b/FreeFileSync/Source/ui/version_check.h index 2e306747..75cef84e 100755 --- a/FreeFileSync/Source/ui/version_check.h +++ b/FreeFileSync/Source/ui/version_check.h @@ -11,7 +11,8 @@ #include <memory> #include <wx/window.h> -namespace zen + +namespace fff { bool updateCheckActive (time_t lastUpdateCheck); void disableUpdateCheck(time_t& lastUpdateCheck); diff --git a/FreeFileSync/Source/ui/version_check_impl.h b/FreeFileSync/Source/ui/version_check_impl.h index 5ced0190..b33bb55b 100755..100644 --- a/FreeFileSync/Source/ui/version_check_impl.h +++ b/FreeFileSync/Source/ui/version_check_impl.h @@ -12,7 +12,7 @@ #include "../version/version.h" -namespace zen +namespace fff { inline time_t getVersionCheckInactiveId() @@ -20,7 +20,7 @@ time_t getVersionCheckInactiveId() //use current version to calculate a changing number for the inactive state near UTC begin, in order to always check for updates after installing a new version //=> convert version into 11-based *unique* number (this breaks lexicographical version ordering, but that's irrelevant!) int id = 0; - const char* first = zen::ffsVersion; + const char* first = ffsVersion; const char* last = first + zen::strLength(ffsVersion); std::for_each(first, last, [&](char c) { diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h index 5fc0f704..44e25841 100755 --- a/FreeFileSync/Source/version/version.h +++ b/FreeFileSync/Source/version/version.h @@ -1,9 +1,9 @@ #ifndef VERSION_HEADER_434343489702544325 #define VERSION_HEADER_434343489702544325 -namespace zen +namespace fff { -const char ffsVersion[] = "9.7"; //internal linkage! +const char ffsVersion[] = "9.8"; //internal linkage! const char FFS_VERSION_SEPARATOR = '.'; } diff --git a/wx+/app_main.h b/wx+/app_main.h index d9559fea..8d2a6eeb 100755 --- a/wx+/app_main.h +++ b/wx+/app_main.h @@ -10,6 +10,7 @@ #include <wx/window.h> #include <wx/app.h> + namespace zen { //just some wrapper around a global variable representing the (logical) main application window @@ -22,12 +23,16 @@ bool mainWindowWasSet(); //######################## implementation ######################## +namespace impl +{ inline bool& refMainWndStatus() { static bool status = false; //external linkage! return status; } +} + inline void setMainWindow(wxWindow* window) @@ -35,10 +40,10 @@ void setMainWindow(wxWindow* window) wxTheApp->SetTopWindow(window); wxTheApp->SetExitOnFrameDelete(true); - refMainWndStatus() = true; + impl::refMainWndStatus() = true; } -inline bool mainWindowWasSet() { return refMainWndStatus(); } +inline bool mainWindowWasSet() { return impl::refMainWndStatus(); } } #endif //APP_MAIN_H_08215601837818347575856 diff --git a/wx+/choice_enum.h b/wx+/choice_enum.h index a19e1457..6b2edd01 100755 --- a/wx+/choice_enum.h +++ b/wx+/choice_enum.h @@ -33,7 +33,6 @@ Update enum tooltips (after user changed selection): updateTooltipEnumVal(enumDescrMap, *m_choiceHandleError); */ - namespace zen { template <class Enum> @@ -41,7 +40,7 @@ struct EnumDescrList { EnumDescrList& add(Enum value, const wxString& text, const wxString& tooltip = {}) { - descrList.emplace_back(value, std::make_pair(text, tooltip)); + descrList.push_back({ value, { text, tooltip } }); return *this; } using DescrList = std::vector<std::pair<Enum, std::pair<wxString, wxString>>>; diff --git a/wx+/context_menu.h b/wx+/context_menu.h index de351df4..7f459aca 100755 --- a/wx+/context_menu.h +++ b/wx+/context_menu.h @@ -22,7 +22,6 @@ Usage: ... menu.popup(wnd); */ - namespace zen { class ContextMenu : private wxEvtHandler @@ -11,6 +11,7 @@ #include <zen/optional.h> #include <wx/dcbuffer.h> //for macro: wxALWAYS_NATIVE_DOUBLE_BUFFER + namespace zen { /* diff --git a/wx+/graph.cpp b/wx+/graph.cpp index b81896b4..dbce3769 100755 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -539,8 +539,6 @@ void Graph2D::addCurve(const std::shared_ptr<CurveData>& data, const CurveAttrib void Graph2D::render(wxDC& dc) const { - using namespace numeric; - //set label font right at the start so that it is considered by wxDC::GetTextExtent() below! dc.SetFont(labelFont_); @@ -722,10 +720,10 @@ void Graph2D::render(wxDC& dc) const const wxPoint screenCurrent = activeSel_->refCurrentPos() - graphAreaOrigin; //normalize positions: a mouse selection is symmetric and *not* an half-open range! - double screenFromX = clampCpy(screenStart .x, 0, graphArea.width - 1); - double screenFromY = clampCpy(screenStart .y, 0, graphArea.height - 1); - double screenToX = clampCpy(screenCurrent.x, 0, graphArea.width - 1); - double screenToY = clampCpy(screenCurrent.y, 0, graphArea.height - 1); + double screenFromX = numeric::clampCpy(screenStart .x, 0, graphArea.width - 1); + double screenFromY = numeric::clampCpy(screenStart .y, 0, graphArea.height - 1); + double screenToX = numeric::clampCpy(screenCurrent.x, 0, graphArea.width - 1); + double screenToY = numeric::clampCpy(screenCurrent.y, 0, graphArea.height - 1); widen(&screenFromX, &screenToX); //use full pixel range for selection! widen(&screenFromY, &screenToY); @@ -780,10 +778,10 @@ void Graph2D::render(wxDC& dc) const shrink(&screenFromX, &screenToX); shrink(&screenFromY, &screenToY); - clamp(screenFromX, 0.0, graphArea.width - 1.0); - clamp(screenFromY, 0.0, graphArea.height - 1.0); - clamp(screenToX, 0.0, graphArea.width - 1.0); - clamp(screenToY, 0.0, graphArea.height - 1.0); + numeric::clamp(screenFromX, 0.0, graphArea.width - 1.0); + numeric::clamp(screenFromY, 0.0, graphArea.height - 1.0); + numeric::clamp(screenToX, 0.0, graphArea.width - 1.0); + numeric::clamp(screenToY, 0.0, graphArea.height - 1.0); const wxPoint pixelFrom = wxPoint(numeric::round(screenFromX), numeric::round(screenFromY)) + graphAreaOrigin; diff --git a/wx+/graph.h b/wx+/graph.h index 5c71ed52..45129c4b 100755 --- a/wx+/graph.h +++ b/wx+/graph.h @@ -16,8 +16,8 @@ #include <zen/string_tools.h> #include <zen/optional.h> -//elegant 2D graph as wxPanel specialization +//elegant 2D graph as wxPanel specialization namespace zen { /* diff --git a/wx+/grid.cpp b/wx+/grid.cpp index b301bf6b..64f7f4a6 100755 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -462,8 +462,8 @@ public: const int yFrom = refParent().CalcUnscrolledPosition(clientRect.GetTopLeft ()).y; const int yTo = refParent().CalcUnscrolledPosition(clientRect.GetBottomRight()).y; - return std::make_pair(std::max(yFrom / rowHeight_, 0), - std::min<ptrdiff_t>((yTo / rowHeight_) + 1, refParent().getRowCount())); + return { std::max(yFrom / rowHeight_, 0), + std::min<ptrdiff_t>((yTo / rowHeight_) + 1, refParent().getRowCount()) }; } private: @@ -1269,7 +1269,7 @@ private: ColLabelWin& colLabelWin_; std::unique_ptr<MouseSelection> activeSelection_; //bound while user is selecting with mouse - MouseHighlight highlight_; //current mouse highlight_ (superseeded by activeSelection_ if available) + MouseHighlight highlight_; //current mouse highlight_ (superseded by activeSelection_ if available) ptrdiff_t cursorRow_ = 0; size_t selectionAnchor_ = 0; @@ -14,8 +14,8 @@ #include <zen/basic_math.h> #include <zen/optional.h> -//a user-friendly, extensible and high-performance grid control +//a user-friendly, extensible and high-performance grid control namespace zen { enum class ColumnType { NONE = -1 }; //user-defiend column type diff --git a/wx+/http.cpp b/wx+/http.cpp index fa88bb1d..1526d30f 100755 --- a/wx+/http.cpp +++ b/wx+/http.cpp @@ -33,9 +33,9 @@ public: { ZEN_ON_SCOPE_FAIL( cleanup(); /*destructor call would lead to member double clean-up!!!*/ ); - assert(!startsWith(url, L"https:", CmpAsciiNoCase())); //not supported by wxHTTP! - const std::wstring urlFmt = startsWith(url, L"http://", CmpAsciiNoCase()) || - startsWith(url, L"https://", CmpAsciiNoCase()) ? afterFirst(url, L"://", IF_MISSING_RETURN_NONE) : url; + //assert(!startsWith(url, L"https:", CmpAsciiNoCase())); //not supported by wxHTTP! + + const std::wstring urlFmt = afterFirst(url, L"://", IF_MISSING_RETURN_NONE); const std::wstring server = beforeFirst(urlFmt, L'/', IF_MISSING_RETURN_ALL); const std::wstring page = L'/' + afterFirst(urlFmt, L'/', IF_MISSING_RETURN_NONE); @@ -15,7 +15,7 @@ namespace zen /* TREAD-SAFETY ------------ - Windows: WinInet-based => may be called from worker thread + Windows: WinInet-based => may be called from worker thread, supports HTTPS Linux: wxWidgets-based => don't call from worker thread */ class HttpInputStream diff --git a/wx+/image_resources.h b/wx+/image_resources.h index 3c3bf39c..cc4ab2cc 100755 --- a/wx+/image_resources.h +++ b/wx+/image_resources.h @@ -11,6 +11,7 @@ #include <wx/animate.h> #include <zen/zstring.h> + namespace zen { void initResourceImages(const Zstring& filepath); //pass resources .zip file at application startup diff --git a/wx+/no_flicker.h b/wx+/no_flicker.h index f9bf862b..03969c00 100755 --- a/wx+/no_flicker.h +++ b/wx+/no_flicker.h @@ -10,6 +10,7 @@ #include <wx/textctrl.h> #include <wx/stattext.h> + namespace zen { inline @@ -12,6 +12,7 @@ #include <wx/image.h> #include <wx/app.h> + namespace zen { //functions supporting right-to-left GUI layout diff --git a/wx+/toggle_button.h b/wx+/toggle_button.h index 0172881e..f61c3857 100755 --- a/wx+/toggle_button.h +++ b/wx+/toggle_button.h @@ -10,6 +10,9 @@ #include <wx/bmpbuttn.h> #include <wx+/bitmap_button.h> + +namespace zen +{ class ToggleButton : public wxBitmapButton { public: @@ -29,12 +32,11 @@ public: const wxBitmap& inactiveBmp); void setActive(bool value); - bool isActive() const { return active; } - void toggle() { setActive(!active); } + bool isActive() const { return active_; } + void toggle() { setActive(!active_); } private: - bool active = false; - + bool active_ = false; wxBitmap activeBmp_; wxBitmap inactiveBmp_; }; @@ -53,15 +55,16 @@ void ToggleButton::init(const wxBitmap& activeBmp, activeBmp_ = activeBmp; inactiveBmp_ = inactiveBmp; - setActive(active); + setActive(active_); } inline void ToggleButton::setActive(bool value) { - active = value; - zen::setImage(*this, active ? activeBmp_ : inactiveBmp_); + active_ = value; + setImage(*this, active_ ? activeBmp_ : inactiveBmp_); +} } #endif //TOGGLE_BUTTON_H_8173024810574556 diff --git a/zen/basic_math.h b/zen/basic_math.h index 1b6b7e97..16f69bde 100755 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -31,7 +31,7 @@ template <class T> T clampCpy(T val, T minVal, T maxVal); template <class T, class InputIterator> //precondition: range must be sorted! auto nearMatch(const T& val, InputIterator first, InputIterator last); -int round(double d); //"little rounding function" +int64_t round(double d); //"little rounding function" template <class N, class D> auto integerDivideRoundUp(N numerator, D denominator); @@ -182,7 +182,7 @@ std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, Input } } } - return std::make_pair(lowest, largest); + return { lowest, largest }; } @@ -220,20 +220,20 @@ bool isNull(T value) inline -int round(double d) +int64_t round(double d) { - assert(d - 0.5 >= std::numeric_limits<int>::min() && //if double is larger than what int can represent: - d + 0.5 <= std::numeric_limits<int>::max()); //=> undefined behavior! - return static_cast<int>(d < 0 ? d - 0.5 : d + 0.5); + assert(d - 0.5 >= std::numeric_limits<int64_t>::min() && //if double is larger than what int can represent: + d + 0.5 <= std::numeric_limits<int64_t>::max()); //=> undefined behavior! + return static_cast<int64_t>(d < 0 ? d - 0.5 : d + 0.5); } template <class N, class D> inline auto integerDivideRoundUp(N numerator, D denominator) { - static_assert(std::is_integral<N>::value && std::is_unsigned<N>::value, ""); - static_assert(std::is_integral<D>::value && std::is_unsigned<D>::value, ""); - assert(denominator > 0); + static_assert(std::is_integral<N>::value, ""); + static_assert(std::is_integral<D>::value, ""); + assert(numerator > 0 && denominator > 0); return (numerator + denominator - 1) / denominator; } diff --git a/zen/build_info.h b/zen/build_info.h index 7b0aa9cf..9b8b7fc0 100755 --- a/zen/build_info.h +++ b/zen/build_info.h @@ -7,8 +7,6 @@ #ifndef BUILD_INFO_H_5928539285603428657 #define BUILD_INFO_H_5928539285603428657 -namespace zen -{ //determine build info: defines ZEN_BUILD_32BIT or ZEN_BUILD_64BIT #ifdef __LP64__ @@ -24,6 +22,5 @@ namespace zen #ifdef ZEN_BUILD_64BIT static_assert(sizeof(void*) == 8, ""); #endif -} #endif //BUILD_INFO_H_5928539285603428657 diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 17f2244d..0cbde150 100755 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -30,11 +30,11 @@ struct DirWatcher::Impl DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError - baseDirPath(dirPath), + baseDirPath_(dirPath), pimpl_(std::make_unique<Impl>()) { //get all subdirectories - std::vector<Zstring> fullFolderList { baseDirPath }; + std::vector<Zstring> fullFolderList { baseDirPath_ }; { std::function<void (const Zstring& path)> traverse; @@ -46,13 +46,13 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError [&](const std::wstring& errorMsg) { throw FileError(errorMsg); }); }; - traverse(baseDirPath); + traverse(baseDirPath_); } //init pimpl_->notifDescr = ::inotify_init(); if (pimpl_->notifDescr == -1) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"inotify_init"); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath_)), L"inotify_init"); ZEN_ON_SCOPE_FAIL( ::close(pimpl_->notifDescr); ); @@ -64,7 +64,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError initSuccess = ::fcntl(pimpl_->notifDescr, F_SETFL, flags | O_NONBLOCK) != -1; } if (!initSuccess) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"fcntl"); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath_)), L"fcntl"); //add watches for (const Zstring& subDirPath : fullFolderList) @@ -101,7 +101,7 @@ DirWatcher::~DirWatcher() } -std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()>&) //throw FileError +std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()>& requestUiRefresh, std::chrono::milliseconds cbInterval) //throw FileError { std::vector<char> buffer(512 * (sizeof(struct ::inotify_event) + NAME_MAX + 1)); @@ -118,7 +118,7 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void() if (errno == EAGAIN) //this error is ignored in all inotify wrappers I found return std::vector<Entry>(); - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"read"); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath_)), L"read"); } std::vector<Entry> output; @@ -135,19 +135,19 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void() { //Note: evt.len is NOT the size of the evt.name c-string, but the array size including all padding 0 characters! //It may be even 0 in which case evt.name must not be used! - const Zstring fullname = it->second + evt.name; + const Zstring itemPath = it->second + evt.name; if ((evt.mask & IN_CREATE) || (evt.mask & IN_MOVED_TO)) - output.emplace_back(ACTION_CREATE, fullname); + output.push_back({ ACTION_CREATE, itemPath }); else if ((evt.mask & IN_MODIFY) || (evt.mask & IN_CLOSE_WRITE)) - output.emplace_back(ACTION_UPDATE, fullname); + output.push_back({ ACTION_UPDATE, itemPath }); else if ((evt.mask & IN_DELETE ) || (evt.mask & IN_DELETE_SELF) || (evt.mask & IN_MOVE_SELF ) || (evt.mask & IN_MOVED_FROM)) - output.emplace_back(ACTION_DELETE, fullname); + output.push_back({ ACTION_DELETE, itemPath }); } } bytePos += sizeof(struct ::inotify_event) + evt.len; diff --git a/zen/dir_watcher.h b/zen/dir_watcher.h index 8045d184..b4796618 100755 --- a/zen/dir_watcher.h +++ b/zen/dir_watcher.h @@ -9,6 +9,7 @@ #include <vector> #include <memory> +#include <chrono> #include <functional> #include "file_error.h" @@ -50,26 +51,22 @@ public: struct Entry { - Entry() {} - Entry(ActionType action, const Zstring& filepath) : action_(action), filepath_(filepath) {} - - ActionType action_ = ACTION_CREATE; - Zstring filepath_; + ActionType action = ACTION_CREATE; + Zstring filePath; }; //extract accumulated changes since last call - std::vector<Entry> getChanges(const std::function<void()>& processGuiMessages); //throw FileError + std::vector<Entry> getChanges(const std::function<void()>& requestUiRefresh, std::chrono::milliseconds cbInterval); //throw FileError private: DirWatcher (const DirWatcher&) = delete; DirWatcher& operator=(const DirWatcher&) = delete; - const Zstring baseDirPath; + const Zstring baseDirPath_; struct Impl; const std::unique_ptr<Impl> pimpl_; }; - } #endif diff --git a/zen/error_log.h b/zen/error_log.h index 1fcbdefb..b6660850 100755 --- a/zen/error_log.h +++ b/zen/error_log.h @@ -20,18 +20,18 @@ namespace zen { enum MessageType { - TYPE_INFO = 0x1, - TYPE_WARNING = 0x2, - TYPE_ERROR = 0x4, - TYPE_FATAL_ERROR = 0x8, + MSG_TYPE_INFO = 0x1, + MSG_TYPE_WARNING = 0x2, + MSG_TYPE_ERROR = 0x4, + MSG_TYPE_FATAL_ERROR = 0x8, }; using MsgString = Zbase<wchar_t>; //std::wstring may employ small string optimization: we cannot accept bloating the "ErrorLog::entries" memory block below (think 1 million items) struct LogEntry { - time_t time; - MessageType type; + time_t time = 0; + MessageType type = MSG_TYPE_FATAL_ERROR; MsgString message; }; @@ -45,7 +45,7 @@ public: template <class String> //a wchar_t-based string! void logMsg(const String& text, MessageType type); - int getItemCount(int typeFilter = TYPE_INFO | TYPE_WARNING | TYPE_ERROR | TYPE_FATAL_ERROR) const; + int getItemCount(int typeFilter = MSG_TYPE_INFO | MSG_TYPE_WARNING | MSG_TYPE_ERROR | MSG_TYPE_FATAL_ERROR) const; //subset of std::vector<> interface: using const_iterator = std::vector<LogEntry>::const_iterator; @@ -67,10 +67,9 @@ private: //######################## implementation ########################## template <class String> inline -void ErrorLog::logMsg(const String& text, zen::MessageType type) +void ErrorLog::logMsg(const String& text, MessageType type) { - const LogEntry newEntry = { std::time(nullptr), type, copyStringTo<MsgString>(text) }; - entries_.push_back(newEntry); + entries_.push_back({ std::time(nullptr), type, copyStringTo<MsgString>(text) }); } @@ -90,13 +89,13 @@ String formatMessageImpl(const LogEntry& entry) //internal linkage { switch (entry.type) { - case TYPE_INFO: + case MSG_TYPE_INFO: return _("Info"); - case TYPE_WARNING: + case MSG_TYPE_WARNING: return _("Warning"); - case TYPE_ERROR: + case MSG_TYPE_ERROR: return _("Error"); - case TYPE_FATAL_ERROR: + case MSG_TYPE_FATAL_ERROR: return _("Serious Error"); } assert(false); @@ -104,7 +103,7 @@ String formatMessageImpl(const LogEntry& entry) //internal linkage }; String formattedText = L"[" + formatTime<String>(FORMAT_TIME, getLocalTime(entry.time)) + L"] " + copyStringTo<String>(getTypeName()) + L": "; - const size_t prefixLen = formattedText.size(); + const size_t prefixLen = formattedText.size(); //considers UTF-16 only! for (auto it = entry.message.begin(); it != entry.message.end(); ) if (*it == L'\n') diff --git a/zen/file_access.h b/zen/file_access.h index 743ad15d..c62ddc98 100755 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -13,6 +13,7 @@ #include "file_id_def.h" #include "serialize.h" + namespace zen { //note: certain functions require COM initialization! (vista_file_op.h) diff --git a/zen/file_error.h b/zen/file_error.h index decc0f7e..086d0998 100755 --- a/zen/file_error.h +++ b/zen/file_error.h @@ -12,6 +12,7 @@ #include "utf.h" #include "sys_error.h" //we'll need this later anyway! + namespace zen { class FileError //A high-level exception class giving detailed context information for end users diff --git a/zen/file_id_def.h b/zen/file_id_def.h index 7772e3e3..f58cb479 100755 --- a/zen/file_id_def.h +++ b/zen/file_id_def.h @@ -7,8 +7,6 @@ #ifndef FILE_ID_DEF_H_013287632486321493 #define FILE_ID_DEF_H_013287632486321493 -#include <utility> - #include <sys/stat.h> diff --git a/zen/file_io.h b/zen/file_io.h index 369cdc01..5b0b8cb0 100755 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -13,7 +13,7 @@ namespace zen { - const char LINE_BREAK[] = "\n"; //since OS X apple uses newline, too + const char LINE_BREAK[] = "\n"; //since OS X Apple uses newline, too /* OS-buffered file IO optimized for diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index aa4c439e..bc53f206 100755 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -55,7 +55,7 @@ void zen::traverseFolder(const Zstring& dirPath, continue; const Zstring& itemName = itemNameRaw; - if (itemName.empty()) //checks result of osx::normalizeUtfForPosix, too! + if (itemName.empty()) //checks result of normalizeUtfForPosix, too! throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name."); const Zstring& itemPath = appendSeparator(dirPath) + itemName; diff --git a/zen/file_traverser.h b/zen/file_traverser.h index 19359148..5c1683f8 100755 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h @@ -18,8 +18,8 @@ struct FileInfo { Zstring itemName; Zstring fullPath; - uint64_t fileSize; //[bytes] - time_t modTime; //number of seconds since Jan. 1st 1970 UTC + uint64_t fileSize; //[bytes] + time_t modTime = 0; //number of seconds since Jan. 1st 1970 UTC }; struct FolderInfo @@ -32,7 +32,7 @@ struct SymlinkInfo { Zstring itemName; Zstring fullPath; - time_t modTime; //number of seconds since Jan. 1st 1970 UTC + time_t modTime = 0; //number of seconds since Jan. 1st 1970 UTC }; //- non-recursive diff --git a/zen/fixed_list.h b/zen/fixed_list.h index 535ffa31..10b66233 100755 --- a/zen/fixed_list.h +++ b/zen/fixed_list.h @@ -11,6 +11,7 @@ #include <iterator> #include "stl_tools.h" + namespace zen { //std::list(C++11)-like class for inplace element construction supporting non-copyable/non-movable types diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 0f54a34b..09134c07 100755 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -5,7 +5,6 @@ // ***************************************************************************** #include "format_unit.h" -//#include <cwchar> //swprintf #include <ctime> #include <cstdio> #include "basic_math.h" @@ -112,7 +111,7 @@ std::wstring roundToBlock(double timeInHigh, const int blockSizeLow = granularity * timeInHigh < 1 ? numeric::nearMatch(granularity * timeInLow, std::begin(stepsLow), std::end(stepsLow)): numeric::nearMatch(granularity * timeInHigh, std::begin(stepsHigh), std::end(stepsHigh)) * unitLowPerHigh; - const int roundedtimeInLow = numeric::round(timeInLow / blockSizeLow) * blockSizeLow; + const int roundedtimeInLow = static_cast<int>(numeric::round(timeInLow / blockSizeLow) * blockSizeLow); std::wstring output = formatUnitTime(roundedtimeInLow / unitLowPerHigh, unitHigh); if (unitLowPerHigh > blockSizeLow) @@ -162,13 +161,13 @@ std::wstring zen::formatFraction(double fraction) -std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number) +std::wstring zen::impl::includeNumberSeparator(const std::wstring& number) { //we have to include thousands separator ourselves; this doesn't work for all countries (e.g india), but is better than nothing //::setlocale (LC_ALL, ""); -> implicitly called by wxLocale const lconv* localInfo = ::localeconv(); //always bound according to doc - const std::wstring& thousandSep = utfTo<std::wstring>(localInfo->thousands_sep); + const std::wstring& thousandSep = zen::utfTo<std::wstring>(localInfo->thousands_sep); // THOUSANDS_SEPARATOR = std::use_facet<std::numpunct<wchar_t>>(std::locale("")).thousands_sep(); - why not working? // DECIMAL_POINT = std::use_facet<std::numpunct<wchar_t>>(std::locale("")).decimal_point(); diff --git a/zen/format_unit.h b/zen/format_unit.h index 154ec542..9c6a4690 100755 --- a/zen/format_unit.h +++ b/zen/format_unit.h @@ -37,7 +37,7 @@ std::wstring formatNumber(NumberType number); //format integer number including //--------------- inline impelementation ------------------------------------------- -namespace ffs_Impl +namespace impl { std::wstring includeNumberSeparator(const std::wstring& number); } @@ -46,7 +46,7 @@ template <class NumberType> inline std::wstring formatNumber(NumberType number) { static_assert(IsInteger<NumberType>::value, ""); - return ffs_Impl::includeNumberSeparator(zen::numberTo<std::wstring>(number)); + return impl::includeNumberSeparator(zen::numberTo<std::wstring>(number)); } } diff --git a/zen/globals.h b/zen/globals.h index 32781f2a..c57d97ff 100755 --- a/zen/globals.h +++ b/zen/globals.h @@ -11,6 +11,7 @@ #include <memory> #include "scope_guard.h" + namespace zen { //solve static destruction order fiasco by providing shared ownership and serialized access to global variables @@ -13,6 +13,7 @@ #include "string_tools.h" #include "format_unit.h" + //minimal layer enabling text translation - without platform/library dependencies! #define ZEN_TRANS_CONCAT_SUB(X, Y) X ## Y @@ -55,7 +56,7 @@ std::shared_ptr<const TranslationHandler> getTranslator(); //######################## implementation ############################## -namespace implementation +namespace impl { inline Global<const TranslationHandler>& refGlobalTranslationHandler() @@ -69,14 +70,14 @@ Global<const TranslationHandler>& refGlobalTranslationHandler() inline std::shared_ptr<const TranslationHandler> getTranslator() { - return implementation::refGlobalTranslationHandler().get(); + return impl::refGlobalTranslationHandler().get(); } inline void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler) { - implementation::refGlobalTranslationHandler().set(std::move(newHandler)); + impl::refGlobalTranslationHandler().set(std::move(newHandler)); } diff --git a/zen/optional.h b/zen/optional.h index a2a1a169..0ef5f1db 100755 --- a/zen/optional.h +++ b/zen/optional.h @@ -10,6 +10,7 @@ #include <cassert> #include <type_traits> + namespace zen { /* diff --git a/zen/process_priority.h b/zen/process_priority.h index bec8f9b5..cfadfff1 100755 --- a/zen/process_priority.h +++ b/zen/process_priority.h @@ -10,6 +10,7 @@ #include <memory> #include "file_error.h" + namespace zen { //signal a "busy" state to the operating system diff --git a/zen/scope_guard.h b/zen/scope_guard.h index 2048af9b..6945b011 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__ < 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ < 2 || (__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support"); + static_assert(__GNUC__ < 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ < 3 || (__GNUC_MINOR__ == 3 && __GNUC_PATCHLEVEL__ <= 0))), "check std::uncaught_exceptions support"); namespace __cxxabiv1 { diff --git a/zen/shell_execute.h b/zen/shell_execute.h index 18e4854b..43bede61 100755 --- a/zen/shell_execute.h +++ b/zen/shell_execute.h @@ -17,10 +17,10 @@ namespace zen { //launch commandline and report errors via popup dialog //Windows: COM needs to be initialized before calling this function! -enum ExecutionType +enum class ExecutionType { - EXEC_TYPE_SYNC, - EXEC_TYPE_ASYNC + SYNC, + ASYNC }; namespace @@ -36,7 +36,7 @@ void shellExecute(const Zstring& command, ExecutionType type) //throw FileError - uses a zero-sized dummy window as a hack to keep focus which leaves a useless empty icon in ALT-TAB list in Windows */ - if (type == EXEC_TYPE_SYNC) + if (type == ExecutionType::SYNC) { //Posix ::system() - execute a shell command const int rv = ::system(command.c_str()); //do NOT use std::system as its documentation says nothing about "WEXITSTATUS(rv)", ect... diff --git a/zen/shutdown.cpp b/zen/shutdown.cpp index dd02814f..1794e4a8 100755 --- a/zen/shutdown.cpp +++ b/zen/shutdown.cpp @@ -18,7 +18,7 @@ void zen::shutdownSystem() //throw FileError //https://linux.die.net/man/2/reboot => needs admin rights! //"systemctl" should work without admin rights: - shellExecute("sleep 1; systemctl poweroff", EXEC_TYPE_ASYNC); //throw FileError + shellExecute("sleep 1; systemctl poweroff", ExecutionType::ASYNC); //throw FileError //sleep 1: give FFS some time to properly shut down! } @@ -27,7 +27,7 @@ void zen::shutdownSystem() //throw FileError void zen::suspendSystem() //throw FileError { //"systemctl" should work without admin rights: - shellExecute("systemctl suspend", EXEC_TYPE_ASYNC); //throw FileError + shellExecute("systemctl suspend", ExecutionType::ASYNC); //throw FileError } diff --git a/zen/shutdown.h b/zen/shutdown.h index b9d47df6..df2314f8 100755 --- a/zen/shutdown.h +++ b/zen/shutdown.h @@ -9,6 +9,7 @@ #include "file_error.h" + namespace zen { void shutdownSystem(); //throw FileError diff --git a/zen/string_base.h b/zen/string_base.h index 30699c38..2d043d4f 100755 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -13,8 +13,8 @@ #include <atomic> #include "string_tools.h" -//Zbase - a policy based string class optimizing performance and flexibility +//Zbase - a policy based string class optimizing performance and flexibility namespace zen { /* diff --git a/zen/string_tools.h b/zen/string_tools.h index 5058f78d..7734b6f0 100755 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -27,6 +27,7 @@ template <class Char> bool isDigit (Char c); //not exactly the same as "std: template <class Char> bool isHexDigit (Char c); template <class Char> bool isAsciiAlpha(Char c); template <class Char> Char asciiToLower(Char c); +template <class Char> Char asciiToUpper(Char c); //case-sensitive comparison (compile-time correctness: use different number of arguments as STL comparison predicates!) struct CmpBinary { template <class Char> int operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const; }; @@ -159,6 +160,15 @@ Char asciiToLower(Char c) } +template <class Char> inline +Char asciiToUpper(Char c) +{ + if (static_cast<Char>('a') <= c && c <= static_cast<Char>('z')) + return static_cast<Char>(c - static_cast<Char>('a') + static_cast<Char>('A')); + return c; +} + + template <class S, class T, class Function> inline bool startsWith(const S& str, const T& prefix, Function cmpStringFun) { @@ -740,7 +750,7 @@ std::pair<char, char> hexify(unsigned char c, bool upperCase) else return static_cast<char>('a' + (num - 10)); }; - return std::make_pair(hexifyDigit(c / 16), hexifyDigit(c % 16)); + return { hexifyDigit(c / 16), hexifyDigit(c % 16) }; } diff --git a/zen/string_traits.h b/zen/string_traits.h index 502250c2..805db46d 100755 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -63,7 +63,7 @@ private: //---------------------- implementation ---------------------- -namespace implementation +namespace impl { template<class S, class Char> //test if result of S::c_str() can convert to const Char* class HasConversion @@ -137,13 +137,13 @@ public: } template <class T> -struct IsStringLike : StaticBool<implementation::StringTraits<T>::isStringLike> {}; +struct IsStringLike : StaticBool<impl::StringTraits<T>::isStringLike> {}; template <class T> -struct GetCharType : ResultType<typename implementation::StringTraits<T>::CharType> {}; +struct GetCharType : ResultType<typename impl::StringTraits<T>::CharType> {}; -namespace implementation +namespace impl { //strlen/wcslen are vectorized since VS14 CTP3 inline size_t cStringLength(const char* str) { return std::strlen(str); } @@ -162,7 +162,7 @@ size_t cStringLength(const C* str) } #endif -template <class S, typename = typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type> inline +template <class S, typename = typename EnableIf<StringTraits<S>::isStringClass>::Type> inline const typename GetCharType<S>::Type* strBegin(const S& str) //SFINAE: T must be a "string" { return str.c_str(); @@ -179,7 +179,7 @@ inline const char* strBegin(const StringRef<const char >& ref) { return ref inline const wchar_t* strBegin(const StringRef<const wchar_t>& ref) { return ref.data(); } -template <class S, typename = typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type> inline +template <class S, typename = typename EnableIf<StringTraits<S>::isStringClass>::Type> inline size_t strLength(const S& str) //SFINAE: T must be a "string" { return str.length(); @@ -201,7 +201,7 @@ template <class S> inline auto strBegin(S&& str) -> const typename GetCharType<S>::Type* { static_assert(IsStringLike<S>::value, ""); - return implementation::strBegin(std::forward<S>(str)); + return impl::strBegin(std::forward<S>(str)); } @@ -209,7 +209,7 @@ template <class S> inline size_t strLength(S&& str) { static_assert(IsStringLike<S>::value, ""); - return implementation::strLength(std::forward<S>(str)); + return impl::strLength(std::forward<S>(str)); } } diff --git a/zen/thread.h b/zen/thread.h index 0bff5adc..bfb66c31 100755 --- a/zen/thread.h +++ b/zen/thread.h @@ -73,7 +73,7 @@ std::async replacement without crappy semantics: Example: Zstring dirpath = ... - auto ft = zen::runAsync([=](){ return zen::dirExists(dirpath); }); + auto ft = zen::runAsync([=]{ return zen::dirExists(dirpath); }); if (ft.wait_for(std::chrono::milliseconds(200)) == std::future_status::ready && ft.get()) //dir exising */ @@ -22,19 +22,19 @@ struct TimeComp //replaces std::tm and SYSTEMTIME int minute = 0; //0-59 int second = 0; //0-60 (including leap second) }; - inline bool operator==(const TimeComp& lhs, const TimeComp& rhs) { return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day && lhs.hour == rhs.hour && lhs.minute == rhs.minute && lhs.second == rhs.second; } +inline bool operator!=(const TimeComp& lhs, const TimeComp& rhs) { return !(lhs == rhs); } -TimeComp getLocalTime(time_t utc = std::time(nullptr)); //convert time_t (UTC) to local time components -time_t localToTimeT(const TimeComp& comp); //convert local time components to time_t (UTC), returns -1 on error +TimeComp getLocalTime(time_t utc = std::time(nullptr)); //convert time_t (UTC) to local time components, returns TimeComp() on error +time_t localToTimeT(const TimeComp& tc); //convert local time components to time_t (UTC), returns -1 on error -TimeComp getUtcTime(time_t utc = std::time(nullptr)); //convert time_t (UTC) to UTC time components -time_t utcToTimeT(const TimeComp& comp); //convert UTC time components to time_t (UTC), returns -1 on error +TimeComp getUtcTime(time_t utc = std::time(nullptr)); //convert time_t (UTC) to UTC time components, returns TimeComp() on error +time_t utcToTimeT(const TimeComp& tc); //convert UTC time components to time_t (UTC), returns -1 on error -TimeComp getCompileTime(); +TimeComp getCompileTime(); //returns TimeComp() on error //---------------------------------------------------------------------------------------------------------------------------------- @@ -45,7 +45,7 @@ format (current) date and time; example: formatTime<std::wstring>(FORMAT_TIME); -> "17:55:34" */ template <class String, class String2> -String formatTime(const String2& format, const TimeComp& comp = getLocalTime()); //format as specified by "std::strftime", returns empty string on failure +String formatTime(const String2& format, const TimeComp& tc = getLocalTime()); //format as specified by "std::strftime", returns empty string on failure //the "format" parameter of formatTime() is partially specialized with the following type tags: const struct FormatDateTag {} FORMAT_DATE = {}; //%x - locale dependent date representation: e.g. 08/23/01 @@ -58,8 +58,12 @@ const struct FormatIsoDateTimeTag {} FORMAT_ISO_DATE_TIME = {}; //%Y-%m-%d %H:%M //---------------------------------------------------------------------------------------------------------------------------------- +/* +example: parseTime("%Y-%m-%d %H:%M:%S", "2001-08-23 14:55:02"); + parseTime(FORMAT_ISO_DATE_TIME, "2001-08-23 14:55:02"); +*/ template <class String, class String2> -bool parseTime(const String& format, const String2& str, TimeComp& comp); //similar to ::strptime(), return true on success +TimeComp parseTime(const String& format, const String2& str); //similar to ::strptime() //---------------------------------------------------------------------------------------------------------------------------------- @@ -75,29 +79,26 @@ bool parseTime(const String& format, const String2& str, TimeComp& comp); //simi - - - //############################ implementation ############################## -namespace implementation +namespace impl { inline -std::tm toClibTimeComponents(const TimeComp& comp) +std::tm toClibTimeComponents(const TimeComp& tc) { - assert(1 <= comp.month && comp.month <= 12 && - 1 <= comp.day && comp.day <= 31 && - 0 <= comp.hour && comp.hour <= 23 && - 0 <= comp.minute && comp.minute <= 59 && - 0 <= comp.second && comp.second <= 61); + assert(1 <= tc.month && tc.month <= 12 && + 1 <= tc.day && tc.day <= 31 && + 0 <= tc.hour && tc.hour <= 23 && + 0 <= tc.minute && tc.minute <= 59 && + 0 <= tc.second && tc.second <= 61); std::tm ctc = {}; - ctc.tm_year = comp.year - 1900; //years since 1900 - ctc.tm_mon = comp.month - 1; //0-11 - ctc.tm_mday = comp.day; //1-31 - ctc.tm_hour = comp.hour; //0-23 - ctc.tm_min = comp.minute; //0-59 - ctc.tm_sec = comp.second; //0-60 (including leap second) - ctc.tm_isdst = -1; //> 0 if DST is active, == 0 if DST is not active, < 0 if the information is not available + ctc.tm_year = tc.year - 1900; //years since 1900 + ctc.tm_mon = tc.month - 1; //0-11 + ctc.tm_mday = tc.day; //1-31 + ctc.tm_hour = tc.hour; //0-23 + ctc.tm_min = tc.minute; //0-59 + ctc.tm_sec = tc.second; //0-60 (including leap second) + ctc.tm_isdst = -1; //> 0 if DST is active, == 0 if DST is not active, < 0 if the information is not available //ctc.tm_wday //ctc.tm_yday return ctc; @@ -106,14 +107,14 @@ std::tm toClibTimeComponents(const TimeComp& comp) inline TimeComp toZenTimeComponents(const std::tm& ctc) { - TimeComp comp; - comp.year = ctc.tm_year + 1900; - comp.month = ctc.tm_mon + 1; - comp.day = ctc.tm_mday; - comp.hour = ctc.tm_hour; - comp.minute = ctc.tm_min; - comp.second = ctc.tm_sec; - return comp; + TimeComp tc; + tc.year = ctc.tm_year + 1900; + tc.month = ctc.tm_mon + 1; + tc.day = ctc.tm_mday; + tc.hour = ctc.tm_hour; + tc.minute = ctc.tm_min; + tc.second = ctc.tm_sec; + return tc; } @@ -206,7 +207,6 @@ bool isValid(const std::tm& t) template <class CharType> inline size_t strftimeWrap(CharType* buffer, size_t bufferSize, const CharType* format, const std::tm* timeptr) { - return strftimeWrap_impl(buffer, bufferSize, format, timeptr); } @@ -215,10 +215,10 @@ struct UserDefinedFormatTag {}; struct PredefinedFormatTag {}; template <class String, class String2> inline -String formatTime(const String2& format, const TimeComp& comp, UserDefinedFormatTag) //format as specified by "std::strftime", returns empty string on failure +String formatTime(const String2& format, const TimeComp& tc, UserDefinedFormatTag) //format as specified by "std::strftime", returns empty string on failure { using CharType = typename GetCharType<String>::Type; - std::tm ctc = toClibTimeComponents(comp); + std::tm ctc = toClibTimeComponents(tc); std::mktime(&ctc); // unfortunately std::strftime() needs all elements of "struct tm" filled, e.g. tm_wday, tm_yday //note: although std::mktime() explicitly expects "local time", calculating weekday and day of year *should* be time-zone and DST independent @@ -227,11 +227,12 @@ String formatTime(const String2& format, const TimeComp& comp, UserDefinedFormat return String(buffer, charsWritten); } + template <class String, class FormatType> inline -String formatTime(FormatType, const TimeComp& comp, PredefinedFormatTag) +String formatTime(FormatType, const TimeComp& tc, PredefinedFormatTag) { using CharType = typename GetCharType<String>::Type; - return formatTime<String>(GetFormat<FormatType>().format(CharType()), comp, UserDefinedFormatTag()); + return formatTime<String>(GetFormat<FormatType>().format(CharType()), tc, UserDefinedFormatTag()); } } @@ -239,37 +240,49 @@ String formatTime(FormatType, const TimeComp& comp, PredefinedFormatTag) inline TimeComp getLocalTime(time_t utc) { - std::tm comp = {}; - if (::localtime_r(&utc, &comp) == nullptr) + if (utc == -1) //failure code from std::time(nullptr) + return TimeComp(); + + std::tm ctc = {}; + if (::localtime_r(&utc, &ctc) == nullptr) return TimeComp(); - return implementation::toZenTimeComponents(comp); + return impl::toZenTimeComponents(ctc); } inline TimeComp getUtcTime(time_t utc) { - std::tm comp = {}; - if (::gmtime_r(&utc, &comp) == nullptr) + if (utc == -1) //failure code from std::time(nullptr) return TimeComp(); - return implementation::toZenTimeComponents(comp); + std::tm ctc = {}; + if (::gmtime_r(&utc, &ctc) == nullptr) + return TimeComp(); + + return impl::toZenTimeComponents(ctc); } inline -time_t localToTimeT(const TimeComp& comp) //returns -1 on error +time_t localToTimeT(const TimeComp& tc) //returns -1 on error { - std::tm ctc = implementation::toClibTimeComponents(comp); + if (tc == TimeComp()) + return -1; + + std::tm ctc = impl::toClibTimeComponents(tc); return std::mktime(&ctc); } inline -time_t utcToTimeT(const TimeComp& comp) //returns -1 on error +time_t utcToTimeT(const TimeComp& tc) //returns -1 on error { - std::tm ctc = implementation::toClibTimeComponents(comp); + if (tc == TimeComp()) + return -1; + + std::tm ctc = impl::toClibTimeComponents(tc); ctc.tm_isdst = 0; //"Zero (0) to indicate that standard time is in effect" => unused by _mkgmtime, but take no chances return ::timegm(&ctc); } @@ -283,39 +296,36 @@ TimeComp getCompileTime() if (compileTime[4] == ' ') //day is space-padded, but %d expects zero-padding compileTime[4] = '0'; - TimeComp tc = {}; - if (parseTime("%b %d %Y %H:%M:%S", compileTime, tc)) - return tc; - - assert(false); - return TimeComp(); + return parseTime("%b %d %Y %H:%M:%S", compileTime); } template <class String, class String2> inline -String formatTime(const String2& format, const TimeComp& comp) +String formatTime(const String2& format, const TimeComp& tc) { + if (tc == TimeComp()) //failure code from getLocalTime() + return String(); + using FormatTag = typename SelectIf< IsSameType<String2, FormatDateTag >::value || IsSameType<String2, FormatTimeTag >::value || IsSameType<String2, FormatDateTimeTag >::value || IsSameType<String2, FormatIsoDateTag >::value || IsSameType<String2, FormatIsoTimeTag >::value || - IsSameType<String2, FormatIsoDateTimeTag>::value, implementation::PredefinedFormatTag, implementation::UserDefinedFormatTag>::Type; + IsSameType<String2, FormatIsoDateTimeTag>::value, impl::PredefinedFormatTag, impl::UserDefinedFormatTag>::Type; - return implementation::formatTime<String>(format, comp, FormatTag()); + return impl::formatTime<String>(format, tc, FormatTag()); } +namespace impl +{ template <class String, class String2> -bool parseTime(const String& format, const String2& str, TimeComp& comp) //return true on success +TimeComp parseTime(const String& format, const String2& str, UserDefinedFormatTag) { using CharType = typename GetCharType<String>::Type; static_assert(IsSameType<CharType, typename GetCharType<String2>::Type>::value, ""); - const CharType* itFmt = strBegin(format); - const CharType* const fmtLast = itFmt + strLength(format); - const CharType* itStr = strBegin(str); const CharType* const strLast = itStr + strLength(str); @@ -332,6 +342,11 @@ bool parseTime(const String& format, const String2& str, TimeComp& comp) //retur return true; }; + TimeComp output; + + const CharType* itFmt = strBegin(format); + const CharType* const fmtLast = itFmt + strLength(format); + for (; itFmt != fmtLast; ++itFmt) { const CharType fmt = *itFmt; @@ -340,22 +355,22 @@ bool parseTime(const String& format, const String2& str, TimeComp& comp) //retur { ++itFmt; if (itFmt == fmtLast) - return false; + return TimeComp(); switch (*itFmt) { case 'Y': - if (!extractNumber(comp.year, 4)) - return false; + if (!extractNumber(output.year, 4)) + return TimeComp(); break; case 'm': - if (!extractNumber(comp.month, 2)) - return false; + if (!extractNumber(output.month, 2)) + return TimeComp(); break; case 'b': //abbreviated month name: Jan-Dec { if (strLast - itStr < 3) - return false; + return TimeComp(); const char* months[] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; auto itMonth = std::find_if(std::begin(months), std::end(months), [&](const char* name) @@ -365,30 +380,30 @@ bool parseTime(const String& format, const String2& str, TimeComp& comp) //retur asciiToLower(itStr[2]) == name[2]; }); if (itMonth == std::end(months)) - return false; + return TimeComp(); - comp.month = 1 + static_cast<int>(itMonth - std::begin(months)); + output.month = 1 + static_cast<int>(itMonth - std::begin(months)); itStr += 3; } break; case 'd': - if (!extractNumber(comp.day, 2)) - return false; + if (!extractNumber(output.day, 2)) + return TimeComp(); break; case 'H': - if (!extractNumber(comp.hour, 2)) - return false; + if (!extractNumber(output.hour, 2)) + return TimeComp(); break; case 'M': - if (!extractNumber(comp.minute, 2)) - return false; + if (!extractNumber(output.minute, 2)) + return TimeComp(); break; case 'S': - if (!extractNumber(comp.second, 2)) - return false; + if (!extractNumber(output.second, 2)) + return TimeComp(); break; default: - return false; + return TimeComp(); } } else if (isWhiteSpace(fmt)) //single whitespace in format => skip 0..n whitespace chars @@ -399,12 +414,36 @@ bool parseTime(const String& format, const String2& str, TimeComp& comp) //retur else { if (itStr == strLast || *itStr != fmt) - return false; + return TimeComp(); ++itStr; } } - return itStr == strLast; + if (itStr != strLast) + return TimeComp(); + + return output; +} + + +template <class FormatType, class String> inline +TimeComp parseTime(FormatType, const String& str, PredefinedFormatTag) +{ + using CharType = typename GetCharType<String>::Type; + return parseTime(GetFormat<FormatType>().format(CharType()), str, UserDefinedFormatTag()); +} +} + + +template <class String, class String2> inline +TimeComp parseTime(const String& format, const String2& str) +{ + using FormatTag = typename SelectIf< + IsSameType<String, FormatIsoDateTag >::value || + IsSameType<String, FormatIsoTimeTag >::value || + IsSameType<String, FormatIsoDateTimeTag>::value, impl::PredefinedFormatTag, impl::UserDefinedFormatTag>::Type; + + return impl::parseTime(format, str, FormatTag()); } } @@ -12,6 +12,7 @@ #include "string_tools.h" //copyStringTo #include "optional.h" + namespace zen { //convert all(!) char- and wchar_t-based "string-like" objects applying a UTF8 conversions (but only if necessary!) @@ -39,7 +40,7 @@ UtfString getUnicodeSubstring(const UtfString& str, size_t uniPosFirst, size_t u //----------------------- implementation ---------------------------------- -namespace implementation +namespace impl { using CodePoint = uint32_t; using Char16 = uint16_t; @@ -308,7 +309,7 @@ using UtfDecoder = UtfDecoderImpl<CharType, sizeof(CharType)>; template <class UtfString> inline bool isValidUtf(const UtfString& str) { - using namespace implementation; + using namespace impl; UtfDecoder<typename GetCharType<UtfString>::Type> decoder(strBegin(str), strLength(str)); while (Opt<CodePoint> cp = decoder.getNext()) @@ -323,7 +324,7 @@ template <class UtfString> inline size_t unicodeLength(const UtfString& str) //return number of code points (+ correctly handle broken UTF encoding) { size_t uniLen = 0; - implementation::UtfDecoder<typename GetCharType<UtfString>::Type> decoder(strBegin(str), strLength(str)); + impl::UtfDecoder<typename GetCharType<UtfString>::Type> decoder(strBegin(str), strLength(str)); while (decoder.getNext()) ++uniLen; return uniLen; @@ -334,7 +335,7 @@ template <class UtfString> inline UtfString getUnicodeSubstring(const UtfString& str, size_t uniPosFirst, size_t uniPosLast) //return position of unicode char in UTF-encoded string { assert(uniPosFirst <= uniPosLast && uniPosLast <= unicodeLength(str)); - using namespace implementation; + using namespace impl; using CharType = typename GetCharType<UtfString>::Type; UtfString output; if (uniPosFirst >= uniPosLast) //optimize for empty range @@ -353,7 +354,7 @@ UtfString getUnicodeSubstring(const UtfString& str, size_t uniPosFirst, size_t u //------------------------------------------------------------------------------------------- -namespace implementation +namespace impl { template <class TargetString, class SourceString> inline TargetString utfTo(const SourceString& str, FalseType) @@ -380,7 +381,7 @@ TargetString utfTo(const SourceString& str, TrueType) { return copyStringTo<Targ template <class TargetString, class SourceString> inline TargetString utfTo(const SourceString& str) { - return implementation::utfTo<TargetString>(str, StaticBool<sizeof(typename GetCharType<SourceString>::Type) == sizeof(typename GetCharType<TargetString>::Type)>()); + return impl::utfTo<TargetString>(str, StaticBool<sizeof(typename GetCharType<SourceString>::Type) == sizeof(typename GetCharType<TargetString>::Type)>()); } } diff --git a/zen/xml_io.h b/zen/xml_io.h index 8d3346c6..a53a7edb 100755 --- a/zen/xml_io.h +++ b/zen/xml_io.h @@ -10,11 +10,11 @@ #include <zenxml/xml.h> #include "file_error.h" + //combine zen::Xml and zen file i/o //-> loadXmlDocument vs loadStream: //1. better error reporting //2. quick exit if (potentially large) input file is not an XML - namespace zen { XmlDoc loadXmlDocument(const Zstring& filePath); //throw FileError diff --git a/zen/zstring.cpp b/zen/zstring.cpp index 2aa3b3f2..ce94fe56 100755 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -44,18 +44,17 @@ int compareNoCaseUtf8(const char* lhs, size_t lhsLen, const char* rhs, size_t rh //- strncasecmp implements ASCII CI-comparsion only! => signature is broken for UTF8-input; toupper() similarly doesn't support Unicode //- wcsncasecmp: https://opensource.apple.com/source/Libc/Libc-763.12/string/wcsncasecmp-fbsd.c // => re-implement comparison based on towlower() to avoid memory allocations - using namespace zen::implementation; - UtfDecoder<char> decL(lhs, lhsLen); - UtfDecoder<char> decR(rhs, rhsLen); + impl::UtfDecoder<char> decL(lhs, lhsLen); + impl::UtfDecoder<char> decR(rhs, rhsLen); for (;;) { - const Opt<CodePoint> cpL = decL.getNext(); - const Opt<CodePoint> cpR = decR.getNext(); + const Opt<impl::CodePoint> cpL = decL.getNext(); + const Opt<impl::CodePoint> cpR = decR.getNext(); if (!cpL || !cpR) return static_cast<int>(!cpR) - static_cast<int>(!cpL); - static_assert(sizeof(wchar_t) == sizeof(CodePoint), ""); + static_assert(sizeof(wchar_t) == sizeof(impl::CodePoint), ""); const wchar_t charL = ::towlower(static_cast<wchar_t>(*cpL)); //ordering: towlower() converts to higher code points than towupper() const wchar_t charR = ::towlower(static_cast<wchar_t>(*cpR)); //uses LC_CTYPE category of current locale if (charL != charR) diff --git a/zenXml/zenxml/bind.h b/zenXml/zenxml/bind.h index f3cfa398..824c6cc6 100755 --- a/zenXml/zenxml/bind.h +++ b/zenXml/zenxml/bind.h @@ -12,6 +12,7 @@ #include "parser.h" #include "io.h" + namespace zen { /** @@ -167,17 +168,17 @@ public: in["elem3"](value3); // \endcode */ - XmlIn(const XmlDoc& doc) : log(std::make_shared<ErrorLog>()) { refList.push_back(&doc.root()); } + XmlIn(const XmlDoc& doc) : log_(std::make_shared<ErrorLog>()) { refList_.push_back(&doc.root()); } ///Construct an input proxy for a single XML element, may be nullptr /** \sa XmlIn(const XmlDoc& doc) */ - XmlIn(const XmlElement* element) : log(std::make_shared<ErrorLog>()) { refList.push_back(element); } + XmlIn(const XmlElement* element) : log_(std::make_shared<ErrorLog>()) { refList_.push_back(element); } ///Construct an input proxy for a single XML element /** \sa XmlIn(const XmlDoc& doc) */ - XmlIn(const XmlElement& element) : log(std::make_shared<ErrorLog>()) { refList.push_back(&element); } + XmlIn(const XmlElement& element) : log_(std::make_shared<ErrorLog>()) { refList_.push_back(&element); } ///Retrieve a handle to an XML child element for reading /** @@ -190,14 +191,14 @@ public: { std::vector<const XmlElement*> childList; - if (refIndex < refList.size()) + if (refIndex_ < refList_.size()) { - auto iterPair = refList[refIndex]->getChildren(name); + auto iterPair = refList_[refIndex_]->getChildren(name); std::for_each(iterPair.first, iterPair.second, [&](const XmlElement& child) { childList.push_back(&child); }); } - return XmlIn(childList, childList.empty() ? getChildNameFormatted(name) : std::string(), log); + return XmlIn(childList, childList.empty() ? getChildNameFormatted(name) : std::string(), log_); } ///Refer to next sibling element with the same name @@ -221,7 +222,7 @@ public: } \endcode */ - void next() { ++refIndex; } + void next() { ++refIndex_; } ///Read user data from the underlying XML element /** @@ -232,16 +233,16 @@ public: template <class T> bool operator()(T& value) const { - if (refIndex < refList.size()) + if (refIndex_ < refList_.size()) { - bool success = readStruc(*refList[refIndex], value); + bool success = readStruc(*refList_[refIndex_], value); if (!success) - log->notifyConversionError(getNameFormatted()); + log_->notifyConversionError(getNameFormatted()); return success; } else { - log->notifyMissingElement(getNameFormatted()); + log_->notifyMissingElement(getNameFormatted()); return false; } } @@ -267,22 +268,22 @@ public: template <class String, class T> bool attribute(const String& name, T& value) const { - if (refIndex < refList.size()) + if (refIndex_ < refList_.size()) { - bool success = refList[refIndex]->getAttribute(name, value); + bool success = refList_[refIndex_]->getAttribute(name, value); if (!success) - log->notifyMissingAttribute(getNameFormatted(), utfTo<std::string>(name)); + log_->notifyMissingAttribute(getNameFormatted(), utfTo<std::string>(name)); return success; } else { - log->notifyMissingElement(getNameFormatted()); + log_->notifyMissingElement(getNameFormatted()); return false; } } ///Return a pointer to the underlying Xml element, may be nullptr - const XmlElement* get() const { return refIndex < refList.size() ? refList[refIndex] : nullptr; } + const XmlElement* get() const { return refIndex_ < refList_.size() ? refList_[refIndex_] : nullptr; } ///Test whether the underlying XML element exists /** @@ -316,7 +317,7 @@ public: However be aware that the chain of connected proxy instances will be broken once you call XmlIn::get() to retrieve the underlying pointer. Errors that occur when working with this pointer are not logged by the original set of related instances. */ - bool errorsOccured() const { return !log->elementList().empty(); } + bool errorsOccured() const { return !log_->elementList().empty(); } ///Get a list of XML element and attribute names which failed to convert to user data. /** @@ -327,14 +328,14 @@ public: std::vector<String> getErrorsAs() const { std::vector<String> output; - const auto& elements = log->elementList(); + const auto& elements = log_->elementList(); std::transform(elements.begin(), elements.end(), std::back_inserter(output), [](const std::string& str) { return utfTo<String>(str); }); return output; } private: XmlIn(const std::vector<const XmlElement*>& siblingList, const std::string& elementNameFmt, const std::shared_ptr<ErrorLog>& sharedlog) : - refList(siblingList), formattedName(elementNameFmt), log(sharedlog) + refList_(siblingList), formattedName_(elementNameFmt), log_(sharedlog) { assert((!siblingList.empty() && elementNameFmt.empty()) || (siblingList.empty() && !elementNameFmt.empty())); } static std::string getNameFormatted(const XmlElement& elem) //"<Root> <Level1> <Level2>" @@ -344,13 +345,13 @@ private: std::string getNameFormatted() const { - if (refIndex < refList.size()) + if (refIndex_ < refList_.size()) { - assert(formattedName.empty()); - return getNameFormatted(*refList[refIndex]); + assert(formattedName_.empty()); + return getNameFormatted(*refList_[refIndex_]); } else - return formattedName; + return formattedName_; } std::string getChildNameFormatted(const std::string& childName) const @@ -379,10 +380,10 @@ private: std::set<std::string> usedElements; }; - std::vector<const XmlElement*> refList; //all sibling elements with same name (all pointers bound!) - size_t refIndex = 0; //this sibling's index in refList - std::string formattedName; //contains full and formatted element name if (and only if) refList is empty - std::shared_ptr<ErrorLog> log; //always bound + std::vector<const XmlElement*> refList_; //all sibling elements with same name (all pointers bound!) + size_t refIndex_ = 0; //this sibling's index in refList_ + std::string formattedName_; //contains full and formatted element name if (and only if) refList_ is empty + std::shared_ptr<ErrorLog> log_; //always bound }; } diff --git a/zenXml/zenxml/cvrt_struc.h b/zenXml/zenxml/cvrt_struc.h index 87687929..3a18ea73 100755 --- a/zenXml/zenxml/cvrt_struc.h +++ b/zenXml/zenxml/cvrt_struc.h @@ -9,6 +9,7 @@ #include "dom.h" + namespace zen { /** @@ -42,11 +43,6 @@ template <class T> void writeStruc(const T& value, XmlElement& output); - - - - - //------------------------------ implementation ------------------------------------- namespace impl_2384343 { @@ -140,7 +136,7 @@ struct ConvertElement<T, VALUE_TYPE_STL_CONTAINER> value.insert(value.end(), childVal); else success = false; - //should we support insertion of partially-loaded struct?? + //should we support insertion of partially-loaded struct?? } return success; } diff --git a/zenXml/zenxml/cvrt_text.h b/zenXml/zenxml/cvrt_text.h index 6ff1ec94..17444861 100755 --- a/zenXml/zenxml/cvrt_text.h +++ b/zenXml/zenxml/cvrt_text.h @@ -10,6 +10,7 @@ #include <zen/utf.h> #include <zen/string_tools.h> + namespace zen { /** diff --git a/zenXml/zenxml/dom.h b/zenXml/zenxml/dom.h index 09977f1d..c8959e4b 100755 --- a/zenXml/zenxml/dom.h +++ b/zenXml/zenxml/dom.h @@ -8,10 +8,12 @@ #define DOM_H_82085720723894567204564256 #include <string> +#include <list> #include <map> #include <zen/fixed_list.h> #include "cvrt_text.h" //"readText/writeText" + namespace zen { class XmlDoc; @@ -20,11 +22,11 @@ class XmlDoc; class XmlElement { public: - XmlElement() : parent_(nullptr) {} + XmlElement() {} //Construct an empty XML element template <class String> - explicit XmlElement(const String& name, XmlElement* parentElement = nullptr) : name_(utfTo<std::string>(name)), parent_(parentElement) {} + explicit XmlElement(const String& name, XmlElement* parent = nullptr) : name_(utfTo<std::string>(name)), parent_(parent) {} ///Retrieve the name of this XML element. /** @@ -60,8 +62,8 @@ public: template <class String, class T> bool getAttribute(const String& name, T& value) const { - auto it = attributes.find(utfTo<std::string>(name)); - return it == attributes.end() ? false : readText(it->second, value); + auto it = attributesSorted_.find(utfTo<std::string>(name)); + return it == attributesSorted_.end() ? false : readText(it->second->value, value); } ///Create or update an XML attribute. @@ -74,9 +76,19 @@ public: template <class String, class T> void setAttribute(const String& name, const T& value) { + std::string attrName = utfTo<std::string>(name); + std::string attrValue; writeText(value, attrValue); - attributes[utfTo<std::string>(name)] = attrValue; + + auto it = attributesSorted_.find(attrName); + if (it != attributesSorted_.end()) + it->second->value = std::move(attrValue); + else + { + auto itBack = attributes_.insert(attributes_.end(), { attrName, std::move(attrValue) }); + attributesSorted_.emplace(std::move(attrName), itBack); + } } ///Remove the attribute with the given name. @@ -84,7 +96,15 @@ public: \tparam String Arbitrary string-like type: e.g. std::string, wchar_t*, char[], wchar_t, wxString, MyStringClass, ... */ template <class String> - void removeAttribute(const String& name) { attributes.erase(utfTo<std::string>(name)); } + void removeAttribute(const String& name) + { + auto it = attributesSorted_.find(utfTo<std::string>(name)); + if (it != attributesSorted_.end()) + { + attributes_.erase(it->second); + attributesSorted_.erase(it); + } + } ///Create a new child element and return a reference to it. /** @@ -94,10 +114,10 @@ public: template <class String> XmlElement& addChild(const String& name) { - std::string utf8Name = utfTo<std::string>(name); - childElements.emplace_back(utf8Name, this); - XmlElement& newElement = childElements.back(); - childElementsSorted.emplace(utf8Name, &newElement); + std::string elemName = utfTo<std::string>(name); + childElements_.emplace_back(elemName, this); + XmlElement& newElement = childElements_.back(); + childElementsSorted_.emplace(std::move(elemName), &newElement); return newElement; } @@ -110,8 +130,8 @@ public: template <class String> const XmlElement* getChild(const String& name) const { - auto it = childElementsSorted.find(utfTo<std::string>(name)); - return it == childElementsSorted.end() ? nullptr : it->second; + auto it = childElementsSorted_.find(utfTo<std::string>(name)); + return it == childElementsSorted_.end() ? nullptr : it->second; } ///\sa getChild @@ -121,8 +141,8 @@ public: return const_cast<XmlElement*>(static_cast<const XmlElement*>(this)->getChild(name)); } - template < class IterTy, //underlying iterator type - class T, //target object type + template < class IterTy, //underlying iterator type + class T, //target object type class AccessPolicy > //access policy: see AccessPtrMap class PtrIter : public std::iterator<std::input_iterator_tag, T>, private AccessPolicy //get rid of shared_ptr indirection { @@ -159,11 +179,11 @@ public: \return A pair of STL begin/end iterators to access the child elements sequentially. */ template <class String> - std::pair<ChildIterConst2, ChildIterConst2> getChildren(const String& name) const { return childElementsSorted.equal_range(utfTo<std::string>(name)); } + std::pair<ChildIterConst2, ChildIterConst2> getChildren(const String& name) const { return childElementsSorted_.equal_range(utfTo<std::string>(name)); } ///\sa getChildren template <class String> - std::pair<ChildIter2, ChildIter2> getChildren(const String& name) { return childElementsSorted.equal_range(utfTo<std::string>(name)); } + std::pair<ChildIter2, ChildIter2> getChildren(const String& name) { return childElementsSorted_.equal_range(utfTo<std::string>(name)); } struct AccessListElement { @@ -183,41 +203,47 @@ public: \endcode \return A pair of STL begin/end iterators to access all child elements sequentially. */ - std::pair<ChildIterConst, ChildIterConst> getChildren() const { return std::make_pair(childElements.begin(), childElements.end()); } + std::pair<ChildIterConst, ChildIterConst> getChildren() const { return { childElements_.begin(), childElements_.end() }; } ///\sa getChildren - std::pair<ChildIter, ChildIter> getChildren() { return std::make_pair(childElements.begin(), childElements.end()); } + std::pair<ChildIter, ChildIter> getChildren() { return { childElements_.begin(), childElements_.end() }; } ///Get parent XML element, may be nullptr for root element XmlElement* parent() { return parent_; } ///Get parent XML element, may be nullptr for root element const XmlElement* parent() const { return parent_; } - using AttrIter = std::map<std::string, std::string>::const_iterator; + struct Attribute + { + std::string name; + std::string value; + }; + using AttrIter = std::list<Attribute>::const_iterator; /* -> disabled documentation extraction \brief Get all attributes associated with the element. \code auto iterPair = elem.getAttributes(); for (auto it = iterPair.first; it != iterPair.second; ++it) - std::cout << "name: " << it->first << " value: " << it->second << "\n"; + std::cout << "name: " << it->name << " value: " << it->value << "\n"; \endcode \return A pair of STL begin/end iterators to access all attributes sequentially as a list of name/value pairs of std::string. */ - std::pair<AttrIter, AttrIter> getAttributes() const { return std::make_pair(attributes.begin(), attributes.end()); } + std::pair<AttrIter, AttrIter> getAttributes() const { return { attributes_.begin(), attributes_.end() }; } //swap two elements while keeping references to parent. -> disabled documentation extraction void swapSubtree(XmlElement& other) { - name_ .swap(other.name_); - value_ .swap(other.value_); - attributes.swap(other.attributes); - childElements.swap(other.childElements); - childElementsSorted.swap(other.childElementsSorted); - //std::swap(parent_, other.parent_); -> parent is physical location; update children's parent reference instead: - for (XmlElement& child : childElements) + name_ .swap(other.name_); + value_ .swap(other.value_); + attributes_ .swap(other.attributes_); + attributesSorted_ .swap(other.attributesSorted_); + childElements_ .swap(other.childElements_); + childElementsSorted_.swap(other.childElementsSorted_); + + for (XmlElement& child : childElements_) child.parent_ = this; - for (XmlElement& child : other.childElements) + for (XmlElement& child : other.childElements_) child.parent_ = &other; } @@ -227,10 +253,13 @@ private: std::string name_; std::string value_; - std::map<std::string, std::string> attributes; - FixedList<XmlElement> childElements; //all child elements in order of creation - std::multimap<std::string, XmlElement*> childElementsSorted; //alternate key: sorted by element name - XmlElement* parent_; + + std::list<Attribute> attributes_; //attributes in order of creation + std::map<std::string, std::list<Attribute>::iterator> attributesSorted_; //alternate view: sorted by attribute name + + FixedList<XmlElement> childElements_; //child elements in order of creation + std::multimap<std::string, XmlElement*> childElementsSorted_; //alternate view: sorted by element name + XmlElement* parent_ = nullptr; }; @@ -247,7 +276,7 @@ class XmlDoc { public: ///Default constructor setting up an empty XML document with a standard declaration: <?xml version="1.0" encoding="UTF-8" ?> - XmlDoc() : version_("1.0"), encoding_("UTF-8"), rootElement("Root") {} + XmlDoc() {} XmlDoc(XmlDoc&& tmp) { swap(tmp); } XmlDoc& operator=(XmlDoc&& tmp) { swap(tmp); return *this; } @@ -258,12 +287,12 @@ public: \param rootName The name of the XML document's root element. */ template <class String> - XmlDoc(String rootName) : version_("1.0"), encoding_("UTF-8"), rootElement(rootName) {} + XmlDoc(String rootName) : root_(rootName) {} ///Get a const reference to the document's root element. - const XmlElement& root() const { return rootElement; } + const XmlElement& root() const { return root_; } ///Get a reference to the document's root element. - XmlElement& root() { return rootElement; } + XmlElement& root() { return root_; } ///Get the version used in the XML declaration. /** @@ -313,18 +342,18 @@ public: version_ .swap(other.version_); encoding_ .swap(other.encoding_); standalone_.swap(other.standalone_); - rootElement.swapSubtree(other.rootElement); + root_.swapSubtree(other.root_); } private: XmlDoc (const XmlDoc&) = delete; //not implemented, thanks to XmlElement::parent_ XmlDoc& operator=(const XmlDoc&) = delete; - std::string version_; - std::string encoding_; + std::string version_ { "1.0" }; + std::string encoding_{ "UTF-8" }; std::string standalone_; - XmlElement rootElement; + XmlElement root_{ "Root" }; }; } diff --git a/zenXml/zenxml/io.h b/zenXml/zenxml/io.h index 1361d865..6d5a21d5 100755 --- a/zenXml/zenxml/io.h +++ b/zenXml/zenxml/io.h @@ -13,6 +13,7 @@ #include <zen/utf.h> #include "error.h" + namespace zen { /** diff --git a/zenXml/zenxml/parser.h b/zenXml/zenxml/parser.h index bff2bb50..5c6a9ec2 100755 --- a/zenXml/zenxml/parser.h +++ b/zenXml/zenxml/parser.h @@ -73,7 +73,7 @@ XmlDoc parse(const std::string& stream); //throw XmlParsingError //---------------------------- implementation ---------------------------- //see: http://www.w3.org/TR/xml/ -namespace implementation +namespace impl { template <class Predicate> inline std::string normalize(const std::string& str, Predicate pred) //pred: unary function taking a char, return true if value shall be encoded as hex @@ -111,7 +111,9 @@ std::string normalize(const std::string& str, Predicate pred) //pred: unary func inline std::string normalizeName(const std::string& str) { - return normalize(str, [](char c) { return isWhiteSpace(c) || c == '=' || c == '/' || c == '\'' || c == '\"'; }); + const std::string nameFmt = normalize(str, [](char c) { return isWhiteSpace(c) || c == '=' || c == '/' || c == '\'' || c == '\"'; }); + assert(!nameFmt.empty()); + return nameFmt; } inline @@ -201,12 +203,12 @@ void serialize(const XmlElement& element, std::string& stream, auto attr = element.getAttributes(); for (auto it = attr.first; it != attr.second; ++it) - stream += ' ' + normalizeName(it->first) + "=\"" + normalizeAttribValue(it->second) + '\"'; + stream += ' ' + normalizeName(it->name) + "=\"" + normalizeAttribValue(it->value) + '\"'; - //no support for mixed-mode content auto iterPair = element.getChildren(); if (iterPair.first != iterPair.second) //structured element { + //no support for mixed-mode content stream += '>' + lineBreak; std::for_each(iterPair.first, iterPair.second, @@ -254,7 +256,7 @@ std::string serialize(const XmlDoc& doc, inline std::string serialize(const XmlDoc& doc, const std::string& lineBreak, - const std::string& indent) { return implementation::serialize(doc, lineBreak, indent); } + const std::string& indent) { return impl::serialize(doc, lineBreak, indent); } /* Grammar for XML parser @@ -280,7 +282,7 @@ pm-expression: element-list-expression */ -namespace implementation +namespace impl { struct Token { @@ -355,7 +357,7 @@ public: { std::string name(&*pos, nameEnd - pos); pos = nameEnd; - return implementation::denormalize(name); + return denormalize(name); } //unknown token @@ -371,7 +373,7 @@ public: }); std::string output(pos, it); pos = it; - return implementation::denormalize(output); + return denormalize(output); } std::string extractAttributeValue() @@ -385,7 +387,7 @@ public: }); std::string output(pos, it); pos = it; - return implementation::denormalize(output); + return denormalize(output); } size_t posRow() const //current row beginning with 0 @@ -575,7 +577,7 @@ private: inline XmlDoc parse(const std::string& stream) //throw XmlParsingError { - return implementation::XmlParser(stream).parse(); //throw XmlParsingError + return impl::XmlParser(stream).parse(); //throw XmlParsingError } } diff --git a/zenXml/zenxml/xml.h b/zenXml/zenxml/xml.h index e0035c2b..9510645a 100755 --- a/zenXml/zenxml/xml.h +++ b/zenXml/zenxml/xml.h @@ -9,6 +9,7 @@ #include "bind.h" + /// The zen::Xml namespace namespace zen {} |