summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Wilhelm <shieldwed@outlook.com>2017-01-08 18:21:23 +0100
committerDaniel Wilhelm <shieldwed@outlook.com>2017-01-08 18:21:23 +0100
commitfe660cdff59aa3a939479ed60172e5c0803552b2 (patch)
tree045cf295b79de10f75ed6362c5836db25c9fc63a
parent8.6 (diff)
downloadFreeFileSync-fe660cdff59aa3a939479ed60172e5c0803552b2.tar.gz
FreeFileSync-fe660cdff59aa3a939479ed60172e5c0803552b2.tar.bz2
FreeFileSync-fe660cdff59aa3a939479ed60172e5c0803552b2.zip
8.7
-rw-r--r--FreeFileSync/Build/Changelog.txt17
-rw-r--r--FreeFileSync/Build/Help/html/exclude-items.html2
-rw-r--r--FreeFileSync/Build/Languages/arabic.lng37
-rw-r--r--FreeFileSync/Build/Languages/english_uk.lng2
-rw-r--r--FreeFileSync/Build/Languages/german.lng110
-rw-r--r--FreeFileSync/Build/Languages/hebrew.lng2
-rw-r--r--FreeFileSync/Build/Languages/portuguese_br.lng2
-rw-r--r--FreeFileSync/Build/Languages/russian.lng10
-rw-r--r--FreeFileSync/Build/Languages/spanish.lng2
-rw-r--r--FreeFileSync/Build/Resources.zipbin295462 -> 304445 bytes
-rw-r--r--FreeFileSync/Source/RealTimeSync/application.cpp27
-rw-r--r--FreeFileSync/Source/RealTimeSync/main_dlg.cpp24
-rw-r--r--FreeFileSync/Source/RealTimeSync/main_dlg.h6
-rw-r--r--FreeFileSync/Source/RealTimeSync/monitor.cpp22
-rw-r--r--FreeFileSync/Source/RealTimeSync/monitor.h2
-rw-r--r--FreeFileSync/Source/RealTimeSync/tray_menu.cpp19
-rw-r--r--FreeFileSync/Source/algorithm.cpp75
-rw-r--r--FreeFileSync/Source/application.cpp59
-rw-r--r--FreeFileSync/Source/comparison.cpp95
-rw-r--r--FreeFileSync/Source/file_hierarchy.cpp64
-rw-r--r--FreeFileSync/Source/fs/abstract.h79
-rw-r--r--FreeFileSync/Source/fs/native.cpp20
-rw-r--r--FreeFileSync/Source/fs/native_traverser_impl.h4
-rw-r--r--FreeFileSync/Source/lib/binary.cpp44
-rw-r--r--FreeFileSync/Source/lib/db_file.cpp12
-rw-r--r--FreeFileSync/Source/lib/dir_exist_async.h7
-rw-r--r--FreeFileSync/Source/lib/dir_lock.cpp26
-rw-r--r--FreeFileSync/Source/lib/error_log.h2
-rw-r--r--FreeFileSync/Source/lib/ffs_paths.cpp33
-rw-r--r--FreeFileSync/Source/lib/ffs_paths.h9
-rw-r--r--FreeFileSync/Source/lib/generate_logfile.h3
-rw-r--r--FreeFileSync/Source/lib/hard_filter.cpp10
-rw-r--r--FreeFileSync/Source/lib/help_provider.h103
-rw-r--r--FreeFileSync/Source/lib/icon_buffer.cpp39
-rw-r--r--FreeFileSync/Source/lib/icon_holder.h28
-rw-r--r--FreeFileSync/Source/lib/icon_loader.cpp3
-rw-r--r--FreeFileSync/Source/lib/localization.cpp8
-rw-r--r--FreeFileSync/Source/lib/parallel_scan.cpp103
-rw-r--r--FreeFileSync/Source/lib/process_xml.cpp20
-rw-r--r--FreeFileSync/Source/lib/process_xml.h3
-rw-r--r--FreeFileSync/Source/lib/resolve_path.cpp3
-rw-r--r--FreeFileSync/Source/lib/status_handler.cpp11
-rw-r--r--FreeFileSync/Source/process_callback.h2
-rw-r--r--FreeFileSync/Source/synchronization.cpp268
-rw-r--r--FreeFileSync/Source/synchronization.h11
-rw-r--r--FreeFileSync/Source/ui/batch_status_handler.cpp141
-rw-r--r--FreeFileSync/Source/ui/batch_status_handler.h19
-rw-r--r--FreeFileSync/Source/ui/column_attr.h30
-rw-r--r--FreeFileSync/Source/ui/custom_grid.cpp56
-rw-r--r--FreeFileSync/Source/ui/folder_selector.cpp4
-rw-r--r--FreeFileSync/Source/ui/folder_selector.h4
-rw-r--r--FreeFileSync/Source/ui/gui_generated.cpp350
-rw-r--r--FreeFileSync/Source/ui/gui_generated.h96
-rw-r--r--FreeFileSync/Source/ui/gui_status_handler.cpp8
-rw-r--r--FreeFileSync/Source/ui/main_dlg.cpp91
-rw-r--r--FreeFileSync/Source/ui/main_dlg.h22
-rw-r--r--FreeFileSync/Source/ui/on_completion_box.cpp29
-rw-r--r--FreeFileSync/Source/ui/progress_indicator.cpp40
-rw-r--r--FreeFileSync/Source/ui/small_dlgs.cpp220
-rw-r--r--FreeFileSync/Source/ui/small_dlgs.h24
-rw-r--r--FreeFileSync/Source/ui/switch_to_gui.h45
-rw-r--r--FreeFileSync/Source/ui/sync_cfg.cpp178
-rw-r--r--FreeFileSync/Source/ui/taskbar.cpp2
-rw-r--r--FreeFileSync/Source/ui/tree_view.cpp84
-rw-r--r--FreeFileSync/Source/ui/version_check.cpp305
-rw-r--r--FreeFileSync/Source/ui/version_check.h18
-rw-r--r--FreeFileSync/Source/ui/version_check_impl.h12
-rw-r--r--FreeFileSync/Source/version/version.h4
-rw-r--r--wx+/async_task.h39
-rw-r--r--wx+/file_drop.h7
-rw-r--r--wx+/grid.cpp356
-rw-r--r--wx+/grid.h28
-rw-r--r--wx+/http.cpp410
-rw-r--r--wx+/http.h27
-rw-r--r--wx+/image_tools.cpp2
-rw-r--r--wx+/popup_dlg.cpp38
-rw-r--r--wx+/popup_dlg.h17
-rw-r--r--wx+/popup_dlg_generated.cpp5
-rw-r--r--wx+/std_button_layout.h7
-rw-r--r--wx+/tooltip.cpp62
-rw-r--r--wx+/tooltip.h4
-rw-r--r--zen/basic_math.h4
-rw-r--r--zen/crc.h13
-rw-r--r--zen/dir_watcher.cpp6
-rw-r--r--zen/file_traverser.cpp10
-rw-r--r--zen/fixed_list.h30
-rw-r--r--zen/format_unit.cpp55
-rw-r--r--zen/i18n.h6
-rw-r--r--zen/perf.h63
-rw-r--r--zen/serialize.h27
-rw-r--r--zen/shell_execute.h4
-rw-r--r--zen/stl_tools.h2
-rw-r--r--zen/string_tools.h52
-rw-r--r--zen/thread.h6
-rw-r--r--zen/tick_count.h141
95 files changed, 2768 insertions, 1863 deletions
diff --git a/FreeFileSync/Build/Changelog.txt b/FreeFileSync/Build/Changelog.txt
index cf12e790..2831c26d 100644
--- a/FreeFileSync/Build/Changelog.txt
+++ b/FreeFileSync/Build/Changelog.txt
@@ -1,3 +1,20 @@
+FreeFileSync 8.7 [2016-12-06]
+-----------------------------
+New auto-updater feature for FreeFileSync Donation Edition
+Download zip archive of portable FreeFileSync Donation Edition
+New command line options to define parameters for silent installation
+Support offline activation for portable Donation Edition
+Use automatic keyboard-interactive SFTP authentication as fallback
+Check for available SFTP authentication methods before login
+Support cloud sync of portable edition installation files
+Access donation transaction details from about dialog
+Use width from flexible grid column when showing/hiding extra columns
+Show item short names in middle column tooltip
+Enhanced file category descriptions with modification times
+Don't warn about missing recycle bin when only moving or updating attributes
+Fixed crash when switching to main dialog during batch sync
+
+
FreeFileSync 8.6 [2016-10-25]
-----------------------------
Added SFTP support for OS X
diff --git a/FreeFileSync/Build/Help/html/exclude-items.html b/FreeFileSync/Build/Help/html/exclude-items.html
index bc60d79a..6e3cf0d9 100644
--- a/FreeFileSync/Build/Help/html/exclude-items.html
+++ b/FreeFileSync/Build/Help/html/exclude-items.html
@@ -72,7 +72,7 @@
<h2>Example: <span style="font-weight:normal">Exclude a sub folder except for certain files</span></h2>
<p>
- Set up two folder pairs with the same source and target paths but with distinct local filters:<br>
+ Set up <b>two folder pairs</b> with the same source and target paths but with distinct local filters:<br>
Folder pair 1; local <em>exclude</em> filter: <span class="file-path">\SubFolder\</span><br>
Folder pair 2; local <em>include</em> filter: <span class="file-path">\SubFolder\*.txt</span>
</p>
diff --git a/FreeFileSync/Build/Languages/arabic.lng b/FreeFileSync/Build/Languages/arabic.lng
index 1d1bb221..1f735dae 100644
--- a/FreeFileSync/Build/Languages/arabic.lng
+++ b/FreeFileSync/Build/Languages/arabic.lng
@@ -115,9 +115,6 @@
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>تحديد مسار مختلف لملف GlobalSettings.xml.</target>
-<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source>
-<target>أي عدد من ملفات خيارات FreeFileSync أو\و _gui and/or .ffs_batch.</target>
-
<source>Any number of alternative directory pairs for at most one config file.</source>
<target>أي عدد من أزواج المسارات البديلة من أجل ملف خيارات واحد.</target>
@@ -278,8 +275,8 @@ Actual: %y bytes
</source>
<target>
حجم غير متوقع لتدفق البيانات.
-المتوقع: %x bytes
-الفعلي: %y bytes
+المتوقع: ‎%x bytes
+الفعلي: ‎%y bytes
</target>
<source>Cannot copy symbolic link %x to %y.</source>
@@ -344,22 +341,22 @@ Actual: %y bytes
<pluralform>%x bytes</pluralform>
</source>
<target>
-<pluralform>0 byte</pluralform>
-<pluralform>1 byte</pluralform>
-<pluralform>2 bytes</pluralform>
-<pluralform>%x bytes</pluralform>
-<pluralform>%x bytes</pluralform>
-<pluralform>%x bytes</pluralform>
+<pluralform>‎0 byte</pluralform>
+<pluralform>‎1 byte</pluralform>
+<pluralform>‎2 bytes</pluralform>
+<pluralform>‎%x bytes</pluralform>
+<pluralform>‎%x bytes</pluralform>
+<pluralform>‎%x bytes</pluralform>
</target>
<source>%x MB</source>
-<target>%x MB</target>
+<target>‎%x MB</target>
<source>%x KB</source>
-<target>%x KB</target>
+<target>‎%x KB</target>
<source>%x GB</source>
-<target>%x GB</target>
+<target>‎%x GB</target>
<source>Cannot load file %x.</source>
<target>لا يمكن فتح الملف %x.</target>
@@ -900,9 +897,6 @@ The command is triggered if:
<source>Find:</source>
<target>بحث:</target>
-<source>Match case</source>
-<target>مطابقة حالة الأحرف (Match case)</target>
-
<source>New</source>
<target>جديد</target>
@@ -1212,7 +1206,7 @@ This guarantees a consistent state even in case of a serious error.
<target>الا&فتراضي</target>
<source>Source code written in C++ using:</source>
-<target>الرماز المصدري مكتوب بلغة C++ باستخدام:</target>
+<target>الرماز المصدري مكتوب بلغة C++‎ باستخدام:</target>
<source>If you like FreeFileSync:</source>
<target>إذا أعجبك FreeFileSync:</target>
@@ -1590,9 +1584,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Parent folder path</source>
<target>مسار المجلد الحاوي</target>
-<source>Temporary local copy for SFTP and MTP storage</source>
-<target>نسخة محلية مؤقتة لتخزين SFTP and MTP</target>
-
<source>Parameters for opposite side</source>
<target>معلمات الجانب المعاكس</target>
@@ -1756,10 +1747,10 @@ This guarantees a consistent state even in case of a serious error.
<target>نوع العنصر %x غير مدعوم:</target>
<source>%x TB</source>
-<target>%x TB</target>
+<target>‎%x TB</target>
<source>%x PB</source>
-<target>%x PB</target>
+<target>‎%x PB</target>
<source>
<pluralform>1 min</pluralform>
diff --git a/FreeFileSync/Build/Languages/english_uk.lng b/FreeFileSync/Build/Languages/english_uk.lng
index 6508fcb0..648076bf 100644
--- a/FreeFileSync/Build/Languages/english_uk.lng
+++ b/FreeFileSync/Build/Languages/english_uk.lng
@@ -1,5 +1,5 @@
<header>
- <language>English (UK)</language>
+ <language>English (UK)‎</language>
<translator>Robert Readman</translator>
<locale>English (U.K.)</locale>
<image>flag_england.png</image>
diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng
index d6cc1e25..a3cff38c 100644
--- a/FreeFileSync/Build/Languages/german.lng
+++ b/FreeFileSync/Build/Languages/german.lng
@@ -7,6 +7,12 @@
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
+<source>Installation was registered on a different operating system.</source>
+<target>Die Installation wurde auf einem anderen Betriebssystem registriert.</target>
+
+<source>The server does not support authentication via %x.</source>
+<target>Der Server unterstützt keine Authentifizierung über %x.</target>
+
<source>Both sides have changed since last synchronization.</source>
<target>Beide Seiten wurden seit der letzten Synchronisation verändert.</target>
@@ -130,14 +136,14 @@
<source>Date:</source>
<target>Datum:</target>
-<source>Files %x have the same date but a different size.</source>
-<target>Die Dateien %x haben dasselbe Datum, aber unterschiedliche Größen.</target>
+<source>Files have the same date but a different size.</source>
+<target>Die Dateien haben dasselbe Datum, aber unterschiedliche Größen.</target>
<source>Size:</source>
<target>Größe:</target>
-<source>Content comparison was skipped for excluded files %x.</source>
-<target>Der Vergleich nach Dateiinhalt wurde für die ausgeschlossenen Dateien %x übersprungen.</target>
+<source>Content comparison was skipped for excluded files.</source>
+<target>Der Vergleich nach Dateiinhalt wurde für die ausgeschlossenen Dateien übersprungen.</target>
<source>Items differ in attributes only</source>
<target>Die Elemente unterscheiden sich nur in Attributen</target>
@@ -279,8 +285,8 @@ Tatsächlich: %y bytes
<source>Cannot open directory %x.</source>
<target>Das Verzeichnis %x kann nicht geöffnet werden.</target>
-<source>Cannot enumerate directory %x.</source>
-<target>Das Verzeichnis %x kann nicht gelistet werden.</target>
+<source>Cannot read directory %x.</source>
+<target>Das Verzeichnis %x kann nicht gelesen werden.</target>
<source>Cannot read file attributes of %x.</source>
<target>Die Dateiattribute von %x können nicht gelesen werden.</target>
@@ -381,7 +387,7 @@ Tatsächlich: %y bytes
<source>Database file %x does not yet exist.</source>
<target>Die Datenbankdatei %x existiert noch nicht.</target>
-<source>Database file is corrupt:</source>
+<source>Database file is corrupted:</source>
<target>Die Datenbankdatei ist beschädigt:</target>
<source>Database files do not share a common session.</source>
@@ -863,11 +869,8 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>&Tools</source>
<target>E&xtras</target>
-<source>&Check for new version</source>
-<target>&Auf neue Version prüfen</target>
-
-<source>&Check now</source>
-<target>&Jetzt prüfen</target>
+<source>&Check for updates now</source>
+<target>&Jetzt auf Aktualisierung prüfen</target>
<source>Check &automatically once a week</source>
<target>&Automatisch wöchentlich prüfen</target>
@@ -1240,6 +1243,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Source code written in C++ using:</source>
<target>Der Quellcode wurde in C++ geschrieben mit:</target>
+<source>Donation details</source>
+<target>Spendendetails</target>
+
<source>If you like FreeFileSync:</source>
<target>Wenn Sie FreeFileSync mögen:</target>
@@ -1249,7 +1255,7 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Feedback and suggestions are welcome</source>
<target>Feedback und Vorschläge sind willkommen</target>
-<source>Homepage</source>
+<source>Home page</source>
<target>Homepage</target>
<source>Email</source>
@@ -1261,6 +1267,27 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Many thanks for localization:</source>
<target>Vielen Dank für die Lokalisation:</target>
+<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
+<target>Aktivieren Sie die FreeFileSync Spendenversion durch eine der folgenden Methoden:</target>
+
+<source>1. Activate via internet now:</source>
+<target>1. Jetzt über das Internet aktivieren:</target>
+
+<source>Activate online</source>
+<target>Online aktivieren</target>
+
+<source>2. Retrieve an offline activation key from the following URL:</source>
+<target>2. Einen Offline-Aktivierungsschlüssel von folgender URL abrufen:</target>
+
+<source>&Copy to clipboard</source>
+<target>In &Zwischenablage kopieren</target>
+
+<source>Enter activation key:</source>
+<target>Aktivierungsschlüssel eingeben:</target>
+
+<source>Activate offline</source>
+<target>Offline aktivieren</target>
+
<source>SSH File Transfer Protocol</source>
<target>SSH File Transfer Protocol</target>
@@ -1279,6 +1306,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Select Time Span</source>
<target>Zeitspanne auswählen</target>
+<source>FreeFileSync Donation Edition</source>
+<target>FreeFileSync Spendenversion</target>
+
<source>&Options</source>
<target>&Optionen</target>
@@ -1300,8 +1330,8 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Overview</source>
<target>Übersicht</target>
-<source>&Download</source>
-<target>Zum &Download</target>
+<source>&Show details</source>
+<target>&Zeige Details</target>
<source>A new version of FreeFileSync is available:</source>
<target>Eine neue Version von FreeFileSync ist verfügbar:</target>
@@ -1543,6 +1573,12 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Loading...</source>
<target>Lade...</target>
+<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
+<target>Die Installationsdateien sind beschädigt. Bitte installieren Sie FreeFileSync neu.</target>
+
+<source>Thank you, %x, for your donation and support!</source>
+<target>Danke %x für die Spende und Unterstützung!</target>
+
<source>Password:</source>
<target>Passwort:</target>
@@ -1606,6 +1642,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>&Show</source>
<target>&Zeigen</target>
+<source>Downloading update...</source>
+<target>Lade Aktualisierung...</target>
+
<source>Identify equal files by comparing modification time and size.</source>
<target>Erkenne gleiche Dateien anhand der Änderungszeit und Größe.</target>
@@ -1699,12 +1738,33 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Percentage</source>
<target>Prozent</target>
+<source>Failed to retrieve update information.</source>
+<target>Aktualisierungsinformationen konnten nicht abgerufen werden.</target>
+
+<source>Automatic updates:</source>
+<target>Automatische Aktualisierung:</target>
+
+<source>Requires FreeFileSync Donation Edition</source>
+<target>Benötigt FreeFileSync Spendenversion</target>
+
<source>Check for Program Updates</source>
<target>Suche nach neuer Programmversion</target>
+<source>Auto-update now or download manually from the FreeFileSync home page?</source>
+<target>Jetzt automatisch aktualisieren oder manuell von der FreeFileSync Homepage herunterladen?</target>
+
+<source>&Auto-update</source>
+<target>&Automatisch</target>
+
+<source>&Home page</source>
+<target>&Homepage</target>
+
<source>Download now?</source>
<target>Jetzt herunterladen?</target>
+<source>&Download</source>
+<target>Zum &Download</target>
+
<source>FreeFileSync is up to date.</source>
<target>FreeFileSync ist auf dem neuesten Stand.</target>
@@ -1717,14 +1777,17 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Consistency check failed for %x.</source>
<target>Die Konsistenzprüfung für %x ist fehlgeschlagen.</target>
-<source>Cannot find system function %x.</source>
-<target>Die Systemfunktion %x wurde nicht gefunden.</target>
+<source>Failed to activate FreeFileSync Donation Edition.</source>
+<target>Die Aktivierung der FreeFileSync Spendenversion ist fehlgeschlagen.</target>
+
+<source>Incorrect activation key.</source>
+<target>Falscher Aktivierungsschlüssel.</target>
<source>Unable to register to receive system messages.</source>
<target>Die Registrierung zum Empfang von Systemmeldungen ist fehlgeschlagen.</target>
-<source>Installation files are corrupt. Please reinstall FreeFileSync.</source>
-<target>Die Installationsdateien sind beschädigt. Bitte installieren Sie FreeFileSync neu.</target>
+<source>Cannot find system function %x.</source>
+<target>Die Systemfunktion %x wurde nicht gefunden.</target>
<source>Unable to register device notifications for %x.</source>
<target>Die Registrierung für Gerätemeldungen ist für %x fehlgeschlagen.</target>
@@ -1882,15 +1945,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Edit with FreeFileSync</source>
<target>Mit FreeFileSync editieren</target>
-<source>Thank you, %x, for your donation and support!</source>
-<target>Danke %x für die Spende und Unterstützung!</target>
-
-<source>This FreeFileSync installer for donors has reached its daily installation limit.</source>
-<target>Das FreeFileSync Installationsprogramm für Spender hat sein tägliches Installationslimit erreicht.</target>
-
-<source>Download the regular version from the FreeFileSync homepage now?</source>
-<target>Jetzt die normale Version von der FreeFileSync Homepage herunterladen?</target>
-
<source>The portable version cannot install into the selected folder.</source>
<target>Die portable Version kann nicht in den gewählten Ordner installiert werden.</target>
diff --git a/FreeFileSync/Build/Languages/hebrew.lng b/FreeFileSync/Build/Languages/hebrew.lng
index 3fdd3083..48fc065d 100644
--- a/FreeFileSync/Build/Languages/hebrew.lng
+++ b/FreeFileSync/Build/Languages/hebrew.lng
@@ -1184,7 +1184,7 @@ This guarantees a consistent state even in case of a serious error.
<target>&ברירת מחדל</target>
<source>Source code written in C++ using:</source>
-<target>קוד מקור נכתב ב- C++ באמצעות:</target>
+<target>קוד מקור נכתב ב- C++‎ באמצעות:</target>
<source>If you like FreeFileSync:</source>
<target>במידה ו-FreeFileSync מוצאת חן בעינכם:</target>
diff --git a/FreeFileSync/Build/Languages/portuguese_br.lng b/FreeFileSync/Build/Languages/portuguese_br.lng
index b155f3a9..8a420222 100644
--- a/FreeFileSync/Build/Languages/portuguese_br.lng
+++ b/FreeFileSync/Build/Languages/portuguese_br.lng
@@ -1,5 +1,5 @@
<header>
- <language>Português (BR)</language>
+ <language>Português (BR)‎</language>
<translator>Edison Aranha</translator>
<locale>pt_BR</locale>
<image>flag_brazil.png</image>
diff --git a/FreeFileSync/Build/Languages/russian.lng b/FreeFileSync/Build/Languages/russian.lng
index 620d79bd..3933e8bb 100644
--- a/FreeFileSync/Build/Languages/russian.lng
+++ b/FreeFileSync/Build/Languages/russian.lng
@@ -796,7 +796,7 @@ The command is triggered if:
<target>Выберите папку SFTP</target>
<source>Select alternative folder type</source>
-<target>Выберете альтернативный тип папки</target>
+<target>Выберите альтернативный тип папки</target>
<source>&New</source>
<target>&Новая</target>
@@ -895,7 +895,7 @@ The command is triggered if:
<target>Тип просмотра:</target>
<source>Select view:</source>
-<target>Выберете отображение:</target>
+<target>Выберите отображение:</target>
<source>Statistics:</source>
<target>Статистика:</target>
@@ -1062,10 +1062,10 @@ The command is triggered if:
<target>Папка на сервере:</target>
<source>Select a directory on the server:</source>
-<target>Выберете папку на сервере:</target>
+<target>Выберите папку на сервере:</target>
<source>Select Folder</source>
-<target>Выберете папку</target>
+<target>Выберите папку</target>
<source>Start synchronization now?</source>
<target>Начать синхронизацию сейчас?</target>
@@ -1098,7 +1098,7 @@ The command is triggered if:
<target>Свернуть в область уведомлений</target>
<source>Bytes copied:</source>
-<target>Данных скопированно:</target>
+<target>Данных скопировано:</target>
<source>Close</source>
<target>Закрыть</target>
diff --git a/FreeFileSync/Build/Languages/spanish.lng b/FreeFileSync/Build/Languages/spanish.lng
index 62240f19..aafbacec 100644
--- a/FreeFileSync/Build/Languages/spanish.lng
+++ b/FreeFileSync/Build/Languages/spanish.lng
@@ -1,6 +1,6 @@
<header>
<language>Español</language>
- <translator>I.R.Maturana (irmlab.com)</translator>
+ <translator>I.R.Maturana (irmlab.com)‎</translator>
<locale>es_ES</locale>
<image>flag_spain.png</image>
<plural_count>2</plural_count>
diff --git a/FreeFileSync/Build/Resources.zip b/FreeFileSync/Build/Resources.zip
index 9acc3a10..85b1967d 100644
--- a/FreeFileSync/Build/Resources.zip
+++ b/FreeFileSync/Build/Resources.zip
Binary files differ
diff --git a/FreeFileSync/Source/RealTimeSync/application.cpp b/FreeFileSync/Source/RealTimeSync/application.cpp
index 735e0b8b..cc127249 100644
--- a/FreeFileSync/Source/RealTimeSync/application.cpp
+++ b/FreeFileSync/Source/RealTimeSync/application.cpp
@@ -65,7 +65,7 @@ bool Application::OnInit()
wxToolTip::SetMaxWidth(-1); //disable tooltip wrapping -> Windows only
#elif defined ZEN_LINUX
- ::gtk_rc_parse((zen::getResourceDir() + "styles.gtk_rc").c_str()); //remove inner border from bitmap buttons
+ ::gtk_rc_parse((zen::getResourceDirPf() + "styles.gtk_rc").c_str()); //remove inner border from bitmap buttons
#endif
//Windows User Experience Interaction Guidelines: tool tips should have 5s timeout, info tips no timeout => compromise:
@@ -74,7 +74,18 @@ bool Application::OnInit()
SetAppName(L"RealTimeSync");
- initResourceImages(getResourceDir() + Zstr("Resources.zip"));
+ initResourceImages(getResourceDirPf() + Zstr("Resources.zip"));
+
+ try
+ {
+ setLanguage(xmlAccess::getProgramLanguage()); //throw FileError
+ }
+ catch (const FileError& e)
+ {
+ warn_static("Bug? (exit on frame delete)")
+ showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString()));
+ //continue!
+ }
Connect(wxEVT_QUERY_END_SESSION, wxEventHandler(Application::onQueryEndSession), nullptr, this);
Connect(wxEVT_END_SESSION, wxEventHandler(Application::onQueryEndSession), nullptr, this);
@@ -85,7 +96,6 @@ bool Application::OnInit()
Connect(EVENT_ENTER_EVENT_LOOP, wxEventHandler(Application::onEnterEventLoop), nullptr, this);
wxCommandEvent scrollEvent(EVENT_ENTER_EVENT_LOOP);
AddPendingEvent(scrollEvent);
-
return true; //true: continue processing; false: exit immediately.
}
@@ -103,17 +113,6 @@ void Application::onEnterEventLoop(wxEvent& event)
{
Disconnect(EVENT_ENTER_EVENT_LOOP, wxEventHandler(Application::onEnterEventLoop), nullptr, this);
- try
- {
- wxLanguage lngId = xmlAccess::getProgramLanguage();
- setLanguage(lngId); //throw FileError
- }
- catch (const FileError& e)
- {
- showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString()));
- //continue!
- }
-
//try to set config/batch- filepath set by %1 parameter
std::vector<Zstring> commandArgs;
for (int i = 1; i < argc; ++i)
diff --git a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
index af22b7a0..4bb61add 100644
--- a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
+++ b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
@@ -73,7 +73,7 @@ void MainDialog::create(const Zstring& cfgFile)
MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName)
: MainDlgGenerated(dlg),
- lastRunConfigPath(zen::getConfigDir() + Zstr("LastRun.ffs_real"))
+ lastRunConfigPath_(zen::getConfigDirPathPf() + Zstr("LastRun.ffs_real"))
{
#ifdef ZEN_WIN
new MouseMoveWindow(*this); //ownership passed to "this"
@@ -100,9 +100,9 @@ MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName)
//--------------------------- load config values ------------------------------------
xmlAccess::XmlRealConfig newConfig;
- const Zstring currentConfigFile = cfgFileName.empty() ? lastRunConfigPath : cfgFileName;
+ const Zstring currentConfigFile = cfgFileName.empty() ? lastRunConfigPath_ : cfgFileName;
bool loadCfgSuccess = false;
- if (!cfgFileName.empty() || fileExists(lastRunConfigPath))
+ if (!cfgFileName.empty() || fileExists(lastRunConfigPath_))
try
{
std::wstring warningMsg;
@@ -161,7 +161,7 @@ MainDialog::~MainDialog()
try //write config to XML
{
- writeConfig(currentCfg, lastRunConfigPath); //throw FileError
+ writeConfig(currentCfg, lastRunConfigPath_); //throw FileError
}
catch (const FileError& e)
{
@@ -172,7 +172,7 @@ MainDialog::~MainDialog()
void MainDialog::onQueryEndSession()
{
- try { writeConfig(getConfiguration(), lastRunConfigPath); } //throw FileError
+ try { writeConfig(getConfiguration(), lastRunConfigPath_); } //throw FileError
catch (const FileError&) {} //we try our best do to something useful in this extreme situation - no reason to notify or even log errors here!
}
@@ -228,7 +228,7 @@ void MainDialog::OnStart(wxCommandEvent& event)
xmlAccess::XmlRealConfig currentCfg = getConfiguration();
- switch (rts::startDirectoryMonitor(currentCfg, xmlAccess::extractJobName(utfCvrtTo<Zstring>(currentConfigFileName))))
+ switch (rts::startDirectoryMonitor(currentCfg, xmlAccess::extractJobName(utfCvrtTo<Zstring>(currentConfigFileName_))))
{
case rts::EXIT_APP:
Close();
@@ -249,7 +249,7 @@ void MainDialog::OnStart(wxCommandEvent& event)
void MainDialog::OnConfigSave(wxCommandEvent& event)
{
- Zstring defaultFileName = currentConfigFileName.empty() ? Zstr("Realtime.ffs_real") : currentConfigFileName;
+ Zstring defaultFileName = currentConfigFileName_.empty() ? Zstr("Realtime.ffs_real") : currentConfigFileName_;
//attention: currentConfigFileName may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config!
if (pathEndsWith(defaultFileName, Zstr(".ffs_batch")))
defaultFileName = beforeLast(defaultFileName, Zstr("."), IF_MISSING_RETURN_NONE) + Zstr(".ffs_real");
@@ -306,15 +306,15 @@ void MainDialog::loadConfig(const Zstring& filepath)
void MainDialog::setLastUsedConfig(const Zstring& filepath)
{
//set title
- if (filepath == lastRunConfigPath)
+ if (filepath == lastRunConfigPath_)
{
SetTitle(L"RealTimeSync - " + _("Automated Synchronization"));
- currentConfigFileName.clear();
+ currentConfigFileName_.clear();
}
else
{
SetTitle(utfCvrtTo<wxString>(filepath));
- currentConfigFileName = filepath;
+ currentConfigFileName_ = filepath;
}
}
@@ -323,7 +323,7 @@ void MainDialog::OnConfigLoad(wxCommandEvent& event)
{
wxFileDialog filePicker(this,
wxString(),
- utfCvrtTo<wxString>(beforeLast(currentConfigFileName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir
+ utfCvrtTo<wxString>(beforeLast(currentConfigFileName_, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir
wxString(),
wxString(L"RealTimeSync (*.ffs_real; *.ffs_batch)|*.ffs_real;*.ffs_batch") + L"|" +_("All files") + L" (*.*)|*",
wxFD_OPEN);
@@ -490,7 +490,7 @@ void MainDialog::removeAddFolder(size_t pos)
//the deferred deletion it is expected to do (and which is implemented correctly on Windows and Linux)
//http://bb10.com/python-wxpython-devel/2012-09/msg00004.html
//=> since we're in a mouse button callback of a sub-component of "pairToDelete" we need to delay deletion ourselves:
- guiQueue.processAsync([] {}, [pairToDelete] { pairToDelete->Destroy(); });
+ guiQueue_.processAsync([] {}, [pairToDelete] { pairToDelete->Destroy(); });
//set size of scrolled window
const size_t additionalRows = std::min(dirpathsExtra.size(), MAX_ADD_FOLDERS); //up to MAX_ADD_FOLDERS additional folders shall be shown
diff --git a/FreeFileSync/Source/RealTimeSync/main_dlg.h b/FreeFileSync/Source/RealTimeSync/main_dlg.h
index f540bd5d..cee7574c 100644
--- a/FreeFileSync/Source/RealTimeSync/main_dlg.h
+++ b/FreeFileSync/Source/RealTimeSync/main_dlg.h
@@ -62,10 +62,10 @@ private:
std::vector<DirectoryPanel*> dirpathsExtra; //additional pairs to the standard pair
- const Zstring lastRunConfigPath;
- Zstring currentConfigFileName;
+ const Zstring lastRunConfigPath_;
+ Zstring currentConfigFileName_;
- zen::AsyncGuiQueue guiQueue; //schedule and run long-running tasks asynchronously, but process results on GUI queue
+ 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 c07c137c..a7902eb6 100644
--- a/FreeFileSync/Source/RealTimeSync/monitor.cpp
+++ b/FreeFileSync/Source/RealTimeSync/monitor.cpp
@@ -11,6 +11,7 @@
#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"
//#include "../library/db_file.h" //SYNC_DB_FILE_ENDING -> complete file too much of a dependency; file ending too little to decouple into single header
@@ -22,7 +23,7 @@ using namespace zen;
namespace
{
-const int CHECK_FOLDER_INTERVAL = 1; //unit: [s]
+const int FOLDER_EXISTENCE_CHECK_INTERVAL_SEC = 1; //unit: [s]
std::vector<Zstring> getFormattedDirs(const std::vector<Zstring>& folderPathPhrases) //throw FileError
@@ -78,7 +79,7 @@ WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw
//a non-existent network path may block, so check existence asynchronously!
auto ftDirExists = runAsync([=] { return zen::dirExists(folderPathFmt); });
//we need to check dirExists(), not somethingExists(): it's not clear if DirWatcher detects a type clash (file instead of directory!)
- while (ftDirExists.wait_for(std::chrono::milliseconds(rts::UI_UPDATE_INTERVAL / 2)) != std::future_status::ready)
+ while (ftDirExists.wait_for(std::chrono::milliseconds(rts::UI_UPDATE_INTERVAL_MS / 2)) != std::future_status::ready)
onRefreshGui(false /*readyForSync*/); //may throw!
if (!ftDirExists.get())
return WaitResult(folderPathFmt);
@@ -93,15 +94,16 @@ WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw
}
}
- auto lastCheck = std::chrono::steady_clock::now();
+ auto lastCheckTime = std::chrono::steady_clock::now();
for (;;)
{
const bool checkDirExistNow = [&]() -> bool //checking once per sec should suffice
{
const auto now = std::chrono::steady_clock::now();
- if (now >= lastCheck + std::chrono::seconds(CHECK_FOLDER_INTERVAL))
+
+ if (numeric::dist(now, lastCheckTime) > std::chrono::seconds(FOLDER_EXISTENCE_CHECK_INTERVAL_SEC)) //handle potential chrono wrap-around!
{
- lastCheck = now;
+ lastCheckTime = now;
return true;
}
return false;
@@ -145,7 +147,7 @@ WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw
}
}
- std::this_thread::sleep_for(std::chrono::milliseconds(rts::UI_UPDATE_INTERVAL / 2));
+ 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
}
}
@@ -174,16 +176,16 @@ void waitForMissingDirs(const std::vector<Zstring>& folderPathPhrases, //throw F
//2. check dir existence
return zen::dirExists(folderPathFmt);
});
- while (ftDirExisting.wait_for(std::chrono::milliseconds(rts::UI_UPDATE_INTERVAL / 2)) != std::future_status::ready)
+ while (ftDirExisting.wait_for(std::chrono::milliseconds(rts::UI_UPDATE_INTERVAL_MS / 2)) != std::future_status::ready)
onRefreshGui(folderPathFmt); //may throw!
if (!ftDirExisting.get())
{
allExisting = false;
//wait some time...
- const int refreshInterval = rts::UI_UPDATE_INTERVAL / 2;
- static_assert(CHECK_FOLDER_INTERVAL * 1000 % refreshInterval == 0, "");
- for (int i = 0; i < CHECK_FOLDER_INTERVAL * 1000 / refreshInterval; ++i)
+ 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)
{
onRefreshGui(folderPathFmt); //may throw!
std::this_thread::sleep_for(std::chrono::milliseconds(refreshInterval));
diff --git a/FreeFileSync/Source/RealTimeSync/monitor.h b/FreeFileSync/Source/RealTimeSync/monitor.h
index 4d3538fd..8fb7ae3f 100644
--- a/FreeFileSync/Source/RealTimeSync/monitor.h
+++ b/FreeFileSync/Source/RealTimeSync/monitor.h
@@ -13,7 +13,7 @@
namespace rts
{
-const int UI_UPDATE_INTERVAL = 100; //unit: [ms]; perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss
+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
diff --git a/FreeFileSync/Source/RealTimeSync/tray_menu.cpp b/FreeFileSync/Source/RealTimeSync/tray_menu.cpp
index c421ff72..83da8f86 100644
--- a/FreeFileSync/Source/RealTimeSync/tray_menu.cpp
+++ b/FreeFileSync/Source/RealTimeSync/tray_menu.cpp
@@ -5,8 +5,8 @@
// *****************************************************************************
#include "tray_menu.h"
+#include <chrono>
#include <zen/thread.h>
-#include <zen/tick_count.h>
#include <wx/taskbar.h>
#include <wx/icon.h> //Linux needs this
#include <wx/app.h>
@@ -25,15 +25,16 @@ using namespace zen;
namespace
{
-const int RETRY_AFTER_ERROR_INTERVAL = 15; //unit: [s]
+const int RETRY_AFTER_ERROR_INTERVAL_SEC = 15; //unit: [s]
+
+std::chrono::steady_clock::time_point lastExec;
-const std::int64_t TICKS_UPDATE_INTERVAL = rts::UI_UPDATE_INTERVAL* ticksPerSec() / 1000;
-TickVal lastExec = getTicks();
bool updateUiIsAllowed()
{
- const TickVal now = getTicks(); //0 on error
- if (dist(lastExec, now) >= TICKS_UPDATE_INTERVAL) //perform ui updates not more often than necessary
+ 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!
{
lastExec = now;
return true;
@@ -307,8 +308,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 * 1000 % UI_UPDATE_INTERVAL == 0, "");
- for (int i = 0; i < RETRY_AFTER_ERROR_INTERVAL * 1000 / UI_UPDATE_INTERVAL; ++i)
+ 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)
{
trayIcon.doUiRefreshNow(); //throw AbortMonitoring
@@ -321,7 +322,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));
+ std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS));
}
}
diff --git a/FreeFileSync/Source/algorithm.cpp b/FreeFileSync/Source/algorithm.cpp
index 5dba0989..1def34af 100644
--- a/FreeFileSync/Source/algorithm.cpp
+++ b/FreeFileSync/Source/algorithm.cpp
@@ -329,14 +329,14 @@ public:
private:
DetectMovedFiles(BaseFolderPair& baseFolder, const InSyncFolder& dbFolder) :
- cmpVar (baseFolder.getCompVariant()),
- fileTimeTolerance(baseFolder.getFileTimeTolerance()),
- ignoreTimeShiftMinutes(baseFolder.getIgnoredTimeShift())
+ cmpVar_ (baseFolder.getCompVariant()),
+ fileTimeTolerance_(baseFolder.getFileTimeTolerance()),
+ ignoreTimeShiftMinutes_(baseFolder.getIgnoredTimeShift())
{
recurse(baseFolder, &dbFolder);
- if ((!exLeftOnlyById .empty() || !exLeftOnlyByPath .empty()) &&
- (!exRightOnlyById.empty() || !exRightOnlyByPath.empty()))
+ if ((!exLeftOnlyById_ .empty() || !exLeftOnlyByPath_ .empty()) &&
+ (!exRightOnlyById_.empty() || !exRightOnlyByPath_.empty()))
detectMovePairs(dbFolder);
}
@@ -360,10 +360,10 @@ private:
if (cat == FILE_LEFT_SIDE_ONLY)
{
if (const InSyncFile* dbFile = getDbFileEntry())
- exLeftOnlyByPath.emplace(dbFile, &file);
+ exLeftOnlyByPath_.emplace(dbFile, &file);
else if (!file.getFileId<LEFT_SIDE>().empty())
{
- auto rv = exLeftOnlyById.emplace(file.getFileId<LEFT_SIDE>(), &file);
+ auto rv = exLeftOnlyById_.emplace(file.getFileId<LEFT_SIDE>(), &file);
if (!rv.second) //duplicate file ID! NTFS hard link/symlink?
rv.first->second = nullptr;
}
@@ -371,10 +371,10 @@ private:
else if (cat == FILE_RIGHT_SIDE_ONLY)
{
if (const InSyncFile* dbFile = getDbFileEntry())
- exRightOnlyByPath.emplace(dbFile, &file);
+ exRightOnlyByPath_.emplace(dbFile, &file);
else if (!file.getFileId<RIGHT_SIDE>().empty())
{
- auto rv = exRightOnlyById.emplace(file.getFileId<RIGHT_SIDE>(), &file);
+ auto rv = exRightOnlyById_.emplace(file.getFileId<RIGHT_SIDE>(), &file);
if (!rv.second) //duplicate file ID! NTFS hard link/symlink?
rv.first->second = nullptr;
}
@@ -443,10 +443,10 @@ private:
void findAndSetMovePair(const InSyncFile& dbFile) const
{
- if (stillInSync(dbFile, cmpVar, fileTimeTolerance, ignoreTimeShiftMinutes))
- if (FilePair* fileLeftOnly = getAssocFilePair<LEFT_SIDE>(dbFile, exLeftOnlyById, exLeftOnlyByPath))
+ if (stillInSync(dbFile, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_))
+ if (FilePair* fileLeftOnly = getAssocFilePair<LEFT_SIDE>(dbFile, exLeftOnlyById_, exLeftOnlyByPath_))
if (sameSizeAndDate<LEFT_SIDE>(*fileLeftOnly, dbFile))
- if (FilePair* fileRightOnly = getAssocFilePair<RIGHT_SIDE>(dbFile, exRightOnlyById, exRightOnlyByPath))
+ if (FilePair* fileRightOnly = getAssocFilePair<RIGHT_SIDE>(dbFile, exRightOnlyById_, exRightOnlyByPath_))
if (sameSizeAndDate<RIGHT_SIDE>(*fileRightOnly, dbFile))
if (fileLeftOnly ->getMoveRef() == nullptr && //don't let a row participate in two move pairs!
fileRightOnly->getMoveRef() == nullptr) //
@@ -456,16 +456,16 @@ private:
}
}
- const CompareVariant cmpVar;
- const int fileTimeTolerance;
- const std::vector<unsigned int> ignoreTimeShiftMinutes;
+ const CompareVariant cmpVar_;
+ const int fileTimeTolerance_;
+ const std::vector<unsigned int> ignoreTimeShiftMinutes_;
- std::unordered_map<AFS::FileId, FilePair*, StringHash> exLeftOnlyById; //FilePair* == nullptr for duplicate ids! => consider aliasing through symlinks!
- std::unordered_map<AFS::FileId, FilePair*, StringHash> exRightOnlyById; //=> avoid ambiguity for mixtures of files/symlinks on one side and allow 1-1 mapping only!
+ std::unordered_map<AFS::FileId, FilePair*, StringHash> exLeftOnlyById_; //FilePair* == nullptr for duplicate ids! => consider aliasing through symlinks!
+ std::unordered_map<AFS::FileId, FilePair*, StringHash> exRightOnlyById_; //=> avoid ambiguity for mixtures of files/symlinks on one side and allow 1-1 mapping only!
//MSVC: std::unordered_map: about twice as fast as std::map for 1 million items!
- std::unordered_map<const InSyncFile*, FilePair*> exLeftOnlyByPath; //MSVC: only 4% faster than std::map for 1 million items!
- std::unordered_map<const InSyncFile*, FilePair*> exRightOnlyByPath;
+ std::unordered_map<const InSyncFile*, FilePair*> exLeftOnlyByPath_; //MSVC: only 4% faster than std::map for 1 million items!
+ std::unordered_map<const InSyncFile*, FilePair*> exRightOnlyByPath_;
/*
detect renamed files:
@@ -502,12 +502,9 @@ public:
private:
RedetermineTwoWay(BaseFolderPair& baseFolder, const InSyncFolder& dbFolder) :
- txtBothSidesChanged(_("Both sides have changed since last synchronization.")),
- txtNoSideChanged(_("Cannot determine sync-direction:") + L" \n" + _("No change since last synchronization.")),
- txtDbNotInSync(_("Cannot determine sync-direction:") + L" \n" + _("The database entry is not in sync considering current settings.")),
- cmpVar (baseFolder.getCompVariant()),
- fileTimeTolerance (baseFolder.getFileTimeTolerance()),
- ignoreTimeShiftMinutes(baseFolder.getIgnoredTimeShift())
+ cmpVar_ (baseFolder.getCompVariant()),
+ fileTimeTolerance_ (baseFolder.getFileTimeTolerance()),
+ ignoreTimeShiftMinutes_(baseFolder.getIgnoredTimeShift())
{
//-> considering filter not relevant:
//if narrowing filter: all ok; if widening filter (if file ex on both sides -> conflict, fine; if file ex. on one side: copy to other side: fine)
@@ -548,13 +545,13 @@ private:
}
//evaluation
- const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(file, dbEntry, ignoreTimeShiftMinutes);
- const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(file, dbEntry, ignoreTimeShiftMinutes);
+ const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(file, dbEntry, ignoreTimeShiftMinutes_);
+ const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(file, dbEntry, ignoreTimeShiftMinutes_);
if (changeOnLeft != changeOnRight)
{
//if database entry not in sync according to current settings! -> set direction based on sync status only!
- if (dbEntry && !stillInSync(dbEntry->second, cmpVar, fileTimeTolerance, ignoreTimeShiftMinutes))
+ if (dbEntry && !stillInSync(dbEntry->second, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_))
file.setSyncDirConflict(txtDbNotInSync);
else
file.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT);
@@ -584,13 +581,13 @@ private:
}
//evaluation
- const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(symlink, dbEntry, ignoreTimeShiftMinutes);
- const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(symlink, dbEntry, ignoreTimeShiftMinutes);
+ const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(symlink, dbEntry, ignoreTimeShiftMinutes_);
+ const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(symlink, dbEntry, ignoreTimeShiftMinutes_);
if (changeOnLeft != changeOnRight)
{
//if database entry not in sync according to current settings! -> set direction based on sync status only!
- if (dbEntry && !stillInSync(dbEntry->second, cmpVar, fileTimeTolerance, ignoreTimeShiftMinutes))
+ if (dbEntry && !stillInSync(dbEntry->second, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_))
symlink.setSyncDirConflict(txtDbNotInSync);
else
symlink.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT);
@@ -650,13 +647,13 @@ private:
recurse(folder, dbEntry ? &dbEntry->second : nullptr);
}
- const std::wstring txtBothSidesChanged;
- const std::wstring txtNoSideChanged;
- const std::wstring txtDbNotInSync;
+ const std::wstring txtBothSidesChanged = _("Both sides have changed since last synchronization.");
+ const std::wstring txtNoSideChanged = _("Cannot determine sync-direction:") + L" \n" + _("No change since last synchronization.");
+ const std::wstring txtDbNotInSync = _("Cannot determine sync-direction:") + L" \n" + _("The database entry is not in sync considering current settings.");
- const CompareVariant cmpVar;
- const int fileTimeTolerance;
- const std::vector<unsigned int> ignoreTimeShiftMinutes;
+ const CompareVariant cmpVar_;
+ const int fileTimeTolerance_;
+ const std::vector<unsigned int> ignoreTimeShiftMinutes_;
};
}
@@ -1572,7 +1569,7 @@ void TempFileBuffer::createTempFiles(const std::set<FileDetails>& workLoad, Proc
tempPathTmp += Zstr("FFS-");
const std::string guid = generateGUID(); //no need for full-blown (pseudo-)random numbers for this one-time invocation
- const uint32_t crc32 = getCrc32(guid.begin(), guid.end());
+ const uint32_t crc32 = getCrc32(guid);
tempPathTmp += printNumber<Zstring>(Zstr("%08x"), static_cast<unsigned int>(crc32));
makeDirectoryRecursively(tempPathTmp); //throw FileError
@@ -1593,7 +1590,7 @@ void TempFileBuffer::createTempFiles(const std::set<FileDetails>& workLoad, Proc
writeNumber (cookie, details.descr.isFollowedSymlink);
writeContainer(cookie, AFS::getInitPathPhrase(details.path));
- const uint16_t crc16 = getCrc16(cookie.ref().begin(), cookie.ref().end());
+ const uint16_t crc16 = getCrc16(cookie.ref());
const Zstring detailsHash = printNumber<Zstring>(Zstr("%04x"), static_cast<unsigned int>(crc16));
const Zstring fileName = AFS::getFileShortName(details.path);
diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp
index 1a86d915..15aa04a0 100644
--- a/FreeFileSync/Source/application.cpp
+++ b/FreeFileSync/Source/application.cpp
@@ -18,17 +18,15 @@
#include "synchronization.h"
#include "ui/batch_status_handler.h"
#include "ui/main_dlg.h"
-#include "ui/switch_to_gui.h"
#include "lib/help_provider.h"
#include "lib/process_xml.h"
#include "lib/error_log.h"
#include "lib/resolve_path.h"
#ifdef ZEN_WIN
- #include <zen/win_ver.h>
- //#include <zen/dll.h>
- #include "lib/app_user_mode_id.h"
#include <zen/service_notification.h>
+ #include "lib/app_user_mode_id.h"
+ #include "ui/version_check.h"
#elif defined ZEN_LINUX
#include <gtk/gtk.h>
@@ -67,21 +65,21 @@ std::vector<Zstring> getCommandlineArgs(const wxApp& app)
while (endsWith(cmdLine, L' ')) //may end with space
cmdLine.pop_back();
- auto iterStart = cmdLine.end(); //end() means: no token
+ auto itStart = cmdLine.end(); //end() means: no token
for (auto it = cmdLine.begin(); it != cmdLine.end(); ++it)
if (*it == L' ') //space commits token
{
- if (iterStart != cmdLine.end())
+ if (itStart != cmdLine.end())
{
- args.emplace_back(iterStart, it);
- iterStart = cmdLine.end(); //expect consecutive blanks!
+ args.emplace_back(itStart, it);
+ itStart = cmdLine.end(); //expect consecutive blanks!
}
}
else
{
//start new token
- if (iterStart == cmdLine.end())
- iterStart = it;
+ if (itStart == cmdLine.end())
+ itStart = it;
if (*it == L'\"')
{
@@ -90,8 +88,8 @@ std::vector<Zstring> getCommandlineArgs(const wxApp& app)
break;
}
}
- if (iterStart != cmdLine.end())
- args.emplace_back(iterStart, cmdLine.end());
+ if (itStart != cmdLine.end())
+ args.emplace_back(itStart, cmdLine.end());
if (!args.empty())
args.erase(args.begin()); //remove first argument which is exe path by convention: https://blogs.msdn.microsoft.com/oldnewthing/20060515-07/?p=31203
@@ -131,7 +129,7 @@ bool Application::OnInit()
#elif defined ZEN_LINUX
::gtk_init(nullptr, nullptr);
- //::gtk_rc_parse((getResourceDir() + "styles.gtk_rc").c_str()); //remove inner border from bitmap buttons
+ //::gtk_rc_parse((getResourceDirPf() + "styles.gtk_rc").c_str()); //remove inner border from bitmap buttons
#endif
//Windows User Experience Interaction Guidelines: tool tips should have 5s timeout, info tips no timeout => compromise:
@@ -140,17 +138,27 @@ bool Application::OnInit()
SetAppName(L"FreeFileSync"); //if not set, the default is the executable's name!
- initResourceImages(getResourceDir() + Zstr("Resources.zip"));
+ initResourceImages(getResourceDirPf() + Zstr("Resources.zip"));
+
+ try
+ {
+ //tentatively set program language to OS default until GlobalSettings.xml is read later
+ setLanguage(xmlAccess::XmlGlobalSettings().programLanguage); //throw FileError
+ }
+ catch (const FileError&) { assert(false); }
Connect(wxEVT_QUERY_END_SESSION, wxEventHandler(Application::onQueryEndSession), nullptr, this);
Connect(wxEVT_END_SESSION, wxEventHandler(Application::onQueryEndSession), nullptr, this);
+#ifdef ZEN_WIN
+ runSanityChecks();
+#endif
+
//do not call wxApp::OnInit() to avoid using wxWidgets command line parser
//Note: app start is deferred: batch mode requires the wxApp eventhandler to be established for UI update events. This is not the case at the time of OnInit()!
Connect(EVENT_ENTER_EVENT_LOOP, wxEventHandler(Application::onEnterEventLoop), nullptr, this);
AddPendingEvent(wxCommandEvent(EVENT_ENTER_EVENT_LOOP));
-
return true; //true: continue processing; false: exit immediately.
}
@@ -251,13 +259,6 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
wxTheApp->SetExitOnFrameDelete(false); //prevent popup-windows from becoming temporary top windows leading to program exit after closure
ZEN_ON_SCOPE_EXIT(if (!mainWindowWasSet()) wxTheApp->ExitMainLoop();); //quit application, if no main window was set (batch silent mode)
- try
- {
- //tentatively set program language to OS default until GlobalSettings.xml is read later
- setLanguage(xmlAccess::XmlGlobalSettings().programLanguage); //throw FileError
- }
- catch (const FileError&) { assert(false); }
-
auto notifyFatalError = [&](const std::wstring& msg, const std::wstring& title)
{
auto titleTmp = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + L" - " + title;
@@ -595,10 +596,8 @@ void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCf
{
const TimeComp timeStamp = localTime();
- const SwitchToGui switchBatchToGui(globalConfigFile, globalCfg, referenceFile, batchCfg); //prepare potential operational switch
-
//class handling status updates and error messages
- BatchStatusHandler statusHandler(!batchCfg.runMinimized, //throw BatchAbortProcess
+ BatchStatusHandler statusHandler(!batchCfg.runMinimized, //throw BatchAbortProcess, BatchRequestSwitchToMainDialog
extractJobName(referenceFile),
globalCfg.soundFileSyncFinished,
timeStamp,
@@ -608,7 +607,6 @@ void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCf
batchCfg.handleError,
globalCfg.automaticRetryCount,
globalCfg.automaticRetryDelay,
- switchBatchToGui,
returnCode,
batchCfg.mainCfg.onCompletion,
globalCfg.gui.onCompletionHistory);
@@ -640,7 +638,7 @@ void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCf
globalCfg.createLockFile,
dirLocks,
cmpConfig,
- statusHandler);
+ statusHandler); //throw ?
//START SYNCHRONIZATION
const std::vector<FolderPairSyncCfg> syncProcessCfg = extractSyncCfg(batchCfg.mainCfg);
@@ -657,9 +655,14 @@ void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCf
globalCfg.folderAccessTimeout,
syncProcessCfg,
cmpResult,
- statusHandler);
+ statusHandler); //throw ?
}
catch (BatchAbortProcess&) {} //exit used by statusHandler
+ catch (BatchRequestSwitchToMainDialog&)
+ {
+ //open new toplevel window *after* progress dialog is gone => run on main event loop
+ return MainDialog::create(globalConfigFile, &globalCfg, xmlAccess::convertBatchToGui(batchCfg), { referenceFile }, true /*startComparison*/);
+ }
try //save global settings to XML: e.g. ignored warnings
{
diff --git a/FreeFileSync/Source/comparison.cpp b/FreeFileSync/Source/comparison.cpp
index 4b251e8b..36318542 100644
--- a/FreeFileSync/Source/comparison.cpp
+++ b/FreeFileSync/Source/comparison.cpp
@@ -45,11 +45,6 @@ namespace
{
struct ResolvedFolderPair
{
- ResolvedFolderPair(const AbstractPath& left,
- const AbstractPath& right) :
- folderPathLeft (left),
- folderPathRight(right) {}
-
AbstractPath folderPathLeft;
AbstractPath folderPathRight;
};
@@ -83,7 +78,7 @@ ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& cfgL
uniqueBaseFolders.insert(folderPathLeft);
uniqueBaseFolders.insert(folderPathRight);
- output.resolvedPairs.emplace_back(folderPathLeft, folderPathRight);
+ output.resolvedPairs.push_back({ folderPathLeft, folderPathRight });
}
const FolderStatus status = getFolderStatusNonBlocking(uniqueBaseFolders, folderAccessTimeout, allowUserInteraction, callback); //re-check *all* directories on each try!
@@ -128,7 +123,7 @@ void checkForIncompleteInput(const std::vector<ResolvedFolderPair>& folderPairs,
else if (!AFS::isNullPath(fp.folderPathLeft))
haveFullPair = true;
- if (havePartialPair == haveFullPair) //error if: all empty or exist both full and partial pairs -> support single-dir scenario
+ if (havePartialPair == haveFullPair) //error if: all empty or exist both full and partial pairs -> support single-folder comparison scenario
callback.reportWarning(_("A folder input field is empty.") + L" \n\n" +
_("The corresponding folder will be considered as empty."), warningInputFieldEmpty);
}
@@ -179,7 +174,7 @@ private:
std::vector<FilePair*>& undefinedFiles,
std::vector<SymlinkPair*>& undefinedSymlinks) const;
- std::map<DirectoryKey, DirectoryValue> directoryBuffer; //contains only *existing* directories
+ std::map<DirectoryKey, DirectoryValue> directoryBuffer_; //contains only *existing* directories
const int fileTimeTolerance_;
ProcessCallback& callback_;
};
@@ -195,8 +190,8 @@ ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, int
void reportStatus(const std::wstring& statusMsg, int itemsTotal) override
{
- callback_.updateProcessedData(itemsTotal - itemsReported, 0); //processed bytes are reported in subfunctions!
- itemsReported = itemsTotal;
+ callback_.updateProcessedData(itemsTotal - itemsReported_, 0); //processed bytes are reported in subfunctions!
+ itemsReported_ = itemsTotal;
callback_.reportStatus(statusMsg); //may throw
//callback_.requestUiRefresh(); //already called by reportStatus()
@@ -219,13 +214,13 @@ ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, int
private:
ProcessCallback& callback_;
- int itemsReported = 0;
+ int itemsReported_ = 0;
} cb(callback);
fillBuffer(keysToRead, //in
- directoryBuffer, //out
+ directoryBuffer_, //out
cb,
- UI_UPDATE_INTERVAL / 2); //every ~50 ms
+ UI_UPDATE_INTERVAL_MS / 2); //every ~50 ms
}
@@ -233,38 +228,39 @@ ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, int
//const wchar_t arrowLeft [] = L"\u2190";
//const wchar_t arrowRight[] = L"\u2192"; unicode arrows -> too small
-const wchar_t arrowLeft [] = L"<--";
-const wchar_t arrowRight[] = L"-->";
+const wchar_t arrowLeft [] = L"<-";
+const wchar_t arrowRight[] = L"->";
+//NOTE: conflict texts are NOT expected to contain additional path info (already implicit through associated item!)
+// => only add path info if information is relevant, e.g. conflict is specific to left/right side only
-//check for very old dates or dates in the future
-std::wstring getConflictInvalidDate(const std::wstring& displayPath, std::int64_t utcTime)
+template <SelectedSide side, class FileOrLinkPair> inline
+std::wstring getConflictInvalidDate(const FileOrLinkPair& file)
{
- return replaceCpy(_("File %x has an invalid date."), L"%x", fmtPath(displayPath)) + L"\n" +
- _("Date:") + L" " + utcToLocalTimeString(utcTime);
+ return replaceCpy(_("File %x has an invalid date."), L"%x", fmtPath(AFS::getDisplayPath(file.template getAbstractPath<side>()))) + L"\n" +
+ _("Date:") + L" " + utcToLocalTimeString(file.template getLastWriteTime<side>());
}
-//check for changed files with same modification date
std::wstring getConflictSameDateDiffSize(const FilePair& file)
{
- return replaceCpy(_("Files %x have the same date but a different size."), L"%x", fmtPath(file.getPairRelativePath())) + L"\n" +
- L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(file.getLastWriteTime< LEFT_SIDE>()) + L" " + _("Size:") + L" " + toGuiString(file.getFileSize<LEFT_SIDE>()) + L"\n" +
- L" " + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(file.getLastWriteTime<RIGHT_SIDE>()) + L" " + _("Size:") + L" " + toGuiString(file.getFileSize<RIGHT_SIDE>());
+ return _("Files have the same date but a different size.") + L"\n" +
+ arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(file.getLastWriteTime< LEFT_SIDE>()) + L" " + _("Size:") + L" " + toGuiString(file.getFileSize<LEFT_SIDE>()) + L"\n" +
+ arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(file.getLastWriteTime<RIGHT_SIDE>()) + L" " + _("Size:") + L" " + toGuiString(file.getFileSize<RIGHT_SIDE>());
}
std::wstring getConflictSkippedBinaryComparison(const FilePair& file)
{
- return replaceCpy(_("Content comparison was skipped for excluded files %x."), L"%x", fmtPath(file.getPairRelativePath()));
+ return _("Content comparison was skipped for excluded files.");
}
std::wstring getDescrDiffMetaShortnameCase(const FileSystemObject& fsObj)
{
return _("Items differ in attributes only") + L"\n" +
- L" " + arrowLeft + L" " + fmtPath(fsObj.getItemName< LEFT_SIDE>()) + L"\n" +
- L" " + arrowRight + L" " + fmtPath(fsObj.getItemName<RIGHT_SIDE>());
+ arrowLeft + L" " + fmtPath(fsObj.getItemName< LEFT_SIDE>()) + L"\n" +
+ arrowRight + L" " + fmtPath(fsObj.getItemName<RIGHT_SIDE>());
}
@@ -272,8 +268,8 @@ template <class FileOrLinkPair>
std::wstring getDescrDiffMetaDate(const FileOrLinkPair& file)
{
return _("Items differ in attributes only") + L"\n" +
- L" " + arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(file.template getLastWriteTime< LEFT_SIDE>()) + L"\n" +
- L" " + arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(file.template getLastWriteTime<RIGHT_SIDE>());
+ arrowLeft + L" " + _("Date:") + L" " + utcToLocalTimeString(file.template getLastWriteTime< LEFT_SIDE>()) + L"\n" +
+ arrowRight + L" " + _("Date:") + L" " + utcToLocalTimeString(file.template getLastWriteTime<RIGHT_SIDE>());
}
//-----------------------------------------------------------------------------
@@ -304,11 +300,11 @@ void categorizeSymlinkByTime(SymlinkPair& symlink)
break;
case TimeResult::LEFT_INVALID:
- symlink.setCategoryConflict(getConflictInvalidDate(AFS::getDisplayPath(symlink.getAbstractPath<LEFT_SIDE>()), symlink.getLastWriteTime<LEFT_SIDE>()));
+ symlink.setCategoryConflict(getConflictInvalidDate<LEFT_SIDE>(symlink));
break;
case TimeResult::RIGHT_INVALID:
- symlink.setCategoryConflict(getConflictInvalidDate(AFS::getDisplayPath(symlink.getAbstractPath<RIGHT_SIDE>()), symlink.getLastWriteTime<RIGHT_SIDE>()));
+ symlink.setCategoryConflict(getConflictInvalidDate<RIGHT_SIDE>(symlink));
break;
}
}
@@ -356,11 +352,11 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::compareByTimeSize(const Resolv
break;
case TimeResult::LEFT_INVALID:
- file->setCategoryConflict(getConflictInvalidDate(AFS::getDisplayPath(file->getAbstractPath<LEFT_SIDE>()), file->getLastWriteTime<LEFT_SIDE>()));
+ file->setCategoryConflict(getConflictInvalidDate<LEFT_SIDE>(*file));
break;
case TimeResult::RIGHT_INVALID:
- file->setCategoryConflict(getConflictInvalidDate(AFS::getDisplayPath(file->getAbstractPath<RIGHT_SIDE>()), file->getLastWriteTime<RIGHT_SIDE>()));
+ file->setCategoryConflict(getConflictInvalidDate<RIGHT_SIDE>(*file));
break;
}
}
@@ -734,7 +730,7 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer
//-----------------------------------------------------------------------------------------------
-//mark excluded directories (see fillBuffer()) + remove superfluous excluded subdirectories
+//uncheck excluded directories (see fillBuffer()) + remove superfluous excluded subdirectories
void stripExcludedDirectories(HierarchyObject& hierObj, const HardFilter& filterProc)
{
for (FolderPair& folder : hierObj.refSubFolders())
@@ -771,8 +767,8 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::performComparison(const Resolv
auto getDirValue = [&](const AbstractPath& folderPath) -> const DirectoryValue*
{
- auto it = directoryBuffer.find(DirectoryKey(folderPath, fpCfg.filter.nameFilter, fpCfg.handleSymlinks));
- return it != directoryBuffer.end() ? &it->second : nullptr;
+ auto it = directoryBuffer_.find(DirectoryKey(folderPath, fpCfg.filter.nameFilter, fpCfg.handleSymlinks));
+ return it != directoryBuffer_.end() ? &it->second : nullptr;
};
const DirectoryValue* bufValueLeft = getDirValue(fp.folderPathLeft);
@@ -904,26 +900,23 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings,
callback.reportInfo(e.toString()); //may throw!
}
- //-------------------some basic checks:------------------------------------------
-
const ResolvedBaseFolders& resInfo = initializeBaseFolders(cfgList, folderAccessTimeout, allowUserInteraction, callback);
-
//directory existence only checked *once* to avoid race conditions!
if (resInfo.resolvedPairs.size() != cfgList.size())
throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+ auto basefolderExisting = [&](const AbstractPath& folderPath) { return resInfo.existingBaseFolders.find(folderPath) != resInfo.existingBaseFolders.end(); };
+
+ //-----------execute basic checks all at once before starting comparison----------
+
checkForIncompleteInput(resInfo.resolvedPairs, warnings.warningInputFieldEmpty, callback);
checkFolderDependency (resInfo.resolvedPairs, warnings.warningDependentFolders, callback);
- //list of directories that are *expected* to be existent (and need to be scanned)!
-
//-------------------end of basic checks------------------------------------------
- auto basefolderExisting = [&](const AbstractPath& folderPath) { return resInfo.existingBaseFolders.find(folderPath) != resInfo.existingBaseFolders.end(); };
-
- std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>> totalWorkLoad;
+ std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>> workLoad;
for (size_t i = 0; i < cfgList.size(); ++i)
- totalWorkLoad.emplace_back(resInfo.resolvedPairs[i], cfgList[i]);
+ workLoad.emplace_back(resInfo.resolvedPairs[i], cfgList[i]);
//lock (existing) directories before comparison
if (createDirLocks)
@@ -941,9 +934,9 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings,
//------------------- fill directory buffer ---------------------------------------------------
std::set<DirectoryKey> dirsToRead;
- for (const auto& w : totalWorkLoad)
+ for (const auto& w : workLoad)
{
- if (basefolderExisting(w.first.folderPathLeft)) //only traverse *currently existing* directories: at this point user is aware that non-ex + empty string are seen as empty folder!
+ 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);
if (basefolderExisting(w.first.folderPathRight))
dirsToRead.emplace(w.first.folderPathRight, w.second.filter.nameFilter, w.second.handleSymlinks);
@@ -960,7 +953,7 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings,
//process binary comparison as one junk
std::vector<std::pair<ResolvedFolderPair, FolderPairCfg>> workLoadByContent;
- for (const auto& w : totalWorkLoad)
+ for (const auto& w : workLoad)
switch (w.second.compareVar)
{
case CompareVariant::TIME_SIZE:
@@ -973,7 +966,7 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings,
std::list<std::shared_ptr<BaseFolderPair>> outputByContent = cmpBuff.compareByContent(workLoadByContent);
//write output in expected order
- for (const auto& w : totalWorkLoad)
+ for (const auto& w : workLoad)
switch (w.second.compareVar)
{
case CompareVariant::TIME_SIZE:
@@ -996,14 +989,14 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings,
//--------- set initial sync-direction --------------------------------------------------
- for (auto j = begin(output); j != end(output); ++j)
+ for (auto it = begin(output); it != end(output); ++it)
{
- const FolderPairCfg& fpCfg = cfgList[j - output.begin()];
+ const FolderPairCfg& fpCfg = cfgList[it - output.begin()];
callback.reportStatus(_("Calculating sync directions..."));
callback.forceUiRefresh();
- zen::redetermineSyncDirection(fpCfg.directionCfg, *j,
+ zen::redetermineSyncDirection(fpCfg.directionCfg, *it,
[&](const std::wstring& warning) { callback.reportWarning(warning, warnings.warningDatabaseError); },
[&](std::int64_t bytesDelta) { callback.requestUiRefresh(); });//throw X
}
diff --git a/FreeFileSync/Source/file_hierarchy.cpp b/FreeFileSync/Source/file_hierarchy.cpp
index 98b72662..0d517525 100644
--- a/FreeFileSync/Source/file_hierarchy.cpp
+++ b/FreeFileSync/Source/file_hierarchy.cpp
@@ -144,6 +144,9 @@ bool hasDirectChild(const HierarchyObject& hierObj, Predicate p)
std::any_of(hierObj.refSubLinks ().begin(), hierObj.refSubLinks ().end(), p) ||
std::any_of(hierObj.refSubFolders().begin(), hierObj.refSubFolders().end(), p);
}
+
+const wchar_t arrowLeft [] = L"<-";
+const wchar_t arrowRight[] = L"->";
}
@@ -316,12 +319,44 @@ std::wstring zen::getCategoryDescription(CompareFilesResult cmpRes)
std::wstring zen::getCategoryDescription(const FileSystemObject& fsObj)
{
+ const std::wstring footer = L"\n[" + utfCvrtTo<std::wstring>(fsObj. getPairItemName()) + L"]";
+
const CompareFilesResult cmpRes = fsObj.getCategory();
- if (cmpRes == FILE_CONFLICT ||
- cmpRes == FILE_DIFFERENT_METADATA)
- return fsObj.getCatExtraDescription();
+ switch (cmpRes)
+ {
+ case FILE_LEFT_SIDE_ONLY:
+ case FILE_RIGHT_SIDE_ONLY:
+ case FILE_DIFFERENT_CONTENT:
+ case FILE_EQUAL:
+ return getCategoryDescription(cmpRes) + footer; //use generic description
+
+ case FILE_LEFT_NEWER:
+ case FILE_RIGHT_NEWER:
+ {
+ std::wstring descr = getCategoryDescription(cmpRes);
- return getCategoryDescription(cmpRes);
+ visitFSObject(fsObj, [](const FolderPair& folder) {},
+ [&](const FilePair& file)
+ {
+ descr = descr + L"\n" +
+ arrowLeft + L" " + zen::utcToLocalTimeString(file.getLastWriteTime< LEFT_SIDE>()) + L"\n" +
+ arrowRight + L" " + zen::utcToLocalTimeString(file.getLastWriteTime<RIGHT_SIDE>());
+ },
+ [&](const SymlinkPair& symlink)
+ {
+ descr = descr + L"\n" +
+ arrowLeft + L" " + zen::utcToLocalTimeString(symlink.getLastWriteTime< LEFT_SIDE>()) + L"\n" +
+ arrowRight + L" " + zen::utcToLocalTimeString(symlink.getLastWriteTime<RIGHT_SIDE>());
+ });
+ return descr + footer;
+ }
+
+ case FILE_DIFFERENT_METADATA:
+ case FILE_CONFLICT:
+ return fsObj.getCatExtraDescription() + footer;
+ }
+ assert(false);
+ return std::wstring();
}
@@ -365,6 +400,8 @@ std::wstring zen::getSyncOpDescription(SyncOperation op)
std::wstring zen::getSyncOpDescription(const FileSystemObject& fsObj)
{
+ const std::wstring footer = L"\n[" + utfCvrtTo<std::wstring>(fsObj. getPairItemName()) + L"]";
+
const SyncOperation op = fsObj.getSyncOperation();
switch (op)
{
@@ -376,7 +413,7 @@ std::wstring zen::getSyncOpDescription(const FileSystemObject& fsObj)
case SO_OVERWRITE_RIGHT:
case SO_DO_NOTHING:
case SO_EQUAL:
- return getSyncOpDescription(op); //use generic description
+ return getSyncOpDescription(op) + footer; //use generic description
case SO_COPY_METADATA_TO_LEFT:
case SO_COPY_METADATA_TO_RIGHT:
@@ -389,11 +426,10 @@ std::wstring zen::getSyncOpDescription(const FileSystemObject& fsObj)
if (shortNameOld != shortNameNew) //detected change in case
return getSyncOpDescription(op) + L"\n" +
- fmtPath(shortNameOld) + L" ->\n" + //show short name only
- fmtPath(shortNameNew);
+ fmtPath(shortNameOld) + L" " + arrowRight + L"\n" + //show short name only
+ fmtPath(shortNameNew) /*+ footer -> redundant */;
}
- //fallback:
- return getSyncOpDescription(op);
+ return getSyncOpDescription(op) + footer; //fallback
case SO_MOVE_LEFT_SOURCE:
case SO_MOVE_LEFT_TARGET:
@@ -413,21 +449,21 @@ std::wstring zen::getSyncOpDescription(const FileSystemObject& fsObj)
const Zstring relSource = getRelName(*sourceFile, onLeft);
const Zstring relTarget = getRelName(*targetFile, !onLeft);
+ //attention: ::SetWindowText() doesn't handle tab characters correctly in combination with certain file names, so don't use them
return getSyncOpDescription(op) + L"\n" +
(equalFilePath(beforeLast(relSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE),
beforeLast(relTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)) ?
//detected pure "rename"
- fmtPath(afterLast(relSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)) + L" ->\n" + //show short name only
+ fmtPath(afterLast(relSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)) + L" " + arrowRight + L"\n" + //show short name only
fmtPath(afterLast(relTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)) :
//"move" or "move + rename"
- fmtPath(relSource) + L" ->\n" +
- fmtPath(relTarget));
- //attention: ::SetWindowText() doesn't handle tab characters correctly in combination with certain file names, so don't use them
+ fmtPath(relSource) + L" " + arrowRight + L"\n" +
+ fmtPath(relTarget)) /*+ footer -> redundant */;
}
break;
case SO_UNRESOLVED_CONFLICT:
- return fsObj.getSyncOpConflict();
+ return fsObj.getSyncOpConflict() + footer;
}
assert(false);
diff --git a/FreeFileSync/Source/fs/abstract.h b/FreeFileSync/Source/fs/abstract.h
index 302c99db..86688543 100644
--- a/FreeFileSync/Source/fs/abstract.h
+++ b/FreeFileSync/Source/fs/abstract.h
@@ -44,18 +44,22 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
static bool isNullPath(const AbstractPath& ap) { return ap.afs->isNullPath(ap.itemPathImpl); }
- static AbstractPath appendRelPath(const AbstractPath& ap, const Zstring& relPath)
+ static AbstractPath appendRelPath(const AbstractPath& ap, const Zstring& relPath);
+
+ struct PathComplement
{
-#ifdef ZEN_WIN
- assert(!contains(relPath, L"/")); //relPath is expected to use FILE_NAME_SEPARATOR!
-#endif
- assert(relPath.empty() || (!startsWith(relPath, FILE_NAME_SEPARATOR) && !endsWith(relPath, FILE_NAME_SEPARATOR)));
- return AbstractPath(ap.afs, ap.afs->appendRelPathToItemPathImpl(ap.itemPathImpl, relPath));
- }
+ Zstring relPathL; //- relative path is filled only if the corresponding abstract path is a sub folder of the other (=> both relative paths are empty if abstract paths match)
+ Zstring relPathR; //- without FILE_NAME_SEPARATOR prefix
+ };
+ static Opt<PathComplement> getPathComplement(const AbstractPath& lhs, const AbstractPath& rhs);
static Zstring getFileShortName(const AbstractPath& ap) { return ap.afs->getFileShortName(ap.itemPathImpl); }
- static bool havePathDependency(const AbstractPath& lhs, const AbstractPath& rhs);
+ static bool havePathDependency(const AbstractPath& lhs, const AbstractPath& rhs)
+ {
+ warn_static("remove after migration")
+ return typeid(*lhs.afs) != typeid(*rhs.afs) ? false : lhs.afs->havePathDependencySameAfsType(lhs.itemPathImpl, rhs);
+ }
static Opt<Zstring> getNativeItemPath(const AbstractPath& ap) { return ap.afs->isNativeFileSystem() ? Opt<Zstring>(ap.itemPathImpl) : NoValue(); }
@@ -140,9 +144,9 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
private:
std::unique_ptr<OutputStreamImpl> outStream_; //bound!
const AbstractPath filePath_;
- bool finalizeSucceeded = false;
- Opt<std::uint64_t> bytesExpected;
- std::uint64_t bytesWrittenTotal = 0;
+ bool finalizeSucceeded_ = false;
+ Opt<std::uint64_t> bytesExpected_;
+ std::uint64_t bytesWrittenTotal_ = 0;
};
//return value always bound:
@@ -278,13 +282,15 @@ private:
virtual Zstring appendRelPathToItemPathImpl(const Zstring& itemPathImpl, const Zstring& relPath) const = 0;
+ virtual Opt<PathComplement> getPathComplementSameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const = 0;
+
+ virtual bool lessItemPathSameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const = 0;
+
//used during folder creation if parent folder is missing
virtual Opt<Zstring> getParentFolderPathImpl(const Zstring& itemPathImpl) const = 0;
virtual Zstring getFileShortName(const Zstring& itemPathImpl) const = 0;
- virtual bool lessItemPathSameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const = 0;
-
virtual bool havePathDependencySameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const = 0;
//----------------------------------------------------------------------------------------------------------------
@@ -414,12 +420,39 @@ bool AbstractFileSystem::equalAbstractPath(const AbstractPath& lhs, const Abstra
return !LessAbstractPath()(lhs, rhs) && !LessAbstractPath()(rhs, lhs);
}
+namespace impl
+{
+inline
+bool isValidRelPath(const Zstring& relPath)
+{
+#ifdef ZEN_WIN
+ const bool check1 = !contains(relPath, L'/'); //relPath is expected to use FILE_NAME_SEPARATOR!
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ const bool check1 = !contains(relPath, '\\');
+#endif
+ const bool check2 = relPath.empty() || (!startsWith(relPath, FILE_NAME_SEPARATOR) && !endsWith(relPath, FILE_NAME_SEPARATOR));
+ return check1 && check2;
+}
+}
inline
-bool AbstractFileSystem::havePathDependency(const AbstractPath& lhs, const AbstractPath& rhs)
+AbstractPath AbstractFileSystem::appendRelPath(const AbstractPath& ap, const Zstring& relPath)
{
- return typeid(*lhs.afs) != typeid(*rhs.afs) ? false : lhs.afs->havePathDependencySameAfsType(lhs.itemPathImpl, rhs);
-};
+ assert(impl::isValidRelPath(relPath));
+ return AbstractPath(ap.afs, ap.afs->appendRelPathToItemPathImpl(ap.itemPathImpl, relPath));
+}
+
+
+inline
+auto AbstractFileSystem::getPathComplement(const AbstractPath& lhs, const AbstractPath& rhs) -> Opt<PathComplement>
+{
+ if (typeid(*lhs.afs) != typeid(*rhs.afs))
+ return NoValue();
+ Opt<PathComplement> result = lhs.afs->getPathComplementSameAfsType(lhs.itemPathImpl, rhs);
+ assert(!result || (impl::isValidRelPath(result->relPathL)&& impl::isValidRelPath(result->relPathR)));
+ assert(!result || result->relPathL.empty() || result->relPathR.empty());
+ return result;
+}
inline
@@ -452,7 +485,7 @@ AbstractFileSystem::OutputStream::OutputStream(std::unique_ptr<OutputStreamImpl>
outStream_(std::move(outStream)), filePath_(filePath)
{
if (streamSize)
- bytesExpected = *streamSize;
+ bytesExpected_ = *streamSize;
}
@@ -463,7 +496,7 @@ AbstractFileSystem::OutputStream::~OutputStream()
outStream_.reset(); //close file handle *before* remove!
- if (!finalizeSucceeded) //transactional output stream! => clean up!
+ if (!finalizeSucceeded_) //transactional output stream! => clean up!
try { AbstractFileSystem::removeFile(filePath_); /*throw FileError*/ }
catch (FileError& e) { (void)e; assert(false); }
}
@@ -479,7 +512,7 @@ size_t AbstractFileSystem::OutputStream::tryWrite(const void* data, size_t len)
if (bytesWritten > len)
throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
- bytesWrittenTotal += bytesWritten;
+ bytesWrittenTotal_ += bytesWritten;
return bytesWritten;
}
@@ -488,14 +521,14 @@ inline
AbstractFileSystem::FileId AbstractFileSystem::OutputStream::finalize(const std::function<void()>& onUpdateStatus) //throw FileError
{
//important check: catches corrupt sftp download with libssh2!
- if (bytesExpected && *bytesExpected != bytesWrittenTotal)
+ if (bytesExpected_ && *bytesExpected_ != bytesWrittenTotal_)
throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getDisplayPath(filePath_))),
replaceCpy(replaceCpy(_("Unexpected size of data stream.\nExpected: %x bytes\nActual: %y bytes"),
- L"%x", numberTo<std::wstring>(*bytesExpected)),
- L"%y", numberTo<std::wstring>(bytesWrittenTotal)));
+ L"%x", numberTo<std::wstring>(*bytesExpected_)),
+ L"%y", numberTo<std::wstring>(bytesWrittenTotal_)));
FileId fileId = outStream_->finalize(onUpdateStatus); //throw FileError
- finalizeSucceeded = true;
+ finalizeSucceeded_ = true;
return fileId;
}
diff --git a/FreeFileSync/Source/fs/native.cpp b/FreeFileSync/Source/fs/native.cpp
index 994d3388..9c341769 100644
--- a/FreeFileSync/Source/fs/native.cpp
+++ b/FreeFileSync/Source/fs/native.cpp
@@ -264,6 +264,24 @@ private:
Zstring appendRelPathToItemPathImpl(const Zstring& itemPathImpl, const Zstring& relPath) const override { return appendPaths(itemPathImpl, relPath, FILE_NAME_SEPARATOR); }
+ Opt<PathComplement> getPathComplementSameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const override
+ {
+ const Zstring& lhs = appendSeparator(itemPathImplLhs);
+ const Zstring& rhs = appendSeparator(getItemPathImpl(apRhs));
+
+ const size_t lenMin = std::min(lhs.length(), rhs.length());
+
+ if (cmpFilePath(lhs.c_str(), lenMin,
+ rhs.c_str(), lenMin) != 0)
+ return NoValue();
+
+ return PathComplement({ Zstring(lhs.begin() + lenMin, lhs.end()),
+ Zstring(rhs.begin() + lenMin, rhs.end())
+ });
+ }
+
+ bool lessItemPathSameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const override { return LessFilePath()(itemPathImplLhs, getItemPathImpl(apRhs)); }
+
//used during folder creation if parent folder is missing
Opt<Zstring> getParentFolderPathImpl(const Zstring& itemPathImpl) const override
{
@@ -292,8 +310,6 @@ private:
Zstring getFileShortName(const Zstring& itemPathImpl) const override { return afterLast(itemPathImpl, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); }
- bool lessItemPathSameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const override { return LessFilePath()(itemPathImplLhs, getItemPathImpl(apRhs)); }
-
bool havePathDependencySameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const override
{
const Zstring& lhs = appendSeparator(itemPathImplLhs);
diff --git a/FreeFileSync/Source/fs/native_traverser_impl.h b/FreeFileSync/Source/fs/native_traverser_impl.h
index dedc5c61..35891c8a 100644
--- a/FreeFileSync/Source/fs/native_traverser_impl.h
+++ b/FreeFileSync/Source/fs/native_traverser_impl.h
@@ -77,7 +77,7 @@ private:
{
struct ::dirent* dirEntry = nullptr;
if (::readdir_r(dirObj, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r");
+ 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! http://blogs.msdn.com/b/oldnewthing/archive/2014/06/12/10533529.aspx
if (!dirEntry) //no more items
@@ -86,7 +86,7 @@ private:
//don't return "." and ".."
const char* itemName = dirEntry->d_name; //evaluate dirEntry *before* going into recursion => we use a single "buffer"!
- if (itemName[0] == 0) throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item is missing a name.");
+ if (itemName[0] == 0) throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item is missing a name.");
if (itemName[0] == '.' &&
(itemName[1] == 0 || (itemName[1] == '.' && itemName[2] == 0)))
continue;
diff --git a/FreeFileSync/Source/lib/binary.cpp b/FreeFileSync/Source/lib/binary.cpp
index 38fc0fda..0d9b9549 100644
--- a/FreeFileSync/Source/lib/binary.cpp
+++ b/FreeFileSync/Source/lib/binary.cpp
@@ -39,22 +39,22 @@ const size_t BLOCK_SIZE_MAX = 16 * 1024 * 1024;
struct StreamReader
{
StreamReader(const AbstractPath& filePath, const std::function<void(std::int64_t bytesDelta)>& notifyProgress, size_t& unevenBytes) :
- stream(AFS::getInputStream(filePath)), //throw FileError, (ErrorFileLocked)
- defaultBlockSize(stream->getBlockSize()),
- dynamicBlockSize(defaultBlockSize),
+ stream_(AFS::getInputStream(filePath)), //throw FileError, (ErrorFileLocked)
+ defaultBlockSize_(stream_->getBlockSize()),
+ dynamicBlockSize_(defaultBlockSize_),
notifyProgress_(notifyProgress),
unevenBytes_(unevenBytes) {}
void appendChunk(std::vector<char>& buffer) //throw FileError
{
- assert(!eof);
- if (eof) return;
+ assert(!eof_);
+ if (eof_) return;
const auto startTime = std::chrono::steady_clock::now();
- buffer.resize(buffer.size() + dynamicBlockSize);
- const size_t bytesRead = stream->tryRead(&*(buffer.end() - dynamicBlockSize), dynamicBlockSize); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0
- buffer.resize(buffer.size() - dynamicBlockSize + bytesRead); //caveat: unsigned arithmetics
+ buffer.resize(buffer.size() + dynamicBlockSize_);
+ const size_t bytesRead = stream_->tryRead(&*(buffer.end() - dynamicBlockSize_), dynamicBlockSize_); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0
+ buffer.resize(buffer.size() - dynamicBlockSize_ + bytesRead); //caveat: unsigned arithmetics
const auto stopTime = std::chrono::steady_clock::now();
@@ -68,7 +68,7 @@ struct StreamReader
if (bytesRead == 0)
{
- eof = true;
+ eof_ = true;
return;
}
@@ -76,31 +76,31 @@ struct StreamReader
const auto loopTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count();
if (loopTimeMs >= 100)
- lastDelayViolation = stopTime;
+ 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!
- if (stopTime >= lastDelayViolation + std::chrono::seconds(2))
+ if (stopTime >= lastDelayViolation_ + std::chrono::seconds(2))
{
- lastDelayViolation = stopTime;
- proposedBlockSize = dynamicBlockSize * 2;
+ lastDelayViolation_ = stopTime;
+ proposedBlockSize = dynamicBlockSize_ * 2;
}
if (loopTimeMs > 500)
- proposedBlockSize = dynamicBlockSize / 2;
+ proposedBlockSize = dynamicBlockSize_ / 2;
- if (defaultBlockSize <= proposedBlockSize && proposedBlockSize <= BLOCK_SIZE_MAX)
- dynamicBlockSize = proposedBlockSize;
+ if (defaultBlockSize_ <= proposedBlockSize && proposedBlockSize <= BLOCK_SIZE_MAX)
+ dynamicBlockSize_ = proposedBlockSize;
}
- bool isEof() const { return eof; }
+ bool isEof() const { return eof_; }
private:
- const std::unique_ptr<AFS::InputStream> stream;
- const size_t defaultBlockSize;
- size_t dynamicBlockSize;
+ const std::unique_ptr<AFS::InputStream> stream_;
+ const size_t defaultBlockSize_;
+ size_t dynamicBlockSize_;
const std::function<void(std::int64_t bytesDelta)> notifyProgress_;
size_t& unevenBytes_;
- std::chrono::steady_clock::time_point lastDelayViolation = std::chrono::steady_clock::now();
- bool eof = false;
+ std::chrono::steady_clock::time_point lastDelayViolation_ = std::chrono::steady_clock::now();
+ bool eof_ = false;
};
}
diff --git a/FreeFileSync/Source/lib/db_file.cpp b/FreeFileSync/Source/lib/db_file.cpp
index 7fb7977e..ea679635 100644
--- a/FreeFileSync/Source/lib/db_file.cpp
+++ b/FreeFileSync/Source/lib/db_file.cpp
@@ -141,11 +141,11 @@ DbStreams loadStreams(const AbstractPath& dbPath, const std::function<void(std::
}
catch (UnexpectedEndOfStreamError&)
{
- throw FileError(_("Database file is corrupt:") + L"\n" + fmtPath(AFS::getDisplayPath(dbPath)), L"Unexpected end of stream.");
+ throw FileError(_("Database file is corrupted:") + L"\n" + fmtPath(AFS::getDisplayPath(dbPath)), L"Unexpected end of stream.");
}
catch (const std::bad_alloc& e) //still required?
{
- throw FileError(_("Database file is corrupt:") + L"\n" + fmtPath(AFS::getDisplayPath(dbPath)),
+ throw FileError(_("Database file is corrupted:") + L"\n" + fmtPath(AFS::getDisplayPath(dbPath)),
_("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what()));
}
}
@@ -305,7 +305,7 @@ public:
const int streamVersionR = readNumber<std::int32_t>(inR); //
if (streamVersionL != streamVersionR)
- throw FileError(_("Database file is corrupt:") + L"\n" + fmtPath(displayFilePathL) + L"\n" + fmtPath(displayFilePathR), L"different stream formats");
+ throw FileError(_("Database file is corrupted:") + L"\n" + fmtPath(displayFilePathL) + L"\n" + fmtPath(displayFilePathR), L"different stream formats");
if (streamVersionL != DB_FORMAT_STREAM)
throw FileError(replaceCpy(_("Database file %x is incompatible."), L"%x", fmtPath(displayFilePathL)), L"unknown stream format");
@@ -314,7 +314,7 @@ public:
const bool has1stPartR = readNumber<std::int8_t>(inR) != 0; //
if (has1stPartL == has1stPartR)
- throw FileError(_("Database file is corrupt:") + L"\n" + fmtPath(displayFilePathL) + L"\n" + fmtPath(displayFilePathR), L"second part missing");
+ throw FileError(_("Database file is corrupted:") + L"\n" + fmtPath(displayFilePathL) + L"\n" + fmtPath(displayFilePathR), L"second part missing");
MemStreamIn& in1stPart = has1stPartL ? inL : inR;
MemStreamIn& in2ndPart = has1stPartL ? inR : inL;
@@ -340,11 +340,11 @@ public:
}
catch (const UnexpectedEndOfStreamError&)
{
- throw FileError(_("Database file is corrupt:") + L"\n" + fmtPath(displayFilePathL) + L"\n" + fmtPath(displayFilePathR), L"Unexpected end of stream.");
+ throw FileError(_("Database file is corrupted:") + L"\n" + fmtPath(displayFilePathL) + L"\n" + fmtPath(displayFilePathR), L"Unexpected end of stream.");
}
catch (const std::bad_alloc& e)
{
- throw FileError(_("Database file is corrupt:") + L"\n" + fmtPath(displayFilePathL) + L"\n" + fmtPath(displayFilePathR),
+ throw FileError(_("Database file is corrupted:") + L"\n" + fmtPath(displayFilePathL) + L"\n" + fmtPath(displayFilePathR),
_("Out of memory.") + L" " + utfCvrtTo<std::wstring>(e.what()));
}
}
diff --git a/FreeFileSync/Source/lib/dir_exist_async.h b/FreeFileSync/Source/lib/dir_exist_async.h
index d3eaa9c1..d790404e 100644
--- a/FreeFileSync/Source/lib/dir_exist_async.h
+++ b/FreeFileSync/Source/lib/dir_exist_async.h
@@ -10,6 +10,7 @@
#include <list>
#include <zen/thread.h>
#include <zen/file_error.h>
+#include <zen/basic_math.h>
#include "../fs/abstract.h"
#include "../process_callback.h"
@@ -49,7 +50,7 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath, AFS::LessAb
}));
//don't wait (almost) endlessly like Win32 would on non-existing network shares:
- std::chrono::steady_clock::time_point stopTime = std::chrono::steady_clock::now() + std::chrono::seconds(folderAccessTimeout);
+ const auto startTime = std::chrono::steady_clock::now();
for (auto& fi : futureInfo)
{
@@ -57,8 +58,8 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath, AFS::LessAb
procCallback.reportStatus(replaceCpy(_("Searching for folder %x..."), L"%x", displayPathFmt)); //may throw!
- while (std::chrono::steady_clock::now() < stopTime &&
- fi.second.wait_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL / 2)) != std::future_status::ready)
+ 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)
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 6a8960ca..e2184781 100644
--- a/FreeFileSync/Source/lib/dir_lock.cpp
+++ b/FreeFileSync/Source/lib/dir_lock.cpp
@@ -364,12 +364,12 @@ std::string retrieveLockId(const Zstring& lockFilePath) //throw FileError
}
-enum ProcessStatus
+enum class ProcessStatus
{
- PROC_STATUS_NOT_RUNNING,
- PROC_STATUS_RUNNING,
- PROC_STATUS_ITS_US,
- PROC_STATUS_CANT_TELL
+ NOT_RUNNING,
+ RUNNING,
+ ITS_US,
+ CANT_TELL,
};
ProcessStatus getProcessStatus(const LockInformation& lockInfo) //throw FileError
@@ -378,15 +378,15 @@ ProcessStatus getProcessStatus(const LockInformation& lockInfo) //throw FileErro
if (lockInfo.computerName != localInfo.computerName ||
lockInfo.userId != localInfo.userId) //another user may run a session right now!
- return PROC_STATUS_CANT_TELL; //lock owned by different computer in this network
+ return ProcessStatus::CANT_TELL; //lock owned by different computer in this network
if (lockInfo.sessionId == localInfo.sessionId &&
lockInfo.processId == localInfo.processId) //obscure, but possible: deletion failed or a lock file is "stolen" and put back while the program is running
- return PROC_STATUS_ITS_US;
+ return ProcessStatus::ITS_US;
if (Opt<SessionId> sessionId = getSessionId(lockInfo.processId)) //throw FileError
- return *sessionId == lockInfo.sessionId ? PROC_STATUS_RUNNING : PROC_STATUS_NOT_RUNNING;
- return PROC_STATUS_NOT_RUNNING;
+ return *sessionId == lockInfo.sessionId ? ProcessStatus::RUNNING : ProcessStatus::NOT_RUNNING;
+ return ProcessStatus::NOT_RUNNING;
}
@@ -412,12 +412,12 @@ void waitOnDirLock(const Zstring& lockFilePath, DirLockCallback* callback) //thr
originalLockId = lockInfo.lockId;
switch (getProcessStatus(lockInfo)) //throw FileError
{
- case PROC_STATUS_ITS_US: //since we've already passed LockAdmin, the lock file seems abandoned ("stolen"?) although it's from this process
- case PROC_STATUS_NOT_RUNNING:
+ 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 PROC_STATUS_RUNNING:
- case PROC_STATUS_CANT_TELL:
+ case ProcessStatus::RUNNING:
+ case ProcessStatus::CANT_TELL:
break;
}
}
diff --git a/FreeFileSync/Source/lib/error_log.h b/FreeFileSync/Source/lib/error_log.h
index bef4a75d..3241803e 100644
--- a/FreeFileSync/Source/lib/error_log.h
+++ b/FreeFileSync/Source/lib/error_log.h
@@ -34,7 +34,7 @@ void logFatalError(const std::string& msg) //throw()
const std::string logEntry = "[" + formatTime<std::string>(FORMAT_DATE) + " "+ formatTime<std::string>(FORMAT_TIME) + "] " + msg;
try
{
- saveBinContainer(getConfigDir() + Zstr("LastError.log"), logEntry, nullptr); //throw FileError
+ saveBinContainer(getConfigDirPathPf() + Zstr("LastError.log"), logEntry, nullptr); //throw FileError
}
catch (const FileError&) {}
}
diff --git a/FreeFileSync/Source/lib/ffs_paths.cpp b/FreeFileSync/Source/lib/ffs_paths.cpp
index 9df9ce5e..f57409d8 100644
--- a/FreeFileSync/Source/lib/ffs_paths.cpp
+++ b/FreeFileSync/Source/lib/ffs_paths.cpp
@@ -30,22 +30,22 @@ Zstring getExecutablePathPf() //directory containing executable WITH path separa
return appendSeparator(beforeLast(utfCvrtTo<Zstring>(wxStandardPaths::Get().GetExecutablePath()), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE));
}
#endif
+}
+
#ifdef ZEN_WIN
-inline
-Zstring getInstallFolderPathPf() //root install directory WITH path separator at end
+Zstring zen::getInstallDirPath() //root install directory WITHOUT path separator at end
{
- return appendSeparator(beforeLast(beforeLast(getExecutablePathPf(), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE));
+ return beforeLast(beforeLast(getExecutablePathPf(), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
}
#endif
-}
bool zen::isPortableVersion()
{
#ifdef ZEN_WIN
- return !fileExists(getInstallFolderPathPf() + L"uninstall.exe") && //created by NSIS
- !dirExists (getInstallFolderPathPf() + L"Uninstall"); //created by Inno Setup
+ return !fileExists(appendSeparator(getInstallDirPath()) + L"uninstall.exe") && //created by NSIS
+ !dirExists (appendSeparator(getInstallDirPath()) + L"Uninstall"); //created by Inno Setup
#elif defined ZEN_LINUX
return !endsWith(getExecutablePathPf(), "/bin/"); //this check is a bit lame...
@@ -56,18 +56,7 @@ bool zen::isPortableVersion()
}
-bool zen::manualProgramUpdateRequired()
-{
-#if defined ZEN_WIN || defined ZEN_MAC
- return true;
-#elif defined ZEN_LINUX
- return true;
- //return isPortableVersion(); //locally installed version is updated by Launchpad
-#endif
-}
-
-
-Zstring zen::getResourceDir()
+Zstring zen::getResourceDirPf()
{
//make independent from wxWidgets global variable "appname"; support being called by RealTimeSync
auto appName = wxTheApp->GetAppName();
@@ -75,7 +64,7 @@ Zstring zen::getResourceDir()
ZEN_ON_SCOPE_EXIT(wxTheApp->SetAppName(appName));
#ifdef ZEN_WIN
- return getInstallFolderPathPf();
+ return appendSeparator(getInstallDirPath());
#elif defined ZEN_LINUX
if (isPortableVersion())
return getExecutablePathPf();
@@ -87,7 +76,7 @@ Zstring zen::getResourceDir()
}
-Zstring zen::getConfigDir()
+Zstring zen::getConfigDirPathPf()
{
//make independent from wxWidgets global variable "appname"; support being called by RealTimeSync
auto appName = wxTheApp->GetAppName();
@@ -96,7 +85,7 @@ Zstring zen::getConfigDir()
#ifdef ZEN_WIN
if (isPortableVersion())
- return getInstallFolderPathPf();
+ return appendSeparator(getInstallDirPath());
#elif defined ZEN_LINUX
if (isPortableVersion())
return getExecutablePathPf();
@@ -119,7 +108,7 @@ Zstring zen::getConfigDir()
Zstring zen::getFreeFileSyncLauncherPath()
{
#ifdef ZEN_WIN
- return getInstallFolderPathPf() + Zstr("FreeFileSync.exe");
+ return appendSeparator(getInstallDirPath()) + Zstr("FreeFileSync.exe");
#elif defined ZEN_LINUX
return getExecutablePathPf() + Zstr("FreeFileSync");
diff --git a/FreeFileSync/Source/lib/ffs_paths.h b/FreeFileSync/Source/lib/ffs_paths.h
index 497681c1..2fe32220 100644
--- a/FreeFileSync/Source/lib/ffs_paths.h
+++ b/FreeFileSync/Source/lib/ffs_paths.h
@@ -14,14 +14,17 @@ namespace zen
//------------------------------------------------------------------------------
//global program directories
//------------------------------------------------------------------------------
-Zstring getResourceDir(); //resource directory WITH path separator at end
-Zstring getConfigDir (); //config directory WITH path separator at end
+Zstring getResourceDirPf(); //resource directory WITH path separator at end
+Zstring getConfigDirPathPf (); //config directory WITH path separator at end
//------------------------------------------------------------------------------
bool isPortableVersion();
+#ifdef ZEN_WIN
+ Zstring getInstallDirPath(); //root install directory WITHOUT path separator at end
+#endif
+
Zstring getFreeFileSyncLauncherPath(); //full path to application launcher C:\...\FreeFileSync.exe
-bool manualProgramUpdateRequired();
}
#endif //FFS_PATHS_H_842759083425342534253
diff --git a/FreeFileSync/Source/lib/generate_logfile.h b/FreeFileSync/Source/lib/generate_logfile.h
index 9b463426..ff85d2f7 100644
--- a/FreeFileSync/Source/lib/generate_logfile.h
+++ b/FreeFileSync/Source/lib/generate_logfile.h
@@ -12,6 +12,7 @@
#include <zen/format_unit.h>
#include "ffs_paths.h"
#include "../fs/abstract.h"
+#include "../file_hierarchy.h"
namespace zen
@@ -152,7 +153,7 @@ void streamToLogFile(const SummaryInfo& summary, //throw FileError
inline
-Zstring getLastSyncsLogfilePath() { return getConfigDir() + Zstr("LastSyncs.log"); }
+Zstring getLastSyncsLogfilePath() { return getConfigDirPathPf() + Zstr("LastSyncs.log"); }
inline
diff --git a/FreeFileSync/Source/lib/hard_filter.cpp b/FreeFileSync/Source/lib/hard_filter.cpp
index 36ad6871..6bef6e11 100644
--- a/FreeFileSync/Source/lib/hard_filter.cpp
+++ b/FreeFileSync/Source/lib/hard_filter.cpp
@@ -110,11 +110,13 @@ const Char* cStringFind(const Char* str, Char ch) //= strchr(), wcschr()
}
+/*
struct FullMatch
{
static bool matchesMaskEnd (const Zchar* path) { return *path == 0; }
static bool matchesMaskStar(const Zchar* path) { return true; }
};
+*/
struct ParentFolderMatch //strict match of parent folder path!
{
@@ -250,8 +252,6 @@ std::vector<Zstring> zen::splitByDelimiter(const Zstring& filterString)
NameFilter::NameFilter(const Zstring& includePhrase, const Zstring& excludePhrase)
{
- //no need for regular expressions: In tests wxRegex was slower than wxString::Matches() by a factor of 10
-
//setup include/exclude filters for files and directories
for (const Zstring& entry : splitByDelimiter(includePhrase)) addFilterEntry(entry, includeMasksFileFolder, includeMasksFolder);
for (const Zstring& entry : splitByDelimiter(excludePhrase)) addFilterEntry(entry, excludeMasksFileFolder, excludeMasksFolder);
@@ -274,6 +274,7 @@ void NameFilter::addExclusion(const Zstring& excludePhrase)
bool NameFilter::passFileFilter(const Zstring& relFilePath) const
{
+ assert(!startsWith(relFilePath, FILE_NAME_SEPARATOR));
#if defined ZEN_WIN || defined ZEN_MAC //Windows does NOT distinguish between upper/lower-case
const Zstring& pathFmt = makeUpperCopy(relFilePath);
#elif defined ZEN_LINUX //Linux DOES distinguish between upper/lower-case
@@ -291,6 +292,7 @@ bool NameFilter::passFileFilter(const Zstring& relFilePath) const
bool NameFilter::passDirFilter(const Zstring& relDirPath, bool* childItemMightMatch) const
{
+ assert(!startsWith(relDirPath, FILE_NAME_SEPARATOR));
assert(!childItemMightMatch || *childItemMightMatch == true); //check correct usage
#if defined ZEN_WIN || defined ZEN_MAC //Windows does NOT distinguish between upper/lower-case
@@ -306,8 +308,8 @@ bool NameFilter::passDirFilter(const Zstring& relDirPath, bool* childItemMightMa
*childItemMightMatch = false; //perf: no need to traverse deeper; subfolders/subfiles would be excluded by filter anyway!
/*
Attention: the design choice that "childItemMightMatch" is optional implies that the filter must provide correct results no matter if this
- value is considered by the client! In particular, if *childItemMightMatch == false, then any filter evaluations for child items must
- also return "false"!
+ value is considered by the client!
+ In particular, if *childItemMightMatch == false, then any filter evaluations for child items must also return "false"!
This is not a problem for folder traversal which stops at the first *childItemMightMatch == false anyway, but other code continues recursing further,
e.g. the database update code in db_file.cpp recurses unconditionally without filter check! It's possible to construct edge cases with incorrect
behavior if "childItemMightMatch" were not optional:
diff --git a/FreeFileSync/Source/lib/help_provider.h b/FreeFileSync/Source/lib/help_provider.h
index a42995be..67e1c34e 100644
--- a/FreeFileSync/Source/lib/help_provider.h
+++ b/FreeFileSync/Source/lib/help_provider.h
@@ -15,22 +15,20 @@ inline void uninitializeHelp() {}
}
#else
+#include <zen/globals.h>
+#include <wx+/http.h>
+#include "ffs_paths.h"
+
#ifdef ZEN_WIN
- #include <zen/zstring.h>
#include <wx/msw/helpchm.h>
-
#elif defined ZEN_LINUX || defined ZEN_MAC
#include <wx/html/helpctrl.h>
#endif
-#include "ffs_paths.h"
-
namespace zen
{
-void displayHelpEntry(wxWindow* parent);
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!
@@ -41,68 +39,29 @@ void uninitializeHelp(); //clean up gracefully during app shutdown: leaving this
//######################## implementation ########################
namespace impl
{
-//finish wxWidgets' job:
#ifdef ZEN_WIN
-class FfsHelpController
+class FfsHelpController //finish wxWidgets' job:
{
public:
- static FfsHelpController& instance()
- {
- static FfsHelpController inst; //external linkage, despite inline definition!
- return inst;
- }
+ FfsHelpController () { chmHlp.Initialize(utfCvrtTo<wxString>(zen::getResourceDirPf()) + L"FreeFileSync.chm"); }
+ ~FfsHelpController() { chmHlp.Quit(); } //don't keep help window open while app is shut down! => crash on Win 8.1!
- void openSection(const wxString& section, wxWindow* parent)
+ void displaySection(const wxString& section)
{
- //don't put in constructor: not needed if only uninitialize() is ever called!
- if (!chmHlp)
- {
- chmHlp = std::make_unique<wxCHMHelpController>();
- chmHlp->Initialize(utfCvrtTo<wxString>(zen::getResourceDir()) + L"FreeFileSync.chm");
- }
-
- if (section.empty())
- chmHlp->DisplayContents();
- else
- chmHlp->DisplaySection(replaceCpy(section, L'/', utfCvrtTo<wxString>(FILE_NAME_SEPARATOR)));
- }
-
- void uninitialize() //avoid static init/teardown order fiasco
- {
- if (chmHlp)
- {
- chmHlp->Quit(); //don't let help windows open while app is shut down! => crash on Win 8.1!
- chmHlp.reset();
- }
+ chmHlp.DisplaySection(replaceCpy(section, L'/', FILE_NAME_SEPARATOR));
}
private:
- FfsHelpController() {}
- ~FfsHelpController() { assert(!chmHlp); }
-
- std::unique_ptr<wxCHMHelpController> chmHlp;
+ wxCHMHelpController chmHlp;
};
-#elif defined ZEN_LINUX || defined ZEN_MAC
-struct FfsHelpController
-{
- static FfsHelpController& instance()
- {
- static FfsHelpController inst;
- return inst;
- }
- void openSection(const wxString& section, wxWindow* parent)
- {
- wxHtmlModalHelp dlg(parent, utfCvrtTo<wxString>(zen::getResourceDir()) + L"Help/FreeFileSync.hhp", section,
- wxHF_DEFAULT_STYLE | wxHF_DIALOG | wxHF_MODAL | wxHF_MERGE_BOOKS);
- (void)dlg;
- //-> solves modal help craziness on OSX!
- //-> Suse Linux: avoids program hang on exit if user closed help parent dialog before the help dialog itself was closed (why is this even possible???)
- // avoids ESC key not being recognized by help dialog (but by parent dialog instead)
- }
- void uninitialize() {}
-};
+inline
+Global<FfsHelpController>& refGlobalHelpController()
+{
+ static Global<FfsHelpController> inst(std::make_unique<FfsHelpController>()); //external linkage even in header!
+ return inst;
+}
#endif
}
@@ -110,21 +69,35 @@ struct FfsHelpController
inline
void displayHelpEntry(const wxString& topic, wxWindow* parent)
{
- impl::FfsHelpController::instance().openSection(L"html/" + topic + L".html", parent);
-}
+ if (internetIsAlive()) //noexcept
+ wxLaunchDefaultBrowser(L"http://www.freefilesync.org/manual.php?topic=" + topic);
+ else
+ -> what if FFS is blocked, but the web browser would have internet access??
+ {
+ const wxString section = L"html/" + topic + L".html";
+#ifdef ZEN_WIN
+ if (std::shared_ptr<impl::FfsHelpController> h = impl::refGlobalHelpController().get())
+ h->displaySection(section);
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ wxHtmlModalHelp dlg(parent, utfCvrtTo<wxString>(zen::getResourceDirPf()) + L"Help/FreeFileSync.hhp", section,
+ wxHF_DEFAULT_STYLE | wxHF_DIALOG | wxHF_MODAL | wxHF_MERGE_BOOKS);
+ (void)dlg;
+ //-> solves modal help craziness on OSX!
+ //-> Suse Linux: avoids program hang on exit if user closed help parent dialog before the help dialog itself was closed (why is this even possible???)
+ // avoids ESC key not being recognized by help dialog (but by parent dialog instead)
-inline
-void displayHelpEntry(wxWindow* parent)
-{
- impl::FfsHelpController::instance().openSection(wxString(), parent);
+#endif
+ }
}
+
inline
void uninitializeHelp()
{
- impl::FfsHelpController::instance().uninitialize();
-
+#ifdef ZEN_WIN
+ impl::refGlobalHelpController().set(nullptr);
+#endif
}
}
#endif
diff --git a/FreeFileSync/Source/lib/icon_buffer.cpp b/FreeFileSync/Source/lib/icon_buffer.cpp
index 91f6f68f..10965397 100644
--- a/FreeFileSync/Source/lib/icon_buffer.cpp
+++ b/FreeFileSync/Source/lib/icon_buffer.cpp
@@ -29,10 +29,6 @@ const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to hold in buffer:
const std::thread::id mainThreadId = std::this_thread::get_id();
#endif
-#ifdef ZEN_WIN
- const bool wereVistaOrLater = vistaOrLater();
-#endif
-
//destroys raw icon! Call from GUI thread only!
wxBitmap extractWxBitmap(ImageHolder&& ih)
@@ -333,14 +329,14 @@ public:
IconBuffer::IconSize st) :
workload_(workload),
buffer_(buffer),
- iconSizeType(st) {}
+ iconSizeType_(st) {}
void operator()() const; //thread entry
private:
std::shared_ptr<WorkLoad> workload_; //main/worker thread may access different shared_ptr instances safely (even though they have the same target!)
std::shared_ptr<Buffer> buffer_; //http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/shared_ptr.htm?sess=8153b05b34d890e02d48730db1ff7ddc#ThreadSafety
- const IconBuffer::IconSize iconSizeType;
+ const IconBuffer::IconSize iconSizeType_;
};
@@ -370,13 +366,11 @@ void WorkerThread::operator()() const //thread entry
{
#ifdef ZEN_WIN
setCurrentThreadName("Icon Buffer Worker");
-
try
{
- //1. Initialize COM here due to the icon_loader.h dependency only, but NOT due to native.h, mtp.h's internal COM usage => this is not our responsibility!
+ //1. Initialize COM here only due to the icon_loader.h dependency, but NOT due to native.h, mtp.h's internal COM usage => this is not our responsibility!
ComInitializer ci; //throw SysError
#endif
-
for (;;)
{
interruptionPoint(); //throw ThreadInterruption
@@ -385,7 +379,7 @@ void WorkerThread::operator()() const //thread entry
const AbstractPath itemPath = workload_->extractNextFile(); //throw ThreadInterruption
if (!buffer_->hasIcon(itemPath)) //perf: workload may contain duplicate entries?
- buffer_->insert(itemPath, getDisplayIcon(itemPath, iconSizeType));
+ buffer_->insert(itemPath, getDisplayIcon(itemPath, iconSizeType_));
}
#ifdef ZEN_WIN
@@ -404,7 +398,7 @@ struct IconBuffer::Impl
InterruptibleThread worker;
//-------------------------
- std::map<Zstring, wxBitmap, LessFilePath> extensionIcons;
+ std::map<Zstring, wxBitmap, LessFilePath> extensionIcons; //no item count limit!? Test case C:\ ~ 3800 unique file extensions
};
@@ -434,11 +428,11 @@ int IconBuffer::getSize(IconSize sz)
return 24;
#endif
case IconBuffer::SIZE_MEDIUM:
-#ifdef ZEN_WIN
- if (!wereVistaOrLater) return 32; //48x48 doesn't look sharp on XP
-#endif
+#ifdef ZEN_WIN_PRE_VISTA
+ return 32; //48x48 doesn't look sharp on XP
+#else //Win (Vista) or Linux/Mac
return 48;
-
+#endif
case IconBuffer::SIZE_LARGE:
return 128;
}
@@ -467,7 +461,6 @@ Opt<wxBitmap> IconBuffer::retrieveFileIcon(const AbstractPath& filePath)
if (hasStandardIconExtension(fileName))
return this->getIconByExtension(fileName); //buffered!!!
#endif
-
if (Opt<wxBitmap> ico = pimpl->buffer->retrieve(filePath))
return ico;
@@ -489,17 +482,17 @@ void IconBuffer::setWorkload(const std::vector<AbstractPath>& load)
wxBitmap IconBuffer::getIconByExtension(const Zstring& filePath)
{
- const Zstring& extension = getFileExtension(filePath);
+ const Zstring& ext = getFileExtension(filePath);
assert(std::this_thread::get_id() == mainThreadId);
- auto it = pimpl->extensionIcons.find(extension);
+ auto it = pimpl->extensionIcons.find(ext);
if (it == pimpl->extensionIcons.end())
{
- const Zstring& templateName(extension.empty() ? Zstr("file") : Zstr("file.") + extension);
+ 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(extension, 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;
@@ -527,7 +520,9 @@ wxBitmap IconBuffer::linkOverlayIcon(IconSize sz)
if (pixelSize >= 128) return L"link_128";
if (pixelSize >= 48) return L"link_48";
+#ifdef ZEN_WIN_PRE_VISTA
if (pixelSize >= 32) return L"link_32";
+#endif
if (pixelSize >= 24) return L"link_24";
return L"link_16";
}());
@@ -540,8 +535,8 @@ bool zen::hasLinkExtension(const Zstring& filepath)
return hasWindowsLinkExtension(filepath);
#elif defined ZEN_LINUX
- const Zstring& extension = getFileExtension(filepath);
- return extension == "desktop";
+ const Zstring& ext = getFileExtension(filepath);
+ return ext == "desktop";
#elif defined ZEN_MAC
return false; //alias files already get their arrow icon via "NSWorkspace::iconForFile"
diff --git a/FreeFileSync/Source/lib/icon_holder.h b/FreeFileSync/Source/lib/icon_holder.h
index 63a93436..9046a18a 100644
--- a/FreeFileSync/Source/lib/icon_holder.h
+++ b/FreeFileSync/Source/lib/icon_holder.h
@@ -18,33 +18,33 @@ struct ImageHolder //prepare conversion to wxImage as much as possible while sta
ImageHolder() {}
ImageHolder(int w, int h, bool withAlpha) : //init with allocated memory
- width(w), height(h),
- rgb(static_cast<unsigned char*>(::malloc(width * height * 3))),
- alpha(withAlpha ? static_cast<unsigned char*>(::malloc(width * height)) : nullptr) {}
+ width_(w), height_(h),
+ rgb_(static_cast<unsigned char*>(::malloc(w * h * 3))),
+ alpha_(withAlpha ? static_cast<unsigned char*>(::malloc(w * h)) : nullptr) {}
ImageHolder (ImageHolder&& tmp) = default; //
ImageHolder& operator=(ImageHolder&& tmp) = default; //move semantics only!
ImageHolder (const ImageHolder&) = delete; //
ImageHolder& operator=(const ImageHolder&) = delete; //
- explicit operator bool() const { return rgb.get() != nullptr; }
+ explicit operator bool() const { return rgb_.get() != nullptr; }
- int getWidth () const { return width; }
- int getHeight() const { return height; }
+ int getWidth () const { return width_; }
+ int getHeight() const { return height_; }
- unsigned char* getRgb () { return rgb .get(); }
- unsigned char* getAlpha() { return alpha.get(); }
+ unsigned char* getRgb () { return rgb_ .get(); }
+ unsigned char* getAlpha() { return alpha_.get(); }
- unsigned char* releaseRgb () { return rgb .release(); }
- unsigned char* releaseAlpha() { return alpha.release(); }
+ unsigned char* releaseRgb () { return rgb_ .release(); }
+ unsigned char* releaseAlpha() { return alpha_.release(); }
struct CLibFree { void operator()(unsigned char* p) const { ::free(p); } }; //use malloc/free to allow direct move into wxImage!
private:
- int width = 0;
- int height = 0;
- std::unique_ptr<unsigned char, CLibFree> rgb; //optional
- std::unique_ptr<unsigned char, CLibFree> alpha; //
+ int width_ = 0;
+ int height_ = 0;
+ std::unique_ptr<unsigned char, CLibFree> rgb_; //optional
+ std::unique_ptr<unsigned char, CLibFree> alpha_; //
};
}
diff --git a/FreeFileSync/Source/lib/icon_loader.cpp b/FreeFileSync/Source/lib/icon_loader.cpp
index 088fb082..1a1c0610 100644
--- a/FreeFileSync/Source/lib/icon_loader.cpp
+++ b/FreeFileSync/Source/lib/icon_loader.cpp
@@ -90,10 +90,11 @@ ImageHolder copyToImageHolder(const GdkPixbuf* pixbuf)
IconSizeType getThumbSizeType(int pixelSize)
{
//coordinate with IconBuffer::getSize()!
- if (pixelSize >= 256) return ICON_SIZE_256;
if (pixelSize >= 128) return ICON_SIZE_128;
if (pixelSize >= 48) return ICON_SIZE_48;
+#ifdef ZEN_WIN_PRE_VISTA
if (pixelSize >= 32) return ICON_SIZE_32;
+#endif
return ICON_SIZE_16;
}
diff --git a/FreeFileSync/Source/lib/localization.cpp b/FreeFileSync/Source/lib/localization.cpp
index 2e715c59..e691e5c9 100644
--- a/FreeFileSync/Source/lib/localization.cpp
+++ b/FreeFileSync/Source/lib/localization.cpp
@@ -158,10 +158,12 @@ std::vector<TranslationInfo> loadTranslations()
{
std::vector<TranslationInfo> locMapping;
{
+ const wchar_t ltrMark = L'\u200E'; //UTF-8: E2 80 8E
+
//default entry:
TranslationInfo newEntry;
newEntry.languageID = wxLANGUAGE_ENGLISH_US;
- newEntry.languageName = L"English (US)";
+ newEntry.languageName = std::wstring(L"English (US)") + ltrMark; //handle weak ")" for bidi-algorithm
newEntry.translatorName = L"Zenju";
newEntry.languageFlag = L"flag_usa.png";
newEntry.langFilePath = Zstr("");
@@ -171,7 +173,7 @@ std::vector<TranslationInfo> loadTranslations()
//search language files available
std::vector<Zstring> lngFilePaths;
- traverseFolder(zen::getResourceDir() + Zstr("Languages"), [&](const zen::FileInfo& fi) //FileInfo is ambiguous on OS X
+ traverseFolder(zen::getResourceDirPf() + Zstr("Languages"), [&](const zen::FileInfo& fi) //FileInfo is ambiguous on OS X
{
if (pathEndsWith(fi.fullPath, Zstr(".lng")))
lngFilePaths.push_back(fi.fullPath);
@@ -191,7 +193,7 @@ std::vector<TranslationInfo> loadTranslations()
assert(!lngHeader.localeName .empty());
assert(!lngHeader.flagFile .empty());
/*
- Some ISO codes are used by multiple wxLanguage ids which can lead to incorrect mapping!!!
+ Some ISO codes are used by multiple wxLanguage IDs which can lead to incorrect mapping!!!
=> Identify by description, e.g. "Chinese (Traditional)". The following ids are affected:
wxLANGUAGE_CHINESE_TRADITIONAL
wxLANGUAGE_ENGLISH_UK
diff --git a/FreeFileSync/Source/lib/parallel_scan.cpp b/FreeFileSync/Source/lib/parallel_scan.cpp
index 1057c530..0c34b534 100644
--- a/FreeFileSync/Source/lib/parallel_scan.cpp
+++ b/FreeFileSync/Source/lib/parallel_scan.cpp
@@ -5,11 +5,13 @@
// *****************************************************************************
#include "parallel_scan.h"
+#include <chrono>
#include <zen/file_error.h>
+#include <zen/basic_math.h>
#include <zen/thread.h>
#include <zen/scope_guard.h>
#include <zen/fixed_list.h>
-#include <zen/tick_count.h>
+//#include <zen/tick_count.h>
#include "db_file.h"
#include "lock_holder.h"
@@ -157,52 +159,53 @@ using BasicWString = Zbase<wchar_t>; //thread-safe string class for UI texts
class AsyncCallback //actor pattern
{
public:
- AsyncCallback(size_t reportingIntervalMs) : reportingIntervalTicks(reportingIntervalMs * ticksPerSec() / 1000) {}
+ AsyncCallback(size_t reportingIntervalMs) : reportingIntervalMs_(reportingIntervalMs) {}
//blocking call: context of worker thread
FillBufferCallback::HandleError reportError(const std::wstring& msg, size_t retryNumber) //throw ThreadInterruption
{
- std::unique_lock<std::mutex> dummy(lockErrorInfo);
- interruptibleWait(conditionCanReportError, dummy, [this] { return !errorInfo && !errorResponse; }); //throw ThreadInterruption
+ std::unique_lock<std::mutex> dummy(lockErrorInfo_);
+ interruptibleWait(conditionCanReportError_, dummy, [this] { return !errorInfo_ && !errorResponse_; }); //throw ThreadInterruption
- errorInfo = std::make_unique<std::pair<BasicWString, size_t>>(copyStringTo<BasicWString>(msg), retryNumber);
+ errorInfo_ = std::make_unique<std::pair<BasicWString, size_t>>(copyStringTo<BasicWString>(msg), retryNumber);
- interruptibleWait(conditionGotResponse, dummy, [this] { return static_cast<bool>(errorResponse); }); //throw ThreadInterruption
+ interruptibleWait(conditionGotResponse_, dummy, [this] { return static_cast<bool>(errorResponse_); }); //throw ThreadInterruption
- FillBufferCallback::HandleError rv = *errorResponse;
+ FillBufferCallback::HandleError rv = *errorResponse_;
- errorInfo .reset();
- errorResponse.reset();
+ errorInfo_ .reset();
+ errorResponse_.reset();
dummy.unlock(); //optimization for condition_variable::notify_all()
- conditionCanReportError.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
+ conditionCanReportError_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
return rv;
}
void processErrors(FillBufferCallback& callback) //context of main thread, call repreatedly
{
- std::unique_lock<std::mutex> dummy(lockErrorInfo);
- if (errorInfo.get() && !errorResponse.get())
+ std::unique_lock<std::mutex> dummy(lockErrorInfo_);
+ if (errorInfo_.get() && !errorResponse_.get())
{
- FillBufferCallback::HandleError rv = callback.reportError(copyStringTo<std::wstring>(errorInfo->first), errorInfo->second); //throw!
- errorResponse = std::make_unique<FillBufferCallback::HandleError>(rv);
+ FillBufferCallback::HandleError rv = callback.reportError(copyStringTo<std::wstring>(errorInfo_->first), errorInfo_->second); //throw!
+ errorResponse_ = std::make_unique<FillBufferCallback::HandleError>(rv);
dummy.unlock(); //optimization for condition_variable::notify_all()
- conditionGotResponse.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
+ conditionGotResponse_.notify_all(); //instead of notify_one(); workaround bug: https://svn.boost.org/trac/boost/ticket/7796
}
}
- void incrementNotifyingThreadId() { ++notifyingThreadID; } //context of main thread
+ void incrementNotifyingThreadId() { ++notifyingThreadID_; } //context of main thread
//perf optimization: comparison phase is 7% faster by avoiding needless std::wstring contstruction for reportCurrentFile()
- bool mayReportCurrentFile(int threadID, TickVal& lastReportTime) const
+ bool mayReportCurrentFile(int threadID, std::chrono::steady_clock::time_point& lastReportTime) const
{
- if (threadID != notifyingThreadID) //only one thread at a time may report status
+ if (threadID != notifyingThreadID_) //only one thread at a time may report status
return false;
- const TickVal now = getTicks(); //0 on error
- if (dist(lastReportTime, now) >= reportingIntervalTicks) //perform ui updates not more often than necessary
+ 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!
{
lastReportTime = now; //keep "lastReportTime" at worker thread level to avoid locking!
return true;
@@ -212,24 +215,24 @@ public:
void reportCurrentFile(const std::wstring& filepath) //context of worker thread
{
- std::lock_guard<std::mutex> dummy(lockCurrentStatus);
- currentFile = copyStringTo<BasicWString>(filepath);
+ std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
+ currentFile_ = copyStringTo<BasicWString>(filepath);
}
std::wstring getCurrentStatus() //context of main thread, call repreatedly
{
std::wstring filepath;
{
- std::lock_guard<std::mutex> dummy(lockCurrentStatus);
- filepath = copyStringTo<std::wstring>(currentFile);
+ std::lock_guard<std::mutex> dummy(lockCurrentStatus_);
+ filepath = copyStringTo<std::wstring>(currentFile_);
}
if (filepath.empty())
return std::wstring();
- std::wstring statusText = copyStringTo<std::wstring>(textScanning);
+ std::wstring statusText = copyStringTo<std::wstring>(textScanning_);
- const long activeCount = activeWorker;
+ const long activeCount = activeWorker_;
if (activeCount >= 2)
statusText += L" [" + replaceCpy(_P("1 thread", "%x threads", activeCount), L"%x", numberTo<std::wstring>(activeCount)) + L"]";
@@ -238,33 +241,33 @@ public:
return statusText;
}
- void incItemsScanned() { ++itemsScanned; } //perf: irrelevant! scanning is almost entirely file I/O bound, not CPU bound! => no prob having multiple threads poking at the same variable!
- long getItemsScanned() const { return itemsScanned; }
+ void incItemsScanned() { ++itemsScanned_; } //perf: irrelevant! scanning is almost entirely file I/O bound, not CPU bound! => no prob having multiple threads poking at the same variable!
+ long getItemsScanned() const { return itemsScanned_; }
- void incActiveWorker() { ++activeWorker; }
- void decActiveWorker() { --activeWorker; }
- long getActiveWorker() const { return activeWorker; }
+ void incActiveWorker() { ++activeWorker_; }
+ void decActiveWorker() { --activeWorker_; }
+ long getActiveWorker() const { return activeWorker_; }
private:
//---- error handling ----
- std::mutex lockErrorInfo;
- std::condition_variable conditionCanReportError;
- std::condition_variable conditionGotResponse;
- std::unique_ptr<std::pair<BasicWString, size_t>> errorInfo; //error message + retry number
- std::unique_ptr<FillBufferCallback::HandleError> errorResponse;
+ std::mutex lockErrorInfo_;
+ std::condition_variable conditionCanReportError_;
+ std::condition_variable conditionGotResponse_;
+ std::unique_ptr<std::pair<BasicWString, size_t>> errorInfo_; //error message + retry number
+ std::unique_ptr<FillBufferCallback::HandleError> errorResponse_;
//---- status updates ----
- std::atomic<int> notifyingThreadID { 0 }; //CAVEAT: do NOT use boost::thread::id: https://svn.boost.org/trac/boost/ticket/5754
+ std::atomic<int> notifyingThreadID_ { 0 }; //CAVEAT: do NOT use boost::thread::id: https://svn.boost.org/trac/boost/ticket/5754
- std::mutex lockCurrentStatus; //use a different lock for current file: continue traversing while some thread may process an error
- BasicWString currentFile;
- const std::int64_t reportingIntervalTicks;
+ std::mutex lockCurrentStatus_; //use a different lock for current file: continue traversing while some thread may process an error
+ BasicWString currentFile_;
+ const std::int64_t reportingIntervalMs_;
- 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!
+ 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!
//---- status updates II (lock free) ----
- std::atomic<int> itemsScanned{ 0 }; //std:atomic is uninitialized by default!
- std::atomic<int> activeWorker{ 0 }; //
+ std::atomic<int> itemsScanned_{ 0 }; //std:atomic is uninitialized by default!
+ std::atomic<int> activeWorker_{ 0 }; //
};
//-------------------------------------------------------------------------------------------------
@@ -296,7 +299,7 @@ public:
AsyncCallback& acb_;
const int threadID_;
- TickVal lastReportTime;
+ std::chrono::steady_clock::time_point lastReportTime_;
};
@@ -339,7 +342,7 @@ void DirCallback::onFile(const FileInfo& fi) //throw ThreadInterruption
const Zstring fileRelPath = parentRelPathPf_ + fi.itemName;
//update status information no matter whether item is excluded or not!
- if (cfg.acb_.mayReportCurrentFile(cfg.threadID_, cfg.lastReportTime))
+ if (cfg.acb_.mayReportCurrentFile(cfg.threadID_, cfg.lastReportTime_))
cfg.acb_.reportCurrentFile(AFS::getDisplayPath(AFS::appendRelPath(cfg.baseFolderPath_, fileRelPath)));
//------------------------------------------------------------------------------------
@@ -371,7 +374,7 @@ std::unique_ptr<AFS::TraverserCallback> DirCallback::onDir(const DirInfo& di) //
const Zstring& folderRelPath = parentRelPathPf_ + di.itemName;
//update status information no matter whether item is excluded or not!
- if (cfg.acb_.mayReportCurrentFile(cfg.threadID_, cfg.lastReportTime))
+ if (cfg.acb_.mayReportCurrentFile(cfg.threadID_, cfg.lastReportTime_))
cfg.acb_.reportCurrentFile(AFS::getDisplayPath(AFS::appendRelPath(cfg.baseFolderPath_, folderRelPath)));
//------------------------------------------------------------------------------------
@@ -390,11 +393,11 @@ std::unique_ptr<AFS::TraverserCallback> DirCallback::onDir(const DirInfo& di) //
if (level_ > 100) //Win32 traverser: stack overflow approximately at level 1000
if (!tryReportingItemError([&] //check after FolderContainer::addSubFolder()
{
- throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", AFS::getDisplayPath(AFS::appendRelPath(cfg.baseFolderPath_, folderRelPath))), L"Endless recursion.");
+ throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", AFS::getDisplayPath(AFS::appendRelPath(cfg.baseFolderPath_, folderRelPath))), L"Endless recursion.");
}, *this, di.itemName))
return nullptr;
- return std::make_unique<DirCallback>(cfg, folderRelPath + FILE_NAME_SEPARATOR, subFolder, level_ + 1); //releaseDirTraverser() is guaranteed to be called in any case
+ return std::make_unique<DirCallback>(cfg, folderRelPath + FILE_NAME_SEPARATOR, subFolder, level_ + 1);
}
@@ -405,7 +408,7 @@ DirCallback::HandleLink DirCallback::onSymlink(const SymlinkInfo& si) //throw Th
const Zstring& linkRelPath = parentRelPathPf_ + si.itemName;
//update status information no matter whether item is excluded or not!
- if (cfg.acb_.mayReportCurrentFile(cfg.threadID_, cfg.lastReportTime))
+ if (cfg.acb_.mayReportCurrentFile(cfg.threadID_, cfg.lastReportTime_))
cfg.acb_.reportCurrentFile(AFS::getDisplayPath(AFS::appendRelPath(cfg.baseFolderPath_, linkRelPath)));
switch (cfg.handleSymlinks_)
@@ -500,7 +503,7 @@ public:
acb_->incActiveWorker();
ZEN_ON_SCOPE_EXIT(acb_->decActiveWorker());
- if (acb_->mayReportCurrentFile(travCfg.threadID_, travCfg.lastReportTime))
+ if (acb_->mayReportCurrentFile(travCfg.threadID_, travCfg.lastReportTime_))
acb_->reportCurrentFile(AFS::getDisplayPath(travCfg.baseFolderPath_)); //just in case first directory access is blocking
DirCallback cb(travCfg, Zstring(), outputContainer, 0);
diff --git a/FreeFileSync/Source/lib/process_xml.cpp b/FreeFileSync/Source/lib/process_xml.cpp
index ba681397..1e31cabf 100644
--- a/FreeFileSync/Source/lib/process_xml.cpp
+++ b/FreeFileSync/Source/lib/process_xml.cpp
@@ -96,7 +96,7 @@ XmlGlobalSettings::XmlGlobalSettings()
Zstring xmlAccess::getGlobalConfigFile()
{
- return zen::getConfigDir() + Zstr("GlobalSettings.xml");
+ return zen::getConfigDirPathPf() + Zstr("GlobalSettings.xml");
}
@@ -492,15 +492,15 @@ void writeText(const ColumnTypeNavi& value, std::string& output)
{
switch (value)
{
- case ColumnTypeNavi::BYTES:
- output = "Bytes";
- break;
- case ColumnTypeNavi::DIRECTORY:
+ case ColumnTypeNavi::FOLDER_NAME:
output = "Tree";
break;
case ColumnTypeNavi::ITEM_COUNT:
output = "Count";
break;
+ case ColumnTypeNavi::BYTES:
+ output = "Bytes";
+ break;
}
}
@@ -508,12 +508,12 @@ template <> inline
bool readText(const std::string& input, ColumnTypeNavi& value)
{
const std::string tmp = trimCpy(input);
- if (tmp == "Bytes")
- value = ColumnTypeNavi::BYTES;
- else if (tmp == "Tree")
- value = ColumnTypeNavi::DIRECTORY;
+ if (tmp == "Tree")
+ value = ColumnTypeNavi::FOLDER_NAME;
else if (tmp == "Count")
value = ColumnTypeNavi::ITEM_COUNT;
+ else if (tmp == "Bytes")
+ value = ColumnTypeNavi::BYTES;
else
return false;
return true;
@@ -1204,7 +1204,6 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config, int formatVer)
//last update check
inGui["LastOnlineCheck" ](config.gui.lastUpdateCheck);
inGui["LastOnlineVersion"](config.gui.lastOnlineVersion);
- inGui["LastOnlineChanges"](config.gui.lastOnlineChangeLog);
//batch specific global settings
//XmlIn inBatch = in["Batch"];
@@ -1599,7 +1598,6 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out)
//last update check
outGui["LastOnlineCheck" ](config.gui.lastUpdateCheck);
outGui["LastOnlineVersion"](config.gui.lastOnlineVersion);
- outGui["LastOnlineChanges"](config.gui.lastOnlineChangeLog);
//batch specific global settings
//XmlOut outBatch = out["Batch"];
diff --git a/FreeFileSync/Source/lib/process_xml.h b/FreeFileSync/Source/lib/process_xml.h
index f29ed729..16659cc5 100644
--- a/FreeFileSync/Source/lib/process_xml.h
+++ b/FreeFileSync/Source/lib/process_xml.h
@@ -251,8 +251,7 @@ struct XmlGlobalSettings
};
time_t lastUpdateCheck = 0; //number of seconds since 00:00 hours, Jan 1, 1970 UTC
- std::wstring lastOnlineVersion;
- std::wstring lastOnlineChangeLog;
+ std::string lastOnlineVersion;
} gui;
};
diff --git a/FreeFileSync/Source/lib/resolve_path.cpp b/FreeFileSync/Source/lib/resolve_path.cpp
index b081e1e9..a67460bd 100644
--- a/FreeFileSync/Source/lib/resolve_path.cpp
+++ b/FreeFileSync/Source/lib/resolve_path.cpp
@@ -722,7 +722,7 @@ void NetworkConnector::connect(const Credentials& cred) //throw FileError
assert(!cred.remoteName.empty());
trgRes.lpRemoteName = const_cast<LPWSTR>(cred.remoteName.c_str()); //
if (!cred.localName.empty())
- trgRes.lpLocalName = const_cast<LPWSTR>(cred.localName.c_str()); //lpNetResource is marked "__in", seems WNetAddConnection2 is not const correct!
+ trgRes.lpLocalName = const_cast<LPWSTR>(cred.localName.c_str()); //lpNetResource is marked "__in", seems WNetAddConnection2 is not const correct!
const DWORD rv = ::WNetAddConnection2(&trgRes, //__in LPNETRESOURCE lpNetResource,
nullptr, //__in LPCTSTR lpPassword,
@@ -736,6 +736,7 @@ void NetworkConnector::connect(const Credentials& cred) //throw FileError
//1219 ERROR_SESSION_CREDENTIAL_CONFLICT Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again.
//1326 ERROR_LOGON_FAILURE Logon failure: unknown user name or bad password.
//1236 ERROR_CONNECTION_ABORTED
+ //1244 ERROR_NOT_AUTHENTICATED
}
}
diff --git a/FreeFileSync/Source/lib/status_handler.cpp b/FreeFileSync/Source/lib/status_handler.cpp
index 1d940cc9..55765fc3 100644
--- a/FreeFileSync/Source/lib/status_handler.cpp
+++ b/FreeFileSync/Source/lib/status_handler.cpp
@@ -5,21 +5,22 @@
// *****************************************************************************
#include "status_handler.h"
-#include <zen/tick_count.h>
+#include <chrono>
+#include <zen/basic_math.h>
using namespace zen;
namespace
{
-const std::int64_t TICKS_UPDATE_INTERVAL = UI_UPDATE_INTERVAL* ticksPerSec() / 1000;
-TickVal lastExec = getTicks();
+std::chrono::steady_clock::time_point lastExec;
};
bool zen::updateUiIsAllowed()
{
- const TickVal now = getTicks(); //0 on error
- if (dist(lastExec, now) >= TICKS_UPDATE_INTERVAL) //perform ui updates not more often than necessary
+ 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!
{
lastExec = now;
return true;
diff --git a/FreeFileSync/Source/process_callback.h b/FreeFileSync/Source/process_callback.h
index e7291196..51dd956f 100644
--- a/FreeFileSync/Source/process_callback.h
+++ b/FreeFileSync/Source/process_callback.h
@@ -12,7 +12,7 @@
//interface for comparison and synchronization process status updates (used by GUI or Batch mode)
-const int UI_UPDATE_INTERVAL = 100; //unit: [ms]; perform ui updates not more often than necessary,
+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
//this one is required by async directory existence check!
diff --git a/FreeFileSync/Source/synchronization.cpp b/FreeFileSync/Source/synchronization.cpp
index 3b58d4ba..4f36ded1 100644
--- a/FreeFileSync/Source/synchronization.cpp
+++ b/FreeFileSync/Source/synchronization.cpp
@@ -91,14 +91,17 @@ void SyncStatistics::processFile(const FilePair& file)
case SO_DELETE_LEFT:
++deleteLeft_;
+ physicalDeleteLeft_ = true;
break;
case SO_DELETE_RIGHT:
++deleteRight_;
+ physicalDeleteRight_ = true;
break;
case SO_MOVE_LEFT_TARGET:
++updateLeft_;
+ //physicalDeleteLeft_ ? -> usually, no; except when falling back to "copy + delete"
break;
case SO_MOVE_RIGHT_TARGET:
@@ -112,15 +115,17 @@ void SyncStatistics::processFile(const FilePair& file)
case SO_OVERWRITE_LEFT:
++updateLeft_;
bytesToProcess_ += static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>());
+ physicalDeleteLeft_ = true;
break;
case SO_OVERWRITE_RIGHT:
++updateRight_;
bytesToProcess_ += static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>());
+ physicalDeleteRight_ = true;
break;
case SO_UNRESOLVED_CONFLICT:
- conflictMsgs_.emplace_back(file.getPairRelativePath(), file.getSyncOpConflict());
+ conflictMsgs_.push_back({ file.getPairRelativePath(), file.getSyncOpConflict() });
break;
case SO_COPY_METADATA_TO_LEFT:
@@ -153,24 +158,28 @@ void SyncStatistics::processLink(const SymlinkPair& link)
case SO_DELETE_LEFT:
++deleteLeft_;
+ physicalDeleteLeft_ = true;
break;
case SO_DELETE_RIGHT:
++deleteRight_;
+ physicalDeleteRight_ = true;
break;
case SO_OVERWRITE_LEFT:
case SO_COPY_METADATA_TO_LEFT:
++updateLeft_;
+ physicalDeleteLeft_ = true;
break;
case SO_OVERWRITE_RIGHT:
case SO_COPY_METADATA_TO_RIGHT:
++updateRight_;
+ physicalDeleteRight_ = true;
break;
case SO_UNRESOLVED_CONFLICT:
- conflictMsgs_.emplace_back(link.getPairRelativePath(), link.getSyncOpConflict());
+ conflictMsgs_.push_back({ link.getPairRelativePath(), link.getSyncOpConflict() });
break;
case SO_MOVE_LEFT_SOURCE:
@@ -198,16 +207,18 @@ void SyncStatistics::processFolder(const FolderPair& folder)
++createRight_;
break;
- case SO_DELETE_LEFT: //if deletion variant == user-defined directory existing on other volume, this results in a full copy + delete operation!
+ case SO_DELETE_LEFT: //if deletion variant == versioning with user-defined directory existing on other volume, this results in a full copy + delete operation!
++deleteLeft_; //however we cannot (reliably) anticipate this situation, fortunately statistics can be adapted during sync!
+ physicalDeleteLeft_ = true;
break;
case SO_DELETE_RIGHT:
++deleteRight_;
+ physicalDeleteRight_ = true;
break;
case SO_UNRESOLVED_CONFLICT:
- conflictMsgs_.emplace_back(folder.getPairRelativePath(), folder.getSyncOpConflict());
+ conflictMsgs_.push_back({ folder.getPairRelativePath(), folder.getSyncOpConflict() });
break;
case SO_OVERWRITE_LEFT:
@@ -262,6 +273,36 @@ std::vector<zen::FolderPairSyncCfg> zen::extractSyncCfg(const MainConfiguration&
namespace
{
+inline
+Opt<SelectedSide> getTargetDirection(SyncOperation syncOp)
+{
+ switch (syncOp)
+ {
+ case SO_CREATE_NEW_LEFT:
+ case SO_DELETE_LEFT:
+ case SO_OVERWRITE_LEFT:
+ case SO_COPY_METADATA_TO_LEFT:
+ case SO_MOVE_LEFT_SOURCE:
+ case SO_MOVE_LEFT_TARGET:
+ return LEFT_SIDE;
+
+ case SO_CREATE_NEW_RIGHT:
+ case SO_DELETE_RIGHT:
+ case SO_OVERWRITE_RIGHT:
+ case SO_COPY_METADATA_TO_RIGHT:
+ case SO_MOVE_RIGHT_SOURCE:
+ case SO_MOVE_RIGHT_TARGET:
+ return RIGHT_SIDE;
+
+ case SO_DO_NOTHING:
+ case SO_EQUAL:
+ case SO_UNRESOLVED_CONFLICT:
+ break; //nothing to do
+ }
+ return NoValue();
+}
+
+
//test if user accidentally selected the wrong folders to sync
bool significantDifferenceDetected(const SyncStatistics& folderPairStat)
{
@@ -542,12 +583,10 @@ public:
{
MinimumDiskSpaceNeeded inst;
inst.recurse(baseFolder);
- return std::make_pair(inst.spaceNeededLeft, inst.spaceNeededRight);
+ return std::make_pair(inst.spaceNeededLeft_, inst.spaceNeededRight_);
}
private:
- MinimumDiskSpaceNeeded() {}
-
void recurse(const HierarchyObject& hierObj)
{
//don't process directories
@@ -557,33 +596,33 @@ private:
switch (file.getSyncOperation()) //evaluate comparison result and sync direction
{
case SO_CREATE_NEW_LEFT:
- spaceNeededLeft += static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>());
+ spaceNeededLeft_ += static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>());
break;
case SO_CREATE_NEW_RIGHT:
- spaceNeededRight += static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>());
+ spaceNeededRight_ += static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>());
break;
case SO_DELETE_LEFT:
//if (freeSpaceDelLeft_)
- spaceNeededLeft -= static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>());
+ spaceNeededLeft_ -= static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>());
break;
case SO_DELETE_RIGHT:
//if (freeSpaceDelRight_)
- spaceNeededRight -= static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>());
+ spaceNeededRight_ -= static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>());
break;
case SO_OVERWRITE_LEFT:
//if (freeSpaceDelLeft_)
- spaceNeededLeft -= static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>());
- spaceNeededLeft += static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>());
+ spaceNeededLeft_ -= static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>());
+ spaceNeededLeft_ += static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>());
break;
case SO_OVERWRITE_RIGHT:
//if (freeSpaceDelRight_)
- spaceNeededRight -= static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>());
- spaceNeededRight += static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>());
+ spaceNeededRight_ -= static_cast<std::int64_t>(file.getFileSize<RIGHT_SIDE>());
+ spaceNeededRight_ += static_cast<std::int64_t>(file.getFileSize<LEFT_SIDE>());
break;
case SO_DO_NOTHING:
@@ -606,8 +645,8 @@ private:
recurse(folder);
}
- std::int64_t spaceNeededLeft = 0;
- std::int64_t spaceNeededRight = 0;
+ std::int64_t spaceNeededLeft_ = 0;
+ std::int64_t spaceNeededRight_ = 0;
};
//----------------------------------------------------------------------------------------
@@ -642,16 +681,16 @@ public:
}
private:
- enum PassId
+ enum PassNo
{
PASS_ONE, //delete files
PASS_TWO, //create, modify
- PASS_NEVER //skip
+ PASS_NEVER //skip item
};
- static PassId getPass(const FilePair& file);
- static PassId getPass(const SymlinkPair& link);
- static PassId getPass(const FolderPair& folder);
+ static PassNo getPass(const FilePair& file);
+ static PassNo getPass(const SymlinkPair& link);
+ static PassNo getPass(const FolderPair& folder);
template <SelectedSide side>
void prepare2StepMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError
@@ -660,7 +699,7 @@ private:
void manageFileMove(FilePair& sourceObj, FilePair& targetObj); //throw FileError
void runZeroPass(HierarchyObject& hierObj);
- template <PassId pass>
+ template <PassNo pass>
void runPass(HierarchyObject& hierObj); //throw X
void synchronizeFile(FilePair& file);
@@ -964,7 +1003,7 @@ void SynchronizeFolderPair::runZeroPass(HierarchyObject& hierObj)
// - support change in type: overwrite file by directory, symlink by file, ect.
inline
-SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const FilePair& file)
+SynchronizeFolderPair::PassNo SynchronizeFolderPair::getPass(const FilePair& file)
{
switch (file.getSyncOperation()) //evaluate comparison result and sync direction
{
@@ -1002,7 +1041,7 @@ SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const FilePair& fil
inline
-SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const SymlinkPair& link)
+SynchronizeFolderPair::PassNo SynchronizeFolderPair::getPass(const SymlinkPair& link)
{
switch (link.getSyncOperation()) //evaluate comparison result and sync direction
{
@@ -1034,7 +1073,7 @@ SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const SymlinkPair&
inline
-SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const FolderPair& folder)
+SynchronizeFolderPair::PassNo SynchronizeFolderPair::getPass(const FolderPair& folder)
{
switch (folder.getSyncOperation()) //evaluate comparison result and sync direction
{
@@ -1065,7 +1104,7 @@ SynchronizeFolderPair::PassId SynchronizeFolderPair::getPass(const FolderPair& f
}
-template <SynchronizeFolderPair::PassId pass>
+template <SynchronizeFolderPair::PassNo pass>
void SynchronizeFolderPair::runPass(HierarchyObject& hierObj) //throw X
{
//synchronize files:
@@ -1091,36 +1130,6 @@ void SynchronizeFolderPair::runPass(HierarchyObject& hierObj) //throw X
//---------------------------------------------------------------------------------------------------------------
inline
-Opt<SelectedSide> getTargetDirection(SyncOperation syncOp)
-{
- switch (syncOp)
- {
- case SO_CREATE_NEW_LEFT:
- case SO_DELETE_LEFT:
- case SO_OVERWRITE_LEFT:
- case SO_COPY_METADATA_TO_LEFT:
- case SO_MOVE_LEFT_SOURCE:
- case SO_MOVE_LEFT_TARGET:
- return LEFT_SIDE;
-
- case SO_CREATE_NEW_RIGHT:
- case SO_DELETE_RIGHT:
- case SO_OVERWRITE_RIGHT:
- case SO_COPY_METADATA_TO_RIGHT:
- case SO_MOVE_RIGHT_SOURCE:
- case SO_MOVE_RIGHT_TARGET:
- return RIGHT_SIDE;
-
- case SO_DO_NOTHING:
- case SO_EQUAL:
- case SO_UNRESOLVED_CONFLICT:
- break; //nothing to do
- }
- return NoValue();
-}
-
-
-inline
void SynchronizeFolderPair::synchronizeFile(FilePair& file)
{
const SyncOperation syncOp = file.getSyncOperation();
@@ -1855,13 +1864,14 @@ void zen::synchronize(const TimeComp& timeStamp,
{
int itemsTotal = 0;
int64_t bytesTotal = 0;
- for (auto j = begin(folderCmp); j != end(folderCmp); ++j)
+ std::for_each(begin(folderCmp), end(folderCmp),
+ [&](const BaseFolderPair& baseFolder)
{
- SyncStatistics fpStats(*j);
+ SyncStatistics fpStats(baseFolder);
itemsTotal += getCUD(fpStats);
bytesTotal += fpStats.getBytesToProcess();
folderPairStats.push_back(fpStats);
- }
+ });
//inform about the total amount of data that will be processed from now on
//keep at beginning so that all gui elements are initialized properly
@@ -1903,15 +1913,16 @@ void zen::synchronize(const TimeComp& timeStamp,
//aggregate information
std::map<AbstractPath, ReadWriteCount, AFS::LessAbstractPath> dirReadWriteCount; //count read/write accesses
- for (auto j = begin(folderCmp); j != end(folderCmp); ++j)
+ std::for_each(begin(folderCmp), end(folderCmp),
+ [&](const BaseFolderPair& baseFolder)
{
//create all entries first! otherwise counting accesses would be too complex during later inserts!
- if (!AFS::isNullPath(j->getAbstractPath<LEFT_SIDE>())) //empty folder is always dependent => exclude!
- dirReadWriteCount[j->getAbstractPath<LEFT_SIDE>()];
- if (!AFS::isNullPath(j->getAbstractPath<RIGHT_SIDE>()))
- dirReadWriteCount[j->getAbstractPath<RIGHT_SIDE>()];
- }
+ if (!AFS::isNullPath(baseFolder.getAbstractPath<LEFT_SIDE>())) //empty folder is always dependent => exclude!
+ dirReadWriteCount[baseFolder.getAbstractPath<LEFT_SIDE>()];
+ if (!AFS::isNullPath(baseFolder.getAbstractPath<RIGHT_SIDE>()))
+ dirReadWriteCount[baseFolder.getAbstractPath<RIGHT_SIDE>()];
+ });
auto incReadCount = [&](const AbstractPath& baseFolderPath)
{
@@ -1936,9 +1947,10 @@ void zen::synchronize(const TimeComp& timeStamp,
std::map<AbstractPath, bool, AFS::LessAbstractPath> recyclerSupported; //expensive to determine on Win XP => buffer + check recycle bin existence only once per base folder!
//start checking folder pairs
- for (auto j = begin(folderCmp); j != end(folderCmp); ++j)
+ for (auto itBase = begin(folderCmp); itBase != end(folderCmp); ++itBase)
{
- const size_t folderIndex = j - begin(folderCmp);
+ BaseFolderPair& baseFolder = *itBase;
+ const size_t folderIndex = itBase - begin(folderCmp);
const FolderPairSyncCfg& folderPairCfg = syncConfig [folderIndex];
const SyncStatistics& folderPairStat = folderPairStats[folderIndex];
@@ -1946,13 +1958,21 @@ void zen::synchronize(const TimeComp& timeStamp,
append(unresolvedConflicts, folderPairStat.getConflicts());
//exclude a few pathological cases (including empty left, right folders)
- if (AFS::equalAbstractPath(j->getAbstractPath< LEFT_SIDE>(),
- j->getAbstractPath<RIGHT_SIDE>()))
+ if (AFS::equalAbstractPath(baseFolder.getAbstractPath< LEFT_SIDE>(),
+ baseFolder.getAbstractPath<RIGHT_SIDE>()))
{
jobType[folderIndex] = FolderPairJobType::SKIP;
continue;
}
+ //skip folder pair if there is nothing to do (except for two-way mode and move-detection, where DB files need to be updated)
+ //-> skip creating (not yet existing) base directories in particular if there's no need
+ if (getCUD(folderPairStat) == 0)
+ {
+ jobType[folderIndex] = FolderPairJobType::ALREADY_IN_SYNC;
+ continue;
+ }
+
const bool writeLeft = folderPairStat.createCount<LEFT_SIDE>() +
folderPairStat.updateCount<LEFT_SIDE>() +
folderPairStat.deleteCount<LEFT_SIDE>() > 0;
@@ -1961,36 +1981,29 @@ void zen::synchronize(const TimeComp& timeStamp,
folderPairStat.updateCount<RIGHT_SIDE>() +
folderPairStat.deleteCount<RIGHT_SIDE>() > 0;
- //skip folder pair if there is nothing to do (except for two-way mode and move-detection, where DB files need to be updated)
- //-> skip creating (not yet existing) base directories in particular if there's no need
- if (!writeLeft && !writeRight)
- {
- jobType[folderIndex] = FolderPairJobType::ALREADY_IN_SYNC;
- continue;
- }
-
//aggregate information of folders used by multiple pairs in read/write access
- if (!AFS::havePathDependency(j->getAbstractPath<LEFT_SIDE>(), j->getAbstractPath<RIGHT_SIDE>()))
+ if (!AFS::havePathDependency(baseFolder.getAbstractPath<LEFT_SIDE >(),
+ baseFolder.getAbstractPath<RIGHT_SIDE>()))
{
if (writeLeft)
- incWriteCount(j->getAbstractPath<LEFT_SIDE>());
+ incWriteCount(baseFolder.getAbstractPath<LEFT_SIDE>());
else if (writeRight)
- incReadCount (j->getAbstractPath<LEFT_SIDE>());
+ incReadCount (baseFolder.getAbstractPath<LEFT_SIDE>());
if (writeRight)
- incWriteCount(j->getAbstractPath<RIGHT_SIDE>());
+ incWriteCount(baseFolder.getAbstractPath<RIGHT_SIDE>());
else if (writeLeft)
- incReadCount (j->getAbstractPath<RIGHT_SIDE>());
+ incReadCount (baseFolder.getAbstractPath<RIGHT_SIDE>());
}
else //if folder pair contains two dependent folders, a warning was already issued after comparison; in this context treat as one write access at most
{
if (writeLeft || writeRight)
- incWriteCount(j->getAbstractPath<LEFT_SIDE>());
+ incWriteCount(baseFolder.getAbstractPath<LEFT_SIDE>());
}
//check for empty target folder paths: this only makes sense if empty field is source (and no DB files need to be created)
- if ((AFS::isNullPath(j->getAbstractPath< LEFT_SIDE>()) && (writeLeft || folderPairCfg.saveSyncDB_)) ||
- (AFS::isNullPath(j->getAbstractPath<RIGHT_SIDE>()) && (writeRight || folderPairCfg.saveSyncDB_)))
+ if ((AFS::isNullPath(baseFolder.getAbstractPath< LEFT_SIDE>()) && (writeLeft || folderPairCfg.saveSyncDB_)) ||
+ (AFS::isNullPath(baseFolder.getAbstractPath<RIGHT_SIDE>()) && (writeRight || folderPairCfg.saveSyncDB_)))
{
callback.reportFatalError(_("Target folder input field must not be empty."));
jobType[folderIndex] = FolderPairJobType::SKIP;
@@ -2000,38 +2013,37 @@ void zen::synchronize(const TimeComp& timeStamp,
//check for network drops after comparison
// - convenience: exit sync right here instead of showing tons of errors during file copy
// - early failure! there's no point in evaluating subsequent warnings
- if (baseFolderDrop< LEFT_SIDE>(*j, folderAccessTimeout, callback) ||
- baseFolderDrop<RIGHT_SIDE>(*j, folderAccessTimeout, callback))
+ if (baseFolderDrop< LEFT_SIDE>(baseFolder, folderAccessTimeout, callback) ||
+ baseFolderDrop<RIGHT_SIDE>(baseFolder, folderAccessTimeout, callback))
{
jobType[folderIndex] = FolderPairJobType::SKIP;
continue;
}
//allow propagation of deletions only from *null-* or *existing* source folder:
- auto sourceFolderMissing = [&](const AbstractPath& baseFolder, bool wasExisting) //we need to evaluate existence status from time of comparison!
+ auto sourceFolderMissing = [&](const AbstractPath& baseFolderPath, bool wasExisting) //we need to evaluate existence status from time of comparison!
{
- if (!AFS::isNullPath(baseFolder))
+ if (!AFS::isNullPath(baseFolderPath))
//PERMANENT network drop: avoid data loss when source directory is not found AND user chose to ignore errors (else we wouldn't arrive here)
if (folderPairStat.deleteCount() > 0) //check deletions only... (respect filtered items!)
//folderPairStat.conflictCount() == 0 && -> there COULD be conflicts for <Two way> variant if directory existence check fails, but loading sync.ffs_db succeeds
//https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3531351&group_id=234430 -> fixed, but still better not consider conflicts!
if (!wasExisting) //avoid race-condition: we need to evaluate existence status from time of comparison!
{
- callback.reportFatalError(replaceCpy(_("Source folder %x not found."), L"%x", fmtPath(AFS::getDisplayPath(baseFolder))));
+ callback.reportFatalError(replaceCpy(_("Source folder %x not found."), L"%x", fmtPath(AFS::getDisplayPath(baseFolderPath))));
return true;
}
return false;
};
- if (sourceFolderMissing(j->getAbstractPath< LEFT_SIDE>(), j->isExisting< LEFT_SIDE>()) ||
- sourceFolderMissing(j->getAbstractPath<RIGHT_SIDE>(), j->isExisting<RIGHT_SIDE>()))
+ if (sourceFolderMissing(baseFolder.getAbstractPath< LEFT_SIDE>(), baseFolder.isExisting< LEFT_SIDE>()) ||
+ sourceFolderMissing(baseFolder.getAbstractPath<RIGHT_SIDE>(), baseFolder.isExisting<RIGHT_SIDE>()))
{
jobType[folderIndex] = FolderPairJobType::SKIP;
continue;
}
//check if user-defined directory for deletion was specified
- if (folderPairCfg.handleDeletion == DeletionPolicy::VERSIONING &&
- folderPairStat.updateCount() + folderPairStat.deleteCount() > 0)
+ if (folderPairCfg.handleDeletion == DeletionPolicy::VERSIONING)
{
if (trimCpy(folderPairCfg.versioningFolderPhrase).empty())
{
@@ -2043,11 +2055,11 @@ void zen::synchronize(const TimeComp& timeStamp,
}
//check if more than 50% of total number of files/dirs are to be created/overwritten/deleted
- if (!AFS::isNullPath(j->getAbstractPath< LEFT_SIDE>()) &&
- !AFS::isNullPath(j->getAbstractPath<RIGHT_SIDE>()))
+ if (!AFS::isNullPath(baseFolder.getAbstractPath< LEFT_SIDE>()) &&
+ !AFS::isNullPath(baseFolder.getAbstractPath<RIGHT_SIDE>()))
if (significantDifferenceDetected(folderPairStat))
- significantDiffPairs.emplace_back(j->getAbstractPath< LEFT_SIDE>(),
- j->getAbstractPath<RIGHT_SIDE>());
+ significantDiffPairs.emplace_back(baseFolder.getAbstractPath< LEFT_SIDE>(),
+ baseFolder.getAbstractPath<RIGHT_SIDE>());
//check for sufficient free diskspace
auto checkSpace = [&](const AbstractPath& baseFolderPath, std::int64_t minSpaceNeeded)
@@ -2063,9 +2075,9 @@ void zen::synchronize(const TimeComp& timeStamp,
}
catch (FileError&) {} //for warning only => no need for tryReportingError()
};
- const std::pair<std::int64_t, std::int64_t> spaceNeeded = MinimumDiskSpaceNeeded::calculate(*j);
- checkSpace(j->getAbstractPath< LEFT_SIDE>(), spaceNeeded.first);
- checkSpace(j->getAbstractPath<RIGHT_SIDE>(), spaceNeeded.second);
+ const std::pair<std::int64_t, std::int64_t> spaceNeeded = MinimumDiskSpaceNeeded::calculate(baseFolder);
+ checkSpace(baseFolder.getAbstractPath< LEFT_SIDE>(), spaceNeeded.first);
+ checkSpace(baseFolder.getAbstractPath<RIGHT_SIDE>(), spaceNeeded.second);
//windows: check if recycle bin really exists; if not, Windows will silently delete, which is wrong
auto checkRecycler = [&](const AbstractPath& baseFolderPath)
@@ -2088,13 +2100,11 @@ void zen::synchronize(const TimeComp& timeStamp,
if (folderPairCfg.handleDeletion == DeletionPolicy::RECYCLER)
{
- if (folderPairStat.updateCount<LEFT_SIDE>() +
- folderPairStat.deleteCount<LEFT_SIDE>() > 0)
- checkRecycler(j->getAbstractPath<LEFT_SIDE>());
+ if (folderPairStat.expectPhysicalDeletion<LEFT_SIDE>())
+ checkRecycler(baseFolder.getAbstractPath<LEFT_SIDE>());
- if (folderPairStat.updateCount<RIGHT_SIDE>() +
- folderPairStat.deleteCount<RIGHT_SIDE>() > 0)
- checkRecycler(j->getAbstractPath<RIGHT_SIDE>());
+ if (folderPairStat.expectPhysicalDeletion<RIGHT_SIDE>())
+ checkRecycler(baseFolder.getAbstractPath<RIGHT_SIDE>());
}
}
@@ -2104,7 +2114,7 @@ void zen::synchronize(const TimeComp& timeStamp,
std::wstring msg = _("The following items have unresolved conflicts and will not be synchronized:");
for (const SyncStatistics::ConflictInfo& item : unresolvedConflicts) //show *all* conflicts in warning message
- msg += L"\n\n" + fmtPath(item.first) + L": " + item.second;
+ msg += L"\n\n" + fmtPath(item.relPath) + L": " + item.msg;
callback.reportWarning(msg, warnings.warningUnresolvedConflicts);
}
@@ -2176,9 +2186,10 @@ void zen::synchronize(const TimeComp& timeStamp,
try
{
//loop through all directory pairs
- for (auto j = begin(folderCmp); j != end(folderCmp); ++j)
+ for (auto itBase = begin(folderCmp); itBase != end(folderCmp); ++itBase)
{
- const size_t folderIndex = j - begin(folderCmp);
+ BaseFolderPair& baseFolder = *itBase;
+ const size_t folderIndex = itBase - begin(folderCmp);
const FolderPairSyncCfg& folderPairCfg = syncConfig [folderIndex];
const SyncStatistics& folderPairStat = folderPairStats[folderIndex];
@@ -2187,19 +2198,19 @@ void zen::synchronize(const TimeComp& timeStamp,
//------------------------------------------------------------------------------------------
callback.reportInfo(_("Synchronizing folder pair:") + L" [" + getVariantName(folderPairCfg.syncVariant_) + L"]\n" +
- L" " + AFS::getDisplayPath(j->getAbstractPath< LEFT_SIDE>()) + L"\n" +
- L" " + AFS::getDisplayPath(j->getAbstractPath<RIGHT_SIDE>()));
+ L" " + AFS::getDisplayPath(baseFolder.getAbstractPath< LEFT_SIDE>()) + L"\n" +
+ L" " + AFS::getDisplayPath(baseFolder.getAbstractPath<RIGHT_SIDE>()));
//------------------------------------------------------------------------------------------
//checking a second time: (a long time may have passed since the intro checks!)
- if (baseFolderDrop< LEFT_SIDE>(*j, folderAccessTimeout, callback) ||
- baseFolderDrop<RIGHT_SIDE>(*j, folderAccessTimeout, callback))
+ if (baseFolderDrop< LEFT_SIDE>(baseFolder, folderAccessTimeout, callback) ||
+ baseFolderDrop<RIGHT_SIDE>(baseFolder, folderAccessTimeout, callback))
continue;
//create base folders if not yet existing
if (folderPairStat.createCount() > 0 || folderPairCfg.saveSyncDB_) //else: temporary network drop leading to deletions already caught by "sourceFolderMissing" check!
- if (!createBaseFolder< LEFT_SIDE>(*j, callback) || //+ detect temporary network drop!!
- !createBaseFolder<RIGHT_SIDE>(*j, callback)) //
+ if (!createBaseFolder< LEFT_SIDE>(baseFolder, callback) || //+ detect temporary network drop!!
+ !createBaseFolder<RIGHT_SIDE>(baseFolder, callback)) //
continue;
//------------------------------------------------------------------------------------------
@@ -2211,7 +2222,7 @@ void zen::synchronize(const TimeComp& timeStamp,
try
{
if (folderPairCfg.saveSyncDB_)
- zen::saveLastSynchronousState(*j, nullptr);
+ zen::saveLastSynchronousState(baseFolder, nullptr);
} //throw FileError
catch (FileError&) {}
);
@@ -2219,15 +2230,16 @@ void zen::synchronize(const TimeComp& timeStamp,
if (jobType[folderIndex] == FolderPairJobType::PROCESS)
{
//guarantee removal of invalid entries (where element is empty on both sides)
- ZEN_ON_SCOPE_EXIT(BaseFolderPair::removeEmpty(*j));
+ ZEN_ON_SCOPE_EXIT(BaseFolderPair::removeEmpty(baseFolder));
bool copyPermissionsFp = false;
tryReportingError([&]
{
copyPermissionsFp = copyFilePermissions && //copy permissions only if asked for and supported by *both* sides!
- !AFS::isNullPath(j->getAbstractPath< LEFT_SIDE>()) && //scenario: directory selected on one side only
- !AFS::isNullPath(j->getAbstractPath<RIGHT_SIDE>()) && //
- AFS::supportPermissionCopy(j->getAbstractPath<LEFT_SIDE>(), j->getAbstractPath<RIGHT_SIDE>()); //throw FileError
+ !AFS::isNullPath(baseFolder.getAbstractPath< LEFT_SIDE>()) && //scenario: directory selected on one side only
+ !AFS::isNullPath(baseFolder.getAbstractPath<RIGHT_SIDE>()) && //
+ AFS::supportPermissionCopy(baseFolder.getAbstractPath<LEFT_SIDE>(),
+ baseFolder.getAbstractPath<RIGHT_SIDE>()); //throw FileError
}, callback); //throw X?
@@ -2244,15 +2256,15 @@ void zen::synchronize(const TimeComp& timeStamp,
};
- DeletionHandling delHandlerL(j->getAbstractPath<LEFT_SIDE>(),
- getEffectiveDeletionPolicy(j->getAbstractPath<LEFT_SIDE>()),
+ DeletionHandling delHandlerL(baseFolder.getAbstractPath<LEFT_SIDE>(),
+ getEffectiveDeletionPolicy(baseFolder.getAbstractPath<LEFT_SIDE>()),
folderPairCfg.versioningFolderPhrase,
folderPairCfg.versioningStyle_,
timeStamp,
callback);
- DeletionHandling delHandlerR(j->getAbstractPath<RIGHT_SIDE>(),
- getEffectiveDeletionPolicy(j->getAbstractPath<RIGHT_SIDE>()),
+ DeletionHandling delHandlerR(baseFolder.getAbstractPath<RIGHT_SIDE>(),
+ getEffectiveDeletionPolicy(baseFolder.getAbstractPath<RIGHT_SIDE>()),
folderPairCfg.versioningFolderPhrase,
folderPairCfg.versioningStyle_,
timeStamp,
@@ -2264,7 +2276,7 @@ void zen::synchronize(const TimeComp& timeStamp,
shadowCopyHandler.get(),
#endif
delHandlerL, delHandlerR);
- syncFP.startSync(*j);
+ 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?
@@ -2282,7 +2294,7 @@ void zen::synchronize(const TimeComp& timeStamp,
tryReportingError([&]
{
std::int64_t bytesWritten = 0;
- zen::saveLastSynchronousState(*j, [&](std::int64_t bytesDelta) //throw FileError
+ zen::saveLastSynchronousState(baseFolder, [&](std::int64_t bytesDelta) //throw FileError
{
bytesWritten += bytesDelta;
callback.reportStatus(dbUpdateMsg + L" (" + filesizeToShortString(bytesWritten) + L")"); //throw X
diff --git a/FreeFileSync/Source/synchronization.h b/FreeFileSync/Source/synchronization.h
index db9b3308..3992cda0 100644
--- a/FreeFileSync/Source/synchronization.h
+++ b/FreeFileSync/Source/synchronization.h
@@ -35,12 +35,19 @@ public:
int deleteCount() const { return SelectParam<side>::ref(deleteLeft_, deleteRight_); }
int deleteCount() const { return deleteLeft_ + deleteRight_; }
+ template <SelectedSide side>
+ bool expectPhysicalDeletion() const { return SelectParam<side>::ref(physicalDeleteLeft_, physicalDeleteRight_); }
+
int conflictCount() const { return static_cast<int>(conflictMsgs_.size()); }
std::int64_t getBytesToProcess() const { return bytesToProcess_; }
size_t rowCount () const { return rowsTotal_; }
- using ConflictInfo = std::pair<Zstring, std::wstring>; //pair(filePath/conflict message)
+ struct ConflictInfo
+ {
+ Zstring relPath;
+ std::wstring msg;
+ };
const std::vector<ConflictInfo>& getConflicts() const { return conflictMsgs_; }
private:
@@ -56,6 +63,8 @@ private:
int updateRight_ = 0;
int deleteLeft_ = 0;
int deleteRight_ = 0;
+ bool physicalDeleteLeft_ = false; //at least 1 item will be deleted; considers most "update" cases which also delete items
+ bool physicalDeleteRight_ = false; //
std::vector<ConflictInfo> conflictMsgs_; //conflict texts to display as a warning message
std::int64_t bytesToProcess_ = 0;
size_t rowsTotal_ = 0;
diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp
index d9438dd4..76fe7b10 100644
--- a/FreeFileSync/Source/ui/batch_status_handler.cpp
+++ b/FreeFileSync/Source/ui/batch_status_handler.cpp
@@ -6,6 +6,7 @@
#include "batch_status_handler.h"
#include <zen/shell_execute.h>
+#include <zen/thread.h>
#include <wx+/popup_dlg.h>
#include <wx/app.h>
#include "on_completion_box.h"
@@ -143,25 +144,22 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress,
const xmlAccess::OnError handleError,
size_t automaticRetryCount,
size_t automaticRetryDelay,
- const SwitchToGui& switchBatchToGui, //functionality to change from batch mode to GUI mode
FfsReturnCode& returnCode,
const Zstring& onCompletion,
std::vector<Zstring>& onCompletionHistory) :
- switchBatchToGui_(switchBatchToGui),
- showFinalResults(showProgress), //=> exit immediately or wait when finished
+ showFinalResults_(showProgress), //=> exit immediately or wait when finished
logfilesCountLimit_(logfilesCountLimit),
lastSyncsLogFileSizeMax_(lastSyncsLogFileSizeMax),
handleError_(handleError),
returnCode_(returnCode),
automaticRetryCount_(automaticRetryCount),
automaticRetryDelay_(automaticRetryDelay),
- progressDlg(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, nullptr, showProgress, jobName, soundFileSyncComplete, onCompletion, onCompletionHistory)),
- jobName_(jobName),
- timeStamp_(timeStamp),
- startTime_(std::time(nullptr)),
- logFolderPathPhrase_(logFolderPathPhrase)
+ progressDlg_(createProgressDialog(*this, [this] { this->onProgressDialogTerminate(); }, *this, nullptr, showProgress, jobName, soundFileSyncComplete, onCompletion, onCompletionHistory)),
+ jobName_(jobName),
+ timeStamp_(timeStamp),
+ logFolderPathPhrase_(logFolderPathPhrase)
{
- //ATTENTION: "progressDlg" is an unmanaged resource!!! However, at this point we already consider construction complete! =>
+ //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,28 +174,21 @@ BatchStatusHandler::~BatchStatusHandler()
//------------ "on completion" command conceptually is part of the sync, not cleanup --------------------------------------
//decide whether to stay on status screen or exit immediately...
- if (switchToGuiRequested) //-> avoid recursive yield() calls, thous switch not before ending batch mode
+ if (progressDlg_)
{
- try
- {
- switchBatchToGui_.execute(); //open FreeFileSync GUI
- }
- catch (...) { assert(false); }
- showFinalResults = false;
- }
- else if (progressDlg)
- {
- if (progressDlg->getWindowIfVisible())
- showFinalResults = true;
+ if (switchToGuiRequested_) //-> avoid recursive yield() calls, thous switch not before ending batch mode
+ showFinalResults_ = false;
+ else if (progressDlg_->getWindowIfVisible())
+ showFinalResults_ = true;
//execute "on completion" command (even in case of ignored errors)
if (!abortIsRequested()) //if aborted (manually), we don't execute the command
{
- const Zstring finalCommand = progressDlg->getExecWhenFinishedCommand(); //final value (after possible user modification)
+ const Zstring finalCommand = progressDlg_->getExecWhenFinishedCommand(); //final value (after possible user modification)
if (!finalCommand.empty())
{
if (isCloseProgressDlgCommand(finalCommand))
- showFinalResults = false; //take precedence over current visibility status
+ showFinalResults_ = false; //take precedence over current visibility status
else
try
{
@@ -211,8 +202,8 @@ BatchStatusHandler::~BatchStatusHandler()
}
//------------ end of sync: begin of cleanup --------------------------------------
- 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(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log
+ const int totalWarnings = errorLog_.getItemCount(TYPE_WARNING);
//finalize error log
std::wstring status; //additionally indicate errors in log file name
@@ -221,21 +212,21 @@ BatchStatusHandler::~BatchStatusHandler()
{
raiseReturnCode(returnCode_, FFS_RC_ABORTED);
finalStatusMsg = _("Synchronization stopped");
- errorLog.logMsg(finalStatusMsg, TYPE_ERROR);
+ errorLog_.logMsg(finalStatusMsg, TYPE_ERROR);
status = _("Stopped");
}
else if (totalErrors > 0)
{
raiseReturnCode(returnCode_, FFS_RC_FINISHED_WITH_ERRORS);
finalStatusMsg = _("Synchronization completed with errors");
- errorLog.logMsg(finalStatusMsg, TYPE_ERROR);
+ errorLog_.logMsg(finalStatusMsg, TYPE_ERROR);
status = _("Error");
}
else if (totalWarnings > 0)
{
raiseReturnCode(returnCode_, FFS_RC_FINISHED_WITH_WARNINGS);
finalStatusMsg = _("Synchronization completed with warnings");
- errorLog.logMsg(finalStatusMsg, TYPE_WARNING);
+ errorLog_.logMsg(finalStatusMsg, TYPE_WARNING);
status = _("Warning");
}
else
@@ -245,7 +236,7 @@ BatchStatusHandler::~BatchStatusHandler()
finalStatusMsg = _("Nothing to synchronize"); //even if "ignored conflicts" occurred!
else
finalStatusMsg = _("Synchronization completed successfully");
- errorLog.logMsg(finalStatusMsg, TYPE_INFO);
+ errorLog_.logMsg(finalStatusMsg, TYPE_INFO);
}
const SummaryInfo summary =
@@ -263,7 +254,7 @@ BatchStatusHandler::~BatchStatusHandler()
{
auto requestUiRefreshNoThrow = [&] { try { requestUiRefresh(); /*throw X*/ } catch (...) {} };
- const AbstractPath logFolderPath = createAbstractPath(trimCpy(logFolderPathPhrase_).empty() ? getConfigDir() + Zstr("Logs") : logFolderPathPhrase_); //noexcept
+ const AbstractPath logFolderPath = createAbstractPath(trimCpy(logFolderPathPhrase_).empty() ? getConfigDirPathPf() + Zstr("Logs") : logFolderPathPhrase_); //noexcept
try
{
tryReportingError([&] //errors logged here do not impact final status calculation above! => not a problem!
@@ -272,7 +263,7 @@ BatchStatusHandler::~BatchStatusHandler()
AFS::OutputStream& logFileStream = *rv.first;
const AbstractPath logFilePath = rv.second;
- streamToLogFile(summary, errorLog, logFileStream, OnUpdateLogfileStatusNoThrow(*this, AFS::getDisplayPath(logFilePath))); //throw FileError
+ streamToLogFile(summary, errorLog_, logFileStream, OnUpdateLogfileStatusNoThrow(*this, AFS::getDisplayPath(logFilePath))); //throw FileError
logFileStream.finalize(requestUiRefreshNoThrow); //throw FileError
}, *this); //throw X?
}
@@ -296,37 +287,37 @@ BatchStatusHandler::~BatchStatusHandler()
//----------------- write results into LastSyncs.log------------------------
try
{
- saveToLastSyncsLog(summary, errorLog, lastSyncsLogFileSizeMax_, OnUpdateLogfileStatusNoThrow(*this, utfCvrtTo<std::wstring>(getLastSyncsLogfilePath()))); //throw FileError
+ saveToLastSyncsLog(summary, errorLog_, lastSyncsLogFileSizeMax_, OnUpdateLogfileStatusNoThrow(*this, utfCvrtTo<std::wstring>(getLastSyncsLogfilePath()))); //throw FileError
}
catch (FileError&) { assert(false); }
- if (progressDlg)
+ if (progressDlg_)
{
- if (showFinalResults) //warning: wxWindow::Show() is called within processHasFinished()!
+ if (showFinalResults_) //warning: wxWindow::Show() is called within processHasFinished()!
{
//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
+ //setMainWindow(progressDlg_->getAsWindow()); -> not required anymore since we block waiting until dialog is closed below
- //notify to progressDlg that current process has ended
+ //notify to progressDlg_ that current process has ended
if (abortIsRequested())
- progressDlg->processHasFinished(SyncProgressDialog::RESULT_ABORTED, errorLog); //enable okay and close events
+ progressDlg_->processHasFinished(SyncProgressDialog::RESULT_ABORTED, errorLog_); //enable okay and close events
else if (totalErrors > 0)
- progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_ERROR, errorLog);
+ progressDlg_->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_ERROR, errorLog_);
else if (totalWarnings > 0)
- progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS, errorLog);
+ progressDlg_->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS, errorLog_);
else
- progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS, errorLog);
+ progressDlg_->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS, errorLog_);
}
else
- progressDlg->closeWindowDirectly(); //progressDlg is main window => program will quit directly
+ progressDlg_->closeWindowDirectly(); //progressDlg_ is main window => program will quit directly
//wait until progress dialog notified shutdown via onProgressDialogTerminate()
//-> required since it has our "this" pointer captured in lambda "notifyWindowTerminate"!
//-> nicely manages dialog lifetime
- while (progressDlg)
+ while (progressDlg_)
{
wxTheApp->Yield(); //*first* refresh GUI (removing flicker) before sleeping!
- std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL));
+ std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS));
}
}
}
@@ -335,8 +326,8 @@ BatchStatusHandler::~BatchStatusHandler()
void BatchStatusHandler::initNewPhase(int itemsTotal, std::int64_t bytesTotal, ProcessCallback::Phase phaseID)
{
StatusHandler::initNewPhase(itemsTotal, bytesTotal, phaseID);
- if (progressDlg)
- progressDlg->initNewPhase(); //call after "StatusHandler::initNewPhase"
+ if (progressDlg_)
+ progressDlg_->initNewPhase(); //call after "StatusHandler::initNewPhase"
forceUiRefresh(); //throw ?; OS X needs a full yield to update GUI and get rid of "dummy" texts
}
@@ -346,8 +337,8 @@ void BatchStatusHandler::updateProcessedData(int itemsDelta, std::int64_t bytesD
{
StatusHandler::updateProcessedData(itemsDelta, bytesDelta);
- if (progressDlg)
- progressDlg->notifyProgressChange(); //noexcept
+ if (progressDlg_)
+ progressDlg_->notifyProgressChange(); //noexcept
//note: this method should NOT throw in order to properly allow undoing setting of statistics!
}
@@ -355,13 +346,13 @@ void BatchStatusHandler::updateProcessedData(int itemsDelta, std::int64_t bytesD
void BatchStatusHandler::reportInfo(const std::wstring& text)
{
StatusHandler::reportInfo(text);
- errorLog.logMsg(text, TYPE_INFO);
+ errorLog_.logMsg(text, TYPE_INFO);
}
void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool& warningActive)
{
- errorLog.logMsg(warningMessage, TYPE_WARNING);
+ errorLog_.logMsg(warningMessage, TYPE_WARNING);
if (!warningActive)
return;
@@ -370,12 +361,12 @@ void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool&
{
case xmlAccess::ON_ERROR_POPUP:
{
- if (!progressDlg) abortProcessNow();
- PauseTimers dummy(*progressDlg);
+ if (!progressDlg_) abortProcessNow();
+ PauseTimers dummy(*progressDlg_);
forceUiRefresh();
bool dontWarnAgain = false;
- switch (showConfirmationDialog3(progressDlg->getWindowIfVisible(), DialogInfoType::WARNING, PopupDialogCfg3().
+ switch (showConfirmationDialog3(progressDlg_->getWindowIfVisible(), DialogInfoType::WARNING, PopupDialogCfg3().
setDetailInstructions(warningMessage + L"\n\n" + _("You can switch to FreeFileSync's main window to resolve this issue.")).
setCheckBox(dontWarnAgain, _("&Don't show this warning again"), ConfirmationButton3::DONT_DO_IT),
_("&Ignore"), _("&Switch")))
@@ -385,10 +376,10 @@ void BatchStatusHandler::reportWarning(const std::wstring& warningMessage, bool&
break;
case ConfirmationButton3::DONT_DO_IT: //switch
- errorLog.logMsg(_("Switching to FreeFileSync's main window"), TYPE_INFO);
- switchToGuiRequested = true;
- abortProcessNow();
- break;
+ errorLog_.logMsg(_("Switching to FreeFileSync's main window"), TYPE_INFO);
+ switchToGuiRequested_ = true; //treat as a special kind of cancel
+ requestAbortion(); //
+ throw BatchRequestSwitchToMainDialog();
case ConfirmationButton3::CANCEL:
abortProcessNow();
@@ -412,33 +403,33 @@ 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);
+ 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); //always round down: don't allow for negative remaining time below
+ 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 + 999) / 1000)); //integer round up
- std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL));
+ (1000 * automaticRetryDelay_ - i * UI_UPDATE_INTERVAL_MS + 999) / 1000)); //integer round up
+ std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS));
}
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, TYPE_ERROR); });
switch (handleError_)
{
case xmlAccess::ON_ERROR_POPUP:
{
- if (!progressDlg) abortProcessNow();
- PauseTimers dummy(*progressDlg);
+ if (!progressDlg_) abortProcessNow();
+ PauseTimers dummy(*progressDlg_);
forceUiRefresh();
bool ignoreNextErrors = false;
- switch (showConfirmationDialog3(progressDlg->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg3().
+ switch (showConfirmationDialog3(progressDlg_->getWindowIfVisible(), DialogInfoType::ERROR2, PopupDialogCfg3().
setDetailInstructions(errorMessage).
setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT),
_("&Ignore"), _("&Retry")))
@@ -450,7 +441,7 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er
case ConfirmationButton3::DONT_DO_IT: //retry
guardWriteLog.dismiss();
- errorLog.logMsg(errorMessage + L"\n-> " + _("Retrying operation..."), TYPE_INFO);
+ errorLog_.logMsg(errorMessage + L"\n-> " + _("Retrying operation..."), TYPE_INFO);
return ProcessCallback::RETRY;
case ConfirmationButton3::CANCEL:
@@ -475,18 +466,18 @@ ProcessCallback::Response BatchStatusHandler::reportError(const std::wstring& er
void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage)
{
- errorLog.logMsg(errorMessage, TYPE_FATAL_ERROR);
+ errorLog_.logMsg(errorMessage, TYPE_FATAL_ERROR);
switch (handleError_)
{
case xmlAccess::ON_ERROR_POPUP:
{
- if (!progressDlg) abortProcessNow();
- PauseTimers dummy(*progressDlg);
+ if (!progressDlg_) abortProcessNow();
+ PauseTimers dummy(*progressDlg_);
forceUiRefresh();
bool ignoreNextErrors = false;
- switch (showConfirmationDialog(progressDlg->getWindowIfVisible(), DialogInfoType::ERROR2,
+ switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::ERROR2,
PopupDialogCfg().setTitle(_("Serious Error")).
setDetailInstructions(errorMessage).
setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors")),
@@ -515,20 +506,20 @@ void BatchStatusHandler::reportFatalError(const std::wstring& errorMessage)
void BatchStatusHandler::forceUiRefresh()
{
- if (progressDlg)
- progressDlg->updateGui();
+ if (progressDlg_)
+ progressDlg_->updateGui();
}
void BatchStatusHandler::abortProcessNow()
{
requestAbortion(); //just make sure...
- throw BatchAbortProcess(); //abort can be triggered by progressDlg
+ throw BatchAbortProcess(); //abort can be triggered by progressDlg_
}
void BatchStatusHandler::onProgressDialogTerminate()
{
- //it's responsibility of "progressDlg" to call requestAbortion() when closing dialog
- progressDlg = nullptr;
+ //it's responsibility of "progressDlg_" to call requestAbortion() when closing dialog
+ progressDlg_ = nullptr;
}
diff --git a/FreeFileSync/Source/ui/batch_status_handler.h b/FreeFileSync/Source/ui/batch_status_handler.h
index 774f230d..27ace6ee 100644
--- a/FreeFileSync/Source/ui/batch_status_handler.h
+++ b/FreeFileSync/Source/ui/batch_status_handler.h
@@ -10,18 +10,17 @@
#include <zen/error_log.h>
#include <zen/time.h>
#include "progress_indicator.h"
-#include "switch_to_gui.h"
#include "../lib/status_handler.h"
#include "../lib/process_xml.h"
#include "../lib/return_codes.h"
-//Exception class used to abort the "compare" and "sync" process
+//Exception classes used to abort the "compare" and "sync" process
class BatchAbortProcess {};
-
+class BatchRequestSwitchToMainDialog {};
//BatchStatusHandler(SyncProgressDialog) will internally process Window messages! disable GUI controls to avoid unexpected callbacks!
-class BatchStatusHandler : public zen::StatusHandler //throw BatchAbortProcess
+class BatchStatusHandler : public zen::StatusHandler //throw BatchAbortProcess, BatchRequestSwitchToMainDialog
{
public:
BatchStatusHandler(bool showProgress, //defines: -start minimized and -quit immediately when finished
@@ -34,7 +33,6 @@ public:
const xmlAccess::OnError handleError,
size_t automaticRetryCount,
size_t automaticRetryDelay,
- const zen::SwitchToGui& switchBatchToGui, //functionality to change from batch mode to GUI mode
zen::FfsReturnCode& returnCode,
const Zstring& onCompletion,
std::vector<Zstring>& onCompletionHistory);
@@ -54,23 +52,22 @@ public:
private:
void onProgressDialogTerminate();
- const zen::SwitchToGui& switchBatchToGui_; //functionality to change from batch mode to GUI mode
- bool showFinalResults;
- bool switchToGuiRequested = false;
+ bool showFinalResults_;
+ bool switchToGuiRequested_ = false;
const int logfilesCountLimit_;
const size_t lastSyncsLogFileSizeMax_;
xmlAccess::OnError handleError_;
- zen::ErrorLog errorLog; //list of non-resolved errors and warnings
+ zen::ErrorLog errorLog_; //list of non-resolved errors and warnings
zen::FfsReturnCode& returnCode_;
const size_t automaticRetryCount_;
const size_t automaticRetryDelay_;
- SyncProgressDialog* progressDlg; //managed to have shorter lifetime than this handler!
+ SyncProgressDialog* progressDlg_; //managed to have shorter lifetime than this handler!
const std::wstring jobName_;
const zen::TimeComp timeStamp_;
- const time_t startTime_; //don't use wxStopWatch: may overflow after a few days due to ::QueryPerformanceCounter()
+ const time_t startTime_ = std::time(nullptr); //don't use wxStopWatch: may overflow after a few days due to ::QueryPerformanceCounter()
const Zstring logFolderPathPhrase_;
};
diff --git a/FreeFileSync/Source/ui/column_attr.h b/FreeFileSync/Source/ui/column_attr.h
index d48640bc..2f8daf5f 100644
--- a/FreeFileSync/Source/ui/column_attr.h
+++ b/FreeFileSync/Source/ui/column_attr.h
@@ -16,7 +16,7 @@ enum class ColumnTypeRim
ITEM_PATH,
SIZE,
DATE,
- EXTENSION
+ EXTENSION,
};
struct ColumnAttributeRim
@@ -33,12 +33,12 @@ struct ColumnAttributeRim
inline
std::vector<ColumnAttributeRim> getDefaultColumnAttributesLeft()
{
- return
+ return //harmonize with main_dlg.cpp::onGridLabelContextRim() => expects stretched ITEM_PATH and non-stretched other columns!
{
{ ColumnTypeRim::ITEM_PATH, -80, 1, true }, //stretch to full width and substract sum of fixed-size widths!
+ { ColumnTypeRim::EXTENSION, 60, 0, false },
{ ColumnTypeRim::DATE, 112, 0, false },
{ ColumnTypeRim::SIZE, 80, 0, true },
- { ColumnTypeRim::EXTENSION, 60, 0, false },
};
}
@@ -48,9 +48,9 @@ std::vector<ColumnAttributeRim> getDefaultColumnAttributesRight()
return
{
{ ColumnTypeRim::ITEM_PATH, -80, 1, true },
+ { ColumnTypeRim::EXTENSION, 60, 0, false },
{ ColumnTypeRim::DATE, 112, 0, false },
{ ColumnTypeRim::SIZE, 80, 0, true },
- { ColumnTypeRim::EXTENSION, 60, 0, false },
};
}
@@ -58,7 +58,7 @@ enum class ItemPathFormat
{
FULL_PATH,
RELATIVE_PATH,
- ITEM_NAME
+ ITEM_NAME,
};
const ItemPathFormat defaultItemPathFormatLeftGrid = ItemPathFormat::RELATIVE_PATH;
@@ -77,9 +77,9 @@ enum class ColumnTypeCenter
enum class ColumnTypeNavi
{
+ FOLDER_NAME,
+ ITEM_COUNT,
BYTES,
- DIRECTORY,
- ITEM_COUNT
};
struct ColumnAttributeNavi
@@ -87,7 +87,7 @@ struct ColumnAttributeNavi
ColumnAttributeNavi() {}
ColumnAttributeNavi(ColumnTypeNavi type, int offset, int stretch, bool visible) : type_(type), offset_(offset), stretch_(stretch), visible_(visible) {}
- ColumnTypeNavi type_ = ColumnTypeNavi::DIRECTORY;
+ ColumnTypeNavi type_ = ColumnTypeNavi::FOLDER_NAME;
int offset_ = 0;
int stretch_ = 0;;
bool visible_ = false;
@@ -97,17 +97,17 @@ struct ColumnAttributeNavi
inline
std::vector<ColumnAttributeNavi> getDefaultColumnAttributesNavi()
{
- return
+ return //harmonize with tree_view.cpp::onGridLabelContext() => expects stretched FOLDER_NAME and non-stretched other columns!
{
- { ColumnTypeNavi::DIRECTORY, -120, 1, true }, //stretch to full width and substract sum of fixed size widths
- { ColumnTypeNavi::ITEM_COUNT, 60, 0, true },
- { ColumnTypeNavi::BYTES, 60, 0, true }, //GTK needs a few pixels more width
+ { ColumnTypeNavi::FOLDER_NAME, -120, 1, true }, //stretch to full width and substract sum of fixed size widths
+ { ColumnTypeNavi::ITEM_COUNT, 60, 0, true },
+ { ColumnTypeNavi::BYTES, 60, 0, true }, //GTK needs a few pixels more width
};
}
-const bool naviGridShowPercentageDefault = true;
-const ColumnTypeNavi naviGridLastSortColumnDefault = ColumnTypeNavi::BYTES; //remember sort on navigation panel
-const bool naviGridLastSortAscendingDefault = false; //
+const bool naviGridShowPercentageDefault = true;
+const ColumnTypeNavi naviGridLastSortColumnDefault = ColumnTypeNavi::BYTES; //remember sort on navigation panel
+const bool naviGridLastSortAscendingDefault = false; //
}
#endif //COLUMN_ATTR_H_189467891346732143213
diff --git a/FreeFileSync/Source/ui/custom_grid.cpp b/FreeFileSync/Source/ui/custom_grid.cpp
index 3d068b6b..b47e6de9 100644
--- a/FreeFileSync/Source/ui/custom_grid.cpp
+++ b/FreeFileSync/Source/ui/custom_grid.cpp
@@ -196,6 +196,29 @@ public:
void setItemPathForm(ItemPathFormat fmt) { itemPathFormat = fmt; }
+ void getUnbufferedIconsForPreload(std::vector<std::pair<ptrdiff_t, AbstractPath>>& newLoad) //return (priority, filepath) list
+ {
+ if (iconMgr_)
+ {
+ const auto& rowsOnScreen = getVisibleRows(refGrid());
+ const ptrdiff_t visibleRowCount = rowsOnScreen.second - rowsOnScreen.first;
+
+ //preload icons not yet on screen:
+ const int preloadSize = 2 * std::max<ptrdiff_t>(20, visibleRowCount); //:= sum of lines above and below of visible range to preload
+ //=> use full visible height to handle "next page" command and a minimum of 20 for excessive mouse wheel scrolls
+
+ for (ptrdiff_t i = 0; i < preloadSize; ++i)
+ {
+ const ptrdiff_t currentRow = rowsOnScreen.first - (preloadSize + 1) / 2 + getAlternatingPos(i, visibleRowCount + preloadSize); //for odd preloadSize start one row earlier
+
+ const IconInfo ii = getIconInfo(currentRow);
+ if (ii.type == IconInfo::ICON_PATH)
+ if (!iconMgr_->refIconBuffer().readyForRetrieval(ii.fsObj->template getAbstractPath<side>()))
+ newLoad.emplace_back(i, ii.fsObj->template getAbstractPath<side>()); //insert least-important items on outer rim first
+ }
+ }
+ }
+
void updateNewAndGetUnbufferedIcons(std::vector<AbstractPath>& newLoad) //loads all not yet drawn icons
{
if (iconMgr_)
@@ -229,29 +252,6 @@ public:
}
}
- void getUnbufferedIconsForPreload(std::vector<std::pair<ptrdiff_t, AbstractPath>>& newLoad) //return (priority, filepath) list
- {
- if (iconMgr_)
- {
- const auto& rowsOnScreen = getVisibleRows(refGrid());
- const ptrdiff_t visibleRowCount = rowsOnScreen.second - rowsOnScreen.first;
-
- //preload icons not yet on screen:
- const int preloadSize = 2 * std::max<ptrdiff_t>(20, visibleRowCount); //:= sum of lines above and below of visible range to preload
- //=> use full visible height to handle "next page" command and a minimum of 20 for excessive mouse wheel scrolls
-
- for (ptrdiff_t i = 0; i < preloadSize; ++i)
- {
- const ptrdiff_t currentRow = rowsOnScreen.first - (preloadSize + 1) / 2 + getAlternatingPos(i, visibleRowCount + preloadSize); //for odd preloadSize start one row earlier
-
- const IconInfo ii = getIconInfo(currentRow);
- if (ii.type == IconInfo::ICON_PATH)
- if (!iconMgr_->refIconBuffer().readyForRetrieval(ii.fsObj->template getAbstractPath<side>()))
- newLoad.emplace_back(i, ii.fsObj->template getAbstractPath<side>()); //insert least-important items on outer rim first
- }
- }
- }
-
private:
bool isFailedLoad(size_t row) const { return row < failedLoads.size() ? failedLoads[row] != 0 : false; }
@@ -886,7 +886,7 @@ public:
}
selectionInProgress = false;
- //update highlight and tooltip: on OS X no mouse movement event is generated after a mouse button click (unlike on Windows)
+ //update highlight_ and tooltip: on OS X no mouse movement event is generated after a mouse button click (unlike on Windows)
wxPoint clientPos = refGrid().getMainWin().ScreenToClient(wxGetMousePosition());
onMouseMovement(clientPos);
}
@@ -1635,14 +1635,14 @@ class IconUpdater : private wxEvtHandler //update file icons periodically: use S
public:
IconUpdater(GridDataLeft& provLeft, GridDataRight& provRight, IconBuffer& iconBuffer) : provLeft_(provLeft), provRight_(provRight), iconBuffer_(iconBuffer)
{
- timer.Connect(wxEVT_TIMER, wxEventHandler(IconUpdater::loadIconsAsynchronously), nullptr, this);
+ timer_.Connect(wxEVT_TIMER, wxEventHandler(IconUpdater::loadIconsAsynchronously), nullptr, this);
}
- void start() { if (!timer.IsRunning()) timer.Start(100); } //timer interval in [ms]
+ void start() { if (!timer_.IsRunning()) timer_.Start(100); } //timer interval in [ms]
//don't check too often! give worker thread some time to fetch data
private:
- void stop() { if (timer.IsRunning()) timer.Stop(); }
+ void stop() { if (timer_.IsRunning()) timer_.Stop(); }
void loadIconsAsynchronously(wxEvent& event) //loads all (not yet) drawn icons
{
@@ -1671,7 +1671,7 @@ private:
GridDataLeft& provLeft_;
GridDataRight& provRight_;
IconBuffer& iconBuffer_;
- wxTimer timer;
+ wxTimer timer_;
};
diff --git a/FreeFileSync/Source/ui/folder_selector.cpp b/FreeFileSync/Source/ui/folder_selector.cpp
index 52af6bd8..1e14634d 100644
--- a/FreeFileSync/Source/ui/folder_selector.cpp
+++ b/FreeFileSync/Source/ui/folder_selector.cpp
@@ -199,8 +199,8 @@ void FolderSelector::onFilesDropped(FileDropEvent& event)
setPath(fmtShellPath(itemPaths[0]));
//drop two folder paths at once:
- if (siblingSelector && itemPaths.size() >= 2)
- siblingSelector->setPath(fmtShellPath(itemPaths[1]));
+ if (siblingSelector_ && itemPaths.size() >= 2)
+ siblingSelector_->setPath(fmtShellPath(itemPaths[1]));
//notify action invoked by user
wxCommandEvent dummy(EVENT_ON_FOLDER_SELECTED);
diff --git a/FreeFileSync/Source/ui/folder_selector.h b/FreeFileSync/Source/ui/folder_selector.h
index f521747f..c504efa0 100644
--- a/FreeFileSync/Source/ui/folder_selector.h
+++ b/FreeFileSync/Source/ui/folder_selector.h
@@ -41,7 +41,7 @@ public:
~FolderSelector();
- void setSiblingSelector(FolderSelector* selector) { siblingSelector = selector; }
+ void setSiblingSelector(FolderSelector* selector) { siblingSelector_ = selector; }
Zstring getPath() const;
void setPath(const Zstring& folderPathPhrase);
@@ -61,7 +61,7 @@ private:
wxButton& selectAltFolderButton_;
FolderHistoryBox& folderComboBox_;
wxStaticText* staticText_ = nullptr; //optional
- FolderSelector* siblingSelector = nullptr;
+ FolderSelector* siblingSelector_ = nullptr;
};
}
diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp
index 4ca60bbe..2683f6a4 100644
--- a/FreeFileSync/Source/ui/gui_generated.cpp
+++ b/FreeFileSync/Source/ui/gui_generated.cpp
@@ -80,9 +80,8 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
m_menuTools->AppendSeparator();
- wxMenuItem* m_menuItem15;
- m_menuItem15 = new wxMenuItem( m_menuTools, wxID_FIND, wxString( _("&Find...") ) + wxT('\t') + wxT("Ctrl+F"), wxEmptyString, wxITEM_NORMAL );
- m_menuTools->Append( m_menuItem15 );
+ m_menuItemFind = new wxMenuItem( m_menuTools, wxID_FIND, wxString( _("&Find...") ) + wxT('\t') + wxT("Ctrl+F"), wxEmptyString, wxITEM_NORMAL );
+ m_menuTools->Append( m_menuItemFind );
wxMenuItem* m_menuItem51;
m_menuItem51 = new wxMenuItem( m_menuTools, wxID_ANY, wxString( _("&Reset layout") ) , wxEmptyString, wxITEM_NORMAL );
@@ -98,16 +97,14 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
m_menuItemHelp = new wxMenuItem( m_menuHelp, wxID_HELP, wxString( _("&View help") ) + wxT('\t') + wxT("F1"), wxEmptyString, wxITEM_NORMAL );
m_menuHelp->Append( m_menuItemHelp );
- m_menuCheckVersion = new wxMenu();
- wxMenuItem* m_menuCheckVersionItem = new wxMenuItem( m_menuHelp, wxID_ANY, _("&Check for new version"), wxEmptyString, wxITEM_NORMAL, m_menuCheckVersion );
- m_menuItemCheckVersionNow = new wxMenuItem( m_menuCheckVersion, wxID_ANY, wxString( _("&Check now") ) , wxEmptyString, wxITEM_NORMAL );
- m_menuCheckVersion->Append( m_menuItemCheckVersionNow );
+ m_menuHelp->AppendSeparator();
- m_menuItemCheckVersionAuto = new wxMenuItem( m_menuCheckVersion, wxID_ANY, wxString( _("Check &automatically once a week") ) , wxEmptyString, wxITEM_CHECK );
- m_menuCheckVersion->Append( m_menuItemCheckVersionAuto );
- m_menuItemCheckVersionAuto->Check( true );
+ m_menuItemCheckVersionNow = new wxMenuItem( m_menuHelp, wxID_ANY, wxString( _("&Check for updates now") ) , wxEmptyString, wxITEM_NORMAL );
+ m_menuHelp->Append( m_menuItemCheckVersionNow );
- m_menuHelp->Append( m_menuCheckVersionItem );
+ m_menuItemCheckVersionAuto = new wxMenuItem( m_menuHelp, wxID_ANY, wxString( _("Check &automatically once a week") ) , wxEmptyString, wxITEM_CHECK );
+ m_menuHelp->Append( m_menuItemCheckVersionAuto );
+ m_menuItemCheckVersionAuto->Check( true );
m_menuHelp->AppendSeparator();
@@ -973,7 +970,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
this->Connect( m_menuItemSyncSettings->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnSyncSettings ) );
this->Connect( m_menuItemSynchronize->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ) );
this->Connect( m_menuItemOptions->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuOptions ) );
- this->Connect( m_menuItem15->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuFindItem ) );
+ this->Connect( m_menuItemFind->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuFindItem ) );
this->Connect( m_menuItem51->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuResetLayout ) );
this->Connect( m_menuItem5->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuExportFileList ) );
this->Connect( m_menuItemHelp->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnShowHelp ) );
@@ -1246,7 +1243,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
m_panelCompSettingsHolder->SetSizer( bSizer275 );
m_panelCompSettingsHolder->Layout();
bSizer275->Fit( m_panelCompSettingsHolder );
- m_notebook->AddPage( m_panelCompSettingsHolder, _("dummy"), false );
+ m_notebook->AddPage( m_panelCompSettingsHolder, _("dummy"), true );
m_panelFilterSettingsHolder = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panelFilterSettingsHolder->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
@@ -1464,7 +1461,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
m_staticline46 = new wxStaticLine( m_panelFilterSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
bSizer280->Add( m_staticline46, 0, wxEXPAND, 5 );
- m_buttonClear = new wxButton( m_panelFilterSettingsHolder, wxID_DEFAULT, _("C&lear"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ m_buttonClear = new wxButton( m_panelFilterSettingsHolder, wxID_ANY, _("C&lear"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
bSizer280->Add( m_buttonClear, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 );
@@ -1761,22 +1758,22 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
m_staticText93->Wrap( -1 );
bSizer198->Add( m_staticText93, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 );
+ wxArrayString m_choiceVersioningStyleChoices;
+ m_choiceVersioningStyle = new wxChoice( m_panelVersioning, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceVersioningStyleChoices, 0 );
+ m_choiceVersioningStyle->SetSelection( 0 );
+ bSizer198->Add( m_choiceVersioningStyle, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
bSizer198->Add( 0, 0, 1, wxEXPAND, 5 );
m_hyperlink17 = new wxHyperlinkCtrl( m_panelVersioning, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- bSizer198->Add( m_hyperlink17, 0, 0, 5 );
+ bSizer198->Add( m_hyperlink17, 0, wxALIGN_CENTER_VERTICAL, 5 );
bSizer191->Add( bSizer198, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
bSizer192 = new wxBoxSizer( wxHORIZONTAL );
- wxArrayString m_choiceVersioningStyleChoices;
- m_choiceVersioningStyle = new wxChoice( m_panelVersioning, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceVersioningStyleChoices, 0 );
- m_choiceVersioningStyle->SetSelection( 0 );
- bSizer192->Add( m_choiceVersioningStyle, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
-
m_staticTextNamingCvtPart1 = new wxStaticText( m_panelVersioning, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextNamingCvtPart1->Wrap( -1 );
m_staticTextNamingCvtPart1->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
@@ -1862,7 +1859,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
m_panelSyncSettingsHolder->SetSizer( bSizer276 );
m_panelSyncSettingsHolder->Layout();
bSizer276->Fit( m_panelSyncSettingsHolder );
- m_notebook->AddPage( m_panelSyncSettingsHolder, _("dummy"), true );
+ m_notebook->AddPage( m_panelSyncSettingsHolder, _("dummy"), false );
bSizer190->Add( m_notebook, 1, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 );
@@ -1933,8 +1930,8 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
m_radioBtnRecycler->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnDeletionRecycler ), NULL, this );
m_radioBtnPermanent->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnDeletionPermanent ), NULL, this );
m_radioBtnVersioning->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnDeletionVersioning ), NULL, this );
- m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpVersioning ), NULL, this );
m_choiceVersioningStyle->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeSyncOption ), NULL, this );
+ m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpVersioning ), NULL, this );
m_radioBtnPopupOnErrors->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnErrorPopup ), NULL, this );
m_radioBtnIgnoreErrors->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnErrorIgnore ), NULL, this );
m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnOkay ), NULL, this );
@@ -3823,7 +3820,7 @@ OptionsDlgGenerated::~OptionsDlgGenerated()
{
}
-TooltipDialogGenerated::TooltipDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
+TooltipDlgGenerated::TooltipDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
@@ -3843,7 +3840,7 @@ TooltipDialogGenerated::TooltipDialogGenerated( wxWindow* parent, wxWindowID id,
bSizer158->Fit( this );
}
-TooltipDialogGenerated::~TooltipDialogGenerated()
+TooltipDlgGenerated::~TooltipDlgGenerated()
{
}
@@ -4017,6 +4014,57 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer181->Add( bSizer187, 0, wxALL|wxEXPAND, 5 );
+ m_panelThankYou = new wxPanel( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ m_panelThankYou->SetBackgroundColour( wxColour( 153, 170, 187 ) );
+
+ wxBoxSizer* bSizer1831;
+ bSizer1831 = new wxBoxSizer( wxVERTICAL );
+
+ m_panel391 = new wxPanel( m_panelThankYou, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ m_panel391->SetBackgroundColour( wxColour( 224, 247, 201 ) );
+
+ wxBoxSizer* bSizer1841;
+ bSizer1841 = new wxBoxSizer( wxHORIZONTAL );
+
+
+ bSizer1841->Add( 0, 0, 1, wxEXPAND, 5 );
+
+ m_bitmapThanks = new wxStaticBitmap( m_panel391, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+ bSizer1841->Add( m_bitmapThanks, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+ wxBoxSizer* bSizer1781;
+ bSizer1781 = new wxBoxSizer( wxVERTICAL );
+
+ m_staticTextThanks = new wxStaticText( m_panel391, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE );
+ m_staticTextThanks->Wrap( -1 );
+ m_staticTextThanks->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) );
+ m_staticTextThanks->SetForegroundColour( wxColour( 0, 0, 0 ) );
+
+ bSizer1781->Add( m_staticTextThanks, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );
+
+ m_buttonShowDonationDetails = new wxButton( m_panel391, wxID_ANY, _("Donation details"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_buttonShowDonationDetails->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) );
+
+ bSizer1781->Add( m_buttonShowDonationDetails, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 );
+
+
+ bSizer1841->Add( bSizer1781, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+
+ bSizer1841->Add( 0, 0, 1, wxEXPAND, 5 );
+
+
+ m_panel391->SetSizer( bSizer1841 );
+ m_panel391->Layout();
+ bSizer1841->Fit( m_panel391 );
+ bSizer1831->Add( m_panel391, 0, wxEXPAND|wxALL, 5 );
+
+
+ m_panelThankYou->SetSizer( bSizer1831 );
+ m_panelThankYou->Layout();
+ bSizer1831->Fit( m_panelThankYou );
+ bSizer181->Add( m_panelThankYou, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 );
+
m_panelDonate = new wxPanel( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panelDonate->SetBackgroundColour( wxColour( 153, 170, 187 ) );
@@ -4038,12 +4086,12 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
wxBoxSizer* bSizer178;
bSizer178 = new wxBoxSizer( wxVERTICAL );
- m_staticText83 = new wxStaticText( m_panel39, wxID_ANY, _("If you like FreeFileSync:"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText83->Wrap( -1 );
- m_staticText83->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
- m_staticText83->SetForegroundColour( wxColour( 0, 0, 0 ) );
+ m_staticTextDonate = new wxStaticText( m_panel39, wxID_ANY, _("If you like FreeFileSync:"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE );
+ m_staticTextDonate->Wrap( -1 );
+ m_staticTextDonate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) );
+ m_staticTextDonate->SetForegroundColour( wxColour( 0, 0, 0 ) );
- bSizer178->Add( m_staticText83, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );
+ bSizer178->Add( m_staticTextDonate, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );
m_buttonDonate = new wxButton( m_panel39, wxID_ANY, _("Donate with PayPal"), wxDefaultPosition, wxDefaultSize, 0 );
m_buttonDonate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) );
@@ -4083,7 +4131,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer166->Add( 0, 0, 1, wxEXPAND, 5 );
m_bitmapHomepage = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 );
- m_bitmapHomepage->SetToolTip( _("Homepage") );
+ m_bitmapHomepage->SetToolTip( _("Home page") );
bSizer166->Add( m_bitmapHomepage, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
@@ -4211,6 +4259,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
// Connect Events
this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( AboutDlgGenerated::OnClose ) );
+ m_buttonShowDonationDetails->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AboutDlgGenerated::OnShowDonationDetails ), NULL, this );
m_buttonDonate->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AboutDlgGenerated::OnDonate ), NULL, this );
m_buttonClose->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( AboutDlgGenerated::OnOK ), NULL, this );
}
@@ -4218,3 +4267,244 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
AboutDlgGenerated::~AboutDlgGenerated()
{
}
+
+DownloadProgressDlgGenerated::DownloadProgressDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
+{
+ this->SetSizeHints( wxDefaultSize, wxDefaultSize );
+
+ wxBoxSizer* bSizer24;
+ bSizer24 = new wxBoxSizer( wxVERTICAL );
+
+ wxBoxSizer* bSizer72;
+ bSizer72 = new wxBoxSizer( wxHORIZONTAL );
+
+ m_bitmapDownloading = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
+ bSizer72->Add( m_bitmapDownloading, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 );
+
+ m_staticTextHeader = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0|wxNO_BORDER );
+ m_staticTextHeader->Wrap( 460 );
+ bSizer72->Add( m_staticTextHeader, 0, wxALIGN_CENTER_VERTICAL|wxALL, 10 );
+
+
+ bSizer72->Add( 20, 0, 0, 0, 5 );
+
+
+ bSizer24->Add( bSizer72, 0, 0, 5 );
+
+ wxBoxSizer* bSizer212;
+ bSizer212 = new wxBoxSizer( wxVERTICAL );
+
+ m_gaugeProgress = new wxGauge( this, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL );
+ m_gaugeProgress->SetValue( 0 );
+ bSizer212->Add( m_gaugeProgress, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 );
+
+ m_staticTextDetails = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextDetails->Wrap( -1 );
+ bSizer212->Add( m_staticTextDetails, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
+
+
+ bSizer24->Add( bSizer212, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+
+ m_staticline9 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer24->Add( m_staticline9, 0, wxEXPAND, 5 );
+
+ bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL );
+
+ m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ m_buttonCancel->SetDefault();
+ bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+
+
+ bSizer24->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 );
+
+
+ this->SetSizer( bSizer24 );
+ this->Layout();
+ bSizer24->Fit( this );
+
+ // Connect Events
+ m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DownloadProgressDlgGenerated::OnCancel ), NULL, this );
+}
+
+DownloadProgressDlgGenerated::~DownloadProgressDlgGenerated()
+{
+}
+
+ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
+{
+ this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize );
+ this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
+
+ wxBoxSizer* bSizer54;
+ bSizer54 = new wxBoxSizer( wxVERTICAL );
+
+ m_panel35 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ m_panel35->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+
+ wxBoxSizer* bSizer172;
+ bSizer172 = new wxBoxSizer( wxVERTICAL );
+
+ wxBoxSizer* bSizer165;
+ bSizer165 = new wxBoxSizer( wxHORIZONTAL );
+
+ m_bitmapActivation = new wxStaticBitmap( m_panel35, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ bSizer165->Add( m_bitmapActivation, 0, wxALL, 10 );
+
+
+ bSizer165->Add( 0, 120, 0, 0, 5 );
+
+ wxBoxSizer* bSizer16;
+ bSizer16 = new wxBoxSizer( wxVERTICAL );
+
+
+ bSizer16->Add( 0, 10, 0, 0, 5 );
+
+ m_textCtrlLastError = new wxTextCtrl( m_panel35, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER );
+ bSizer16->Add( m_textCtrlLastError, 1, wxEXPAND, 5 );
+
+
+ bSizer16->Add( 0, 5, 0, 0, 5 );
+
+
+ bSizer165->Add( bSizer16, 1, wxEXPAND, 5 );
+
+
+ bSizer172->Add( bSizer165, 1, wxEXPAND, 5 );
+
+ m_staticTextMain = new wxStaticText( m_panel35, wxID_ANY, _("Activate the FreeFileSync Donation Edition by one of the following methods:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextMain->Wrap( -1 );
+ bSizer172->Add( m_staticTextMain, 0, wxBOTTOM|wxRIGHT|wxLEFT, 10 );
+
+
+ m_panel35->SetSizer( bSizer172 );
+ m_panel35->Layout();
+ bSizer172->Fit( m_panel35 );
+ bSizer54->Add( m_panel35, 1, wxEXPAND, 5 );
+
+ m_staticline181 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer54->Add( m_staticline181, 0, wxEXPAND|wxBOTTOM, 5 );
+
+ m_staticline18111 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer54->Add( m_staticline18111, 0, wxEXPAND|wxTOP, 5 );
+
+ m_panel3511 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ m_panel3511->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+
+ wxBoxSizer* bSizer263;
+ bSizer263 = new wxBoxSizer( wxVERTICAL );
+
+ wxBoxSizer* bSizer234;
+ bSizer234 = new wxBoxSizer( wxHORIZONTAL );
+
+ m_staticText136 = new wxStaticText( m_panel3511, wxID_ANY, _("1. Activate via internet now:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText136->Wrap( -1 );
+ bSizer234->Add( m_staticText136, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
+
+ m_buttonActivateOnline = new wxButton( m_panel3511, wxID_ANY, _("Activate online"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ m_buttonActivateOnline->SetDefault();
+ m_buttonActivateOnline->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+
+ bSizer234->Add( m_buttonActivateOnline, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+
+
+ bSizer263->Add( bSizer234, 0, wxEXPAND|wxALL, 5 );
+
+
+ m_panel3511->SetSizer( bSizer263 );
+ m_panel3511->Layout();
+ bSizer263->Fit( m_panel3511 );
+ bSizer54->Add( m_panel3511, 0, wxEXPAND, 5 );
+
+ m_staticline181111 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer54->Add( m_staticline181111, 0, wxEXPAND|wxBOTTOM, 5 );
+
+ m_staticline181112 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer54->Add( m_staticline181112, 0, wxEXPAND|wxTOP, 5 );
+
+ m_panel351 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ m_panel351->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+
+ wxBoxSizer* bSizer266;
+ bSizer266 = new wxBoxSizer( wxVERTICAL );
+
+ wxBoxSizer* bSizer237;
+ bSizer237 = new wxBoxSizer( wxVERTICAL );
+
+ wxBoxSizer* bSizer236;
+ bSizer236 = new wxBoxSizer( wxHORIZONTAL );
+
+ m_staticText1361 = new wxStaticText( m_panel351, wxID_ANY, _("2. Retrieve an offline activation key from the following URL:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText1361->Wrap( -1 );
+ bSizer236->Add( m_staticText1361, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
+
+ m_buttonCopyUrl = new wxButton( m_panel351, wxID_ANY, _("&Copy to clipboard"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ m_buttonCopyUrl->SetDefault();
+ m_buttonCopyUrl->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) );
+
+ bSizer236->Add( m_buttonCopyUrl, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+
+
+ bSizer237->Add( bSizer236, 0, wxEXPAND, 5 );
+
+ m_textCtrlManualActivationUrl = new wxTextCtrl( m_panel351, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220,-1 ), wxTE_MULTILINE|wxTE_READONLY|wxWANTS_CHARS );
+ m_textCtrlManualActivationUrl->SetMaxLength( 0 );
+ bSizer237->Add( m_textCtrlManualActivationUrl, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+
+ wxBoxSizer* bSizer235;
+ bSizer235 = new wxBoxSizer( wxHORIZONTAL );
+
+ m_staticText13611 = new wxStaticText( m_panel351, wxID_ANY, _("Enter activation key:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText13611->Wrap( -1 );
+ bSizer235->Add( m_staticText13611, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
+
+ m_textCtrlOfflineActivationKey = new wxTextCtrl( m_panel351, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220,-1 ), wxTE_PROCESS_ENTER|wxWANTS_CHARS );
+ m_textCtrlOfflineActivationKey->SetMaxLength( 0 );
+ bSizer235->Add( m_textCtrlOfflineActivationKey, 1, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 );
+
+ m_buttonActivateOffline = new wxButton( m_panel351, wxID_ANY, _("Activate offline"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ m_buttonActivateOffline->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) );
+
+ bSizer235->Add( m_buttonActivateOffline, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
+
+
+ bSizer237->Add( bSizer235, 0, wxEXPAND, 5 );
+
+
+ bSizer266->Add( bSizer237, 0, wxALL|wxEXPAND, 5 );
+
+
+ m_panel351->SetSizer( bSizer266 );
+ m_panel351->Layout();
+ bSizer266->Fit( m_panel351 );
+ bSizer54->Add( m_panel351, 0, wxEXPAND, 5 );
+
+ m_staticline13 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer54->Add( m_staticline13, 0, wxEXPAND, 5 );
+
+ bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL );
+
+ m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 );
+
+
+ bSizer54->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 );
+
+
+ this->SetSizer( bSizer54 );
+ this->Layout();
+ bSizer54->Fit( this );
+
+ this->Centre( wxBOTH );
+
+ // Connect Events
+ this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( ActivationDlgGenerated::OnClose ) );
+ m_buttonActivateOnline->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ActivationDlgGenerated::OnActivateOnline ), NULL, this );
+ m_buttonCopyUrl->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ActivationDlgGenerated::OnCopyUrl ), NULL, this );
+ m_textCtrlOfflineActivationKey->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( ActivationDlgGenerated::OnOfflineActivationEnter ), NULL, this );
+ m_buttonActivateOffline->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ActivationDlgGenerated::OnActivateOffline ), NULL, this );
+ m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ActivationDlgGenerated::OnCancel ), NULL, this );
+}
+
+ActivationDlgGenerated::~ActivationDlgGenerated()
+{
+}
diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h
index 9ab016bf..ac691409 100644
--- a/FreeFileSync/Source/ui/gui_generated.h
+++ b/FreeFileSync/Source/ui/gui_generated.h
@@ -52,6 +52,7 @@ namespace zen { class TripleSplitter; }
#include <wx/animate.h>
#include <wx/grid.h>
#include <wx/calctrl.h>
+#include <wx/gauge.h>
#include "zen/i18n.h"
@@ -82,9 +83,9 @@ protected:
wxMenu* m_menuTools;
wxMenuItem* m_menuItemOptions;
wxMenu* m_menuLanguages;
+ wxMenuItem* m_menuItemFind;
wxMenu* m_menuHelp;
wxMenuItem* m_menuItemHelp;
- wxMenu* m_menuCheckVersion;
wxMenuItem* m_menuItemCheckVersionNow;
wxMenuItem* m_menuItemCheckVersionAuto;
wxMenuItem* m_menuItemAbout;
@@ -373,9 +374,9 @@ protected:
FolderHistoryBox* m_versioningFolderPath;
wxButton* m_buttonSelectVersioningFolder;
wxStaticText* m_staticText93;
+ wxChoice* m_choiceVersioningStyle;
wxHyperlinkCtrl* m_hyperlink17;
wxBoxSizer* bSizer192;
- wxChoice* m_choiceVersioningStyle;
wxStaticText* m_staticTextNamingCvtPart1;
wxStaticText* m_staticTextNamingCvtPart2Bold;
wxStaticText* m_staticTextNamingCvtPart3;
@@ -431,8 +432,8 @@ protected:
virtual void OnDeletionRecycler( wxCommandEvent& event ) { event.Skip(); }
virtual void OnDeletionPermanent( wxCommandEvent& event ) { event.Skip(); }
virtual void OnDeletionVersioning( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnHelpVersioning( wxHyperlinkEvent& event ) { event.Skip(); }
virtual void OnChangeSyncOption( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnHelpVersioning( wxHyperlinkEvent& event ) { event.Skip(); }
virtual void OnErrorPopup( wxCommandEvent& event ) { event.Skip(); }
virtual void OnErrorIgnore( wxCommandEvent& event ) { event.Skip(); }
virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); }
@@ -928,9 +929,9 @@ public:
};
///////////////////////////////////////////////////////////////////////////////
-/// Class TooltipDialogGenerated
+/// Class TooltipDlgGenerated
///////////////////////////////////////////////////////////////////////////////
-class TooltipDialogGenerated : public wxDialog
+class TooltipDlgGenerated : public wxDialog
{
private:
@@ -940,8 +941,8 @@ public:
wxStaticBitmap* m_bitmapLeft;
wxStaticText* m_staticTextMain;
- TooltipDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE );
- ~TooltipDialogGenerated();
+ TooltipDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE );
+ ~TooltipDlgGenerated();
};
@@ -998,10 +999,15 @@ protected:
wxHyperlinkCtrl* m_hyperlink10;
wxHyperlinkCtrl* m_hyperlink18;
wxHyperlinkCtrl* m_hyperlink9;
+ wxPanel* m_panelThankYou;
+ wxPanel* m_panel391;
+ wxStaticBitmap* m_bitmapThanks;
+ wxStaticText* m_staticTextThanks;
+ wxButton* m_buttonShowDonationDetails;
wxPanel* m_panelDonate;
wxPanel* m_panel39;
wxStaticBitmap* m_bitmapDonate;
- wxStaticText* m_staticText83;
+ wxStaticText* m_staticTextDonate;
wxButton* m_buttonDonate;
wxStaticText* m_staticText94;
wxStaticBitmap* m_bitmapHomepage;
@@ -1022,6 +1028,7 @@ protected:
// Virtual event handlers, overide them in your derived class
virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
+ virtual void OnShowDonationDetails( wxCommandEvent& event ) { event.Skip(); }
virtual void OnDonate( wxCommandEvent& event ) { event.Skip(); }
virtual void OnOK( wxCommandEvent& event ) { event.Skip(); }
@@ -1033,4 +1040,77 @@ public:
};
+///////////////////////////////////////////////////////////////////////////////
+/// Class DownloadProgressDlgGenerated
+///////////////////////////////////////////////////////////////////////////////
+class DownloadProgressDlgGenerated : public wxDialog
+{
+private:
+
+protected:
+ wxStaticBitmap* m_bitmapDownloading;
+ wxStaticText* m_staticTextHeader;
+ wxGauge* m_gaugeProgress;
+ wxStaticText* m_staticTextDetails;
+ wxStaticLine* m_staticline9;
+ wxBoxSizer* bSizerStdButtons;
+ wxButton* m_buttonCancel;
+
+ // Virtual event handlers, overide them in your derived class
+ virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); }
+
+
+public:
+
+ DownloadProgressDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0 );
+ ~DownloadProgressDlgGenerated();
+
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/// Class ActivationDlgGenerated
+///////////////////////////////////////////////////////////////////////////////
+class ActivationDlgGenerated : public wxDialog
+{
+private:
+
+protected:
+ wxPanel* m_panel35;
+ wxStaticBitmap* m_bitmapActivation;
+ wxTextCtrl* m_textCtrlLastError;
+ wxStaticText* m_staticTextMain;
+ wxStaticLine* m_staticline181;
+ wxStaticLine* m_staticline18111;
+ wxPanel* m_panel3511;
+ wxStaticText* m_staticText136;
+ wxButton* m_buttonActivateOnline;
+ wxStaticLine* m_staticline181111;
+ wxStaticLine* m_staticline181112;
+ wxPanel* m_panel351;
+ wxStaticText* m_staticText1361;
+ wxButton* m_buttonCopyUrl;
+ wxTextCtrl* m_textCtrlManualActivationUrl;
+ wxStaticText* m_staticText13611;
+ wxTextCtrl* m_textCtrlOfflineActivationKey;
+ wxButton* m_buttonActivateOffline;
+ wxStaticLine* m_staticline13;
+ wxBoxSizer* bSizerStdButtons;
+ wxButton* m_buttonCancel;
+
+ // Virtual event handlers, overide them in your derived class
+ virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
+ virtual void OnActivateOnline( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnCopyUrl( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnOfflineActivationEnter( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnActivateOffline( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); }
+
+
+public:
+
+ ActivationDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("FreeFileSync Donation Edition"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
+ ~ActivationDlgGenerated();
+
+};
+
#endif //__GUI_GENERATED_H__
diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp
index b7427321..be40b2bf 100644
--- a/FreeFileSync/Source/ui/gui_status_handler.cpp
+++ b/FreeFileSync/Source/ui/gui_status_handler.cpp
@@ -362,7 +362,7 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog()
while (progressDlg)
{
wxTheApp->Yield(); //*first* refresh GUI (removing flicker) before sleeping!
- std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL));
+ std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS));
}
}
}
@@ -403,12 +403,12 @@ ProcessCallback::Response StatusHandlerFloatingDialog::reportError(const std::ws
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); //always round down: don't allow for negative remaining time below
+ 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 + 999) / 1000)); //integer round up
- std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL));
+ (1000 * automaticRetryDelay_ - i * UI_UPDATE_INTERVAL_MS + 999) / 1000)); //integer round up
+ std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS));
}
return ProcessCallback::RETRY;
}
diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp
index a4aaf819..4d56a4f7 100644
--- a/FreeFileSync/Source/ui/main_dlg.cpp
+++ b/FreeFileSync/Source/ui/main_dlg.cpp
@@ -346,7 +346,7 @@ xmlAccess::XmlGlobalSettings loadGlobalConfig(const Zstring& globalConfigFile) /
Zstring MainDialog::getLastRunConfigPath()
{
- return zen::getConfigDir() + Zstr("LastRun.ffs_gui");
+ return zen::getConfigDirPathPf() + Zstr("LastRun.ffs_gui");
}
@@ -463,9 +463,7 @@ MainDialog::MainDialog(const Zstring& globalConfigFile,
bool startComparison) :
MainDialogGenerated(nullptr),
globalConfigFile_(globalConfigFile),
- lastRunConfigPath(getLastRunConfigPath()),
- folderHistoryLeft (std::make_shared<FolderHistory>()), //make sure it is always bound
- folderHistoryRight(std::make_shared<FolderHistory>()) //
+ lastRunConfigPath(getLastRunConfigPath())
{
m_folderPathLeft ->init(folderHistoryLeft);
m_folderPathRight->init(folderHistoryRight);
@@ -652,21 +650,11 @@ MainDialog::MainDialog(const Zstring& globalConfigFile,
m_menuItemSynchronize ->SetBitmap(getResourceImage(L"sync_small"));
m_menuItemOptions ->SetBitmap(getResourceImage(L"settings_small"));
+ m_menuItemFind ->SetBitmap(getResourceImage(L"find_small"));
m_menuItemHelp ->SetBitmap(getResourceImage(L"help_small"));
m_menuItemAbout->SetBitmap(getResourceImage(L"about_small"));
-
- if (!manualProgramUpdateRequired())
- {
- m_menuItemCheckVersionNow ->Enable(false);
- m_menuItemCheckVersionAuto->Enable(false);
-
- //wxFormbuilder doesn't give us a wxMenuItem for m_menuCheckVersion, so we need this abomination:
- wxMenuItemList& items = m_menuHelp->GetMenuItems();
- for (auto it = items.begin(); it != items.end(); ++it)
- if ((*it)->GetSubMenu() == m_menuCheckVersion)
- (*it)->Enable(false);
- }
+ m_menuItemCheckVersionNow->SetBitmap(getResourceImage(L"update_check_small"));
//create language selection menu
for (const TranslationInfo& ti : getExistingTranslations())
@@ -686,8 +674,8 @@ MainDialog::MainDialog(const Zstring& globalConfigFile,
if (!globalSettings.gui.lastOnlineVersion.empty() && haveNewerVersionOnline(globalSettings.gui.lastOnlineVersion))
{
auto menu = new wxMenu();
- wxMenuItem* newItem = new wxMenuItem(menu, wxID_ANY, _("&Download"));
- this->Connect(newItem->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnMenuDownloadNewVersion));
+ wxMenuItem* newItem = new wxMenuItem(menu, wxID_ANY, _("&Show details"));
+ this->Connect(newItem->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnMenuUpdateAvailable));
menu->Append(newItem); //pass ownership
m_menubar1->Append(menu, L"\u21D2 " + _("A new version of FreeFileSync is available:") + L" \u2605 " + globalSettings.gui.lastOnlineVersion + L" \u2605");
}
@@ -1619,7 +1607,7 @@ void MainDialog::flashStatusInformation(const wxString& text)
oldStatusMsgs.push_back(m_staticTextStatusCenter->GetLabel());
m_staticTextStatusCenter->SetLabel(text);
- m_staticTextStatusCenter->SetForegroundColour(wxColor(31, 57, 226)); //highlight color: blue
+ m_staticTextStatusCenter->SetForegroundColour(wxColor(31, 57, 226)); //highlight_ color: blue
m_staticTextStatusCenter->SetFont(m_staticTextStatusCenter->GetFont().Bold());
m_panelStatusBar->Layout();
@@ -2494,18 +2482,32 @@ void MainDialog::onGridLabelContextRim(Grid& grid, ColumnTypeRim type, bool left
{
auto colAttr = grid.getColumnConfig();
+ Grid::ColumnAttribute* caItemPath = nullptr;
+ Grid::ColumnAttribute* caToggle = nullptr;
+
for (Grid::ColumnAttribute& ca : colAttr)
- if (ca.type_ == ct)
- {
- ca.visible_ = !ca.visible_;
- grid.setColumnConfig(colAttr);
- return;
- }
+ if (ca.type_ == static_cast<ColumnType>(ColumnTypeRim::ITEM_PATH))
+ caItemPath = &ca;
+ else if (ca.type_ == ct)
+ caToggle = &ca;
+
+ assert(caItemPath && caItemPath->stretch_ > 0 && caItemPath->visible_);
+ assert(caToggle && caToggle->stretch_ == 0);
+
+ if (caItemPath && caToggle)
+ {
+ caToggle->visible_ = !caToggle->visible_;
+
+ //take width of newly visible column from stretched item path column
+ caItemPath->offset_ -= caToggle->visible_ ? caToggle->offset_ : -caToggle->offset_;
+
+ grid.setColumnConfig(colAttr);
+ }
};
if (const GridData* prov = grid.getDataProvider())
for (const Grid::ColumnAttribute& ca : grid.getColumnConfig())
- menu.addCheckBox(prov->getColumnLabel(ca.type_), [ca, toggleColumn] { toggleColumn(ca.type_); },
+ menu.addCheckBox(prov->getColumnLabel(ca.type_), [ct = ca.type_, toggleColumn] { toggleColumn(ct); },
ca.visible_, ca.type_ != static_cast<ColumnType>(ColumnTypeRim::ITEM_PATH)); //do not allow user to hide this column!
//----------------------------------------------------------------------------------------------
menu.addSeparator();
@@ -3785,7 +3787,7 @@ void MainDialog::OnCompare(wxCommandEvent& event)
//play (optional) sound notification
if (!globalCfg.soundFileCompareFinished.empty())
{
- const Zstring soundFile = getResourceDir() + globalCfg.soundFileCompareFinished;
+ const Zstring soundFile = getResourceDirPf() + globalCfg.soundFileCompareFinished;
if (fileExists(soundFile))
wxSound::Play(utfCvrtTo<wxString>(soundFile), wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as batch!
}
@@ -4890,13 +4892,13 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event)
void MainDialog::OnMenuCheckVersion(wxCommandEvent& event)
{
- zen::checkForUpdateNow(this, globalCfg.gui.lastOnlineVersion, globalCfg.gui.lastOnlineChangeLog);
+ zen::checkForUpdateNow(this, globalCfg.gui.lastOnlineVersion);
}
-void MainDialog::OnMenuDownloadNewVersion(wxCommandEvent& event)
+void MainDialog::OnMenuUpdateAvailable(wxCommandEvent& event)
{
- wxLaunchDefaultBrowser(L"http://www.freefilesync.org/get_latest.php");
+ zen::checkForUpdateNow(this, globalCfg.gui.lastOnlineVersion); //show changelog + handle Donation Edition auto-updater (including expiration)
}
@@ -4913,9 +4915,8 @@ void MainDialog::OnMenuCheckVersionAutomatically(wxCommandEvent& event)
{
flashStatusInformation(_("Searching for program updates..."));
//synchronous update check is sufficient here:
- periodicUpdateCheckEval(this, globalCfg.gui.lastUpdateCheck,
- globalCfg.gui.lastOnlineVersion,
- globalCfg.gui.lastOnlineChangeLog, periodicUpdateCheckRunAsync(periodicUpdateCheckPrepare().get()).get());
+ periodicUpdateCheckEval(this, globalCfg.gui.lastUpdateCheck, globalCfg.gui.lastOnlineVersion,
+ periodicUpdateCheckRunAsync(periodicUpdateCheckPrepare().get()).get());
}
}
@@ -4925,21 +4926,19 @@ void MainDialog::OnRegularUpdateCheck(wxIdleEvent& event)
//execute just once per startup!
Disconnect(wxEVT_IDLE, wxIdleEventHandler(MainDialog::OnRegularUpdateCheck), nullptr, this);
- if (manualProgramUpdateRequired())
- if (shouldRunPeriodicUpdateCheck(globalCfg.gui.lastUpdateCheck))
- {
- flashStatusInformation(_("Searching for program updates..."));
+ if (shouldRunPeriodicUpdateCheck(globalCfg.gui.lastUpdateCheck))
+ {
+ flashStatusInformation(_("Searching for program updates..."));
- std::shared_ptr<UpdateCheckResultPrep> resultPrep = periodicUpdateCheckPrepare(); //run on main thread:
+ std::shared_ptr<UpdateCheckResultPrep> resultPrep = periodicUpdateCheckPrepare(); //run on main thread:
- guiQueue.processAsync([resultPrep] { return periodicUpdateCheckRunAsync(resultPrep.get()); }, //run on worker thread: (long-running part of the check)
- [this] (std::shared_ptr<UpdateCheckResult>&& resultAsync)
- {
- periodicUpdateCheckEval(this, globalCfg.gui.lastUpdateCheck,
- globalCfg.gui.lastOnlineVersion,
- globalCfg.gui.lastOnlineChangeLog, resultAsync.get()); //run on main thread:
- });
- }
+ guiQueue.processAsync([resultPrep] { return periodicUpdateCheckRunAsync(resultPrep.get()); }, //run on worker thread: (long-running part of the check)
+ [this] (std::shared_ptr<UpdateCheckResult>&& resultAsync)
+ {
+ periodicUpdateCheckEval(this, globalCfg.gui.lastUpdateCheck, globalCfg.gui.lastOnlineVersion,
+ resultAsync.get()); //run on main thread:
+ });
+ }
}
diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h
index 1bd621c2..0aace163 100644
--- a/FreeFileSync/Source/ui/main_dlg.h
+++ b/FreeFileSync/Source/ui/main_dlg.h
@@ -257,16 +257,16 @@ private:
void OnSearchPanelKeyPressed(wxKeyEvent& event);
//menu events
- void OnMenuOptions (wxCommandEvent& event) override;
- void OnMenuExportFileList(wxCommandEvent& event) override;
- void OnMenuResetLayout (wxCommandEvent& event) override { resetLayout(); }
- void OnMenuFindItem (wxCommandEvent& event) override;
- void OnMenuCheckVersion (wxCommandEvent& event) override;
+ void OnMenuOptions (wxCommandEvent& event) override;
+ void OnMenuExportFileList (wxCommandEvent& event) override;
+ void OnMenuResetLayout (wxCommandEvent& event) override { resetLayout(); }
+ void OnMenuFindItem (wxCommandEvent& event) override;
+ void OnMenuCheckVersion (wxCommandEvent& event) override;
void OnMenuCheckVersionAutomatically(wxCommandEvent& event) override;
- void OnMenuDownloadNewVersion (wxCommandEvent& event);
- void OnMenuAbout (wxCommandEvent& event) override;
- void OnShowHelp (wxCommandEvent& event) override;
- void OnMenuQuit (wxCommandEvent& event) override { Close(); }
+ void OnMenuUpdateAvailable(wxCommandEvent& event);
+ void OnMenuAbout (wxCommandEvent& event) override;
+ void OnShowHelp (wxCommandEvent& event) override;
+ void OnMenuQuit (wxCommandEvent& event) override { Close(); }
void OnMenuLanguageSwitch(wxCommandEvent& event);
@@ -329,8 +329,8 @@ private:
std::int64_t manualTimeSpanFrom = 0;
std::int64_t manualTimeSpanTo = 0; //buffer manual time span selection at session level
- std::shared_ptr<FolderHistory> folderHistoryLeft; //shared by all wxComboBox dropdown controls
- std::shared_ptr<FolderHistory> folderHistoryRight; //always bound!
+ std::shared_ptr<FolderHistory> folderHistoryLeft = std::make_shared<FolderHistory>(); //shared by all wxComboBox dropdown controls
+ std::shared_ptr<FolderHistory> folderHistoryRight = std::make_shared<FolderHistory>(); //always bound!
zen::AsyncGuiQueue guiQueue; //schedule and run long-running tasks asynchronously, but process results on GUI queue
diff --git a/FreeFileSync/Source/ui/on_completion_box.cpp b/FreeFileSync/Source/ui/on_completion_box.cpp
index 898178dc..c38909a3 100644
--- a/FreeFileSync/Source/ui/on_completion_box.cpp
+++ b/FreeFileSync/Source/ui/on_completion_box.cpp
@@ -10,9 +10,9 @@
#include <algorithm>
#include <zen/stl_tools.h>
#include <zen/utf.h>
-#ifdef ZEN_WIN
- #include <zen/win_ver.h>
-#endif
+//#ifdef ZEN_WIN
+// #include <zen/win_ver.h>
+//#endif
using namespace zen;
@@ -31,19 +31,16 @@ std::vector<std::pair<std::wstring, Zstring>> getDefaultCommands() //(gui name/c
auto addEntry = [&](const std::wstring& name, const Zstring& value) { output.emplace_back(name, value); };
#ifdef ZEN_WIN
- if (zen::vistaOrLater())
- {
- addEntry(_("Log off" ), Zstr("shutdown /l"));
- addEntry(_("Standby" ), Zstr("rundll32.exe powrprof.dll,SetSuspendState Sleep")); //suspend/Suspend to RAM/sleep
- addEntry(_("Shut down"), Zstr("shutdown /s /t 60"));
- }
- else //XP
- {
- addEntry(_("Log off" ), Zstr("shutdown -l"));
- addEntry(_("Standby" ), Zstr("rundll32.exe powrprof.dll,SetSuspendState")); //this triggers standby OR hibernate, depending on whether hibernate setting is active!
- addEntry(_("Shut down"), Zstr("shutdown -s -t 60"));
- //no suspend on XP?
- }
+#ifdef ZEN_WIN_VISTA_AND_LATER
+ addEntry(_("Log off" ), Zstr("shutdown /l"));
+ addEntry(_("Standby" ), Zstr("rundll32.exe powrprof.dll,SetSuspendState Sleep")); //suspend/Suspend to RAM/sleep
+ addEntry(_("Shut down"), Zstr("shutdown /s /t 60"));
+#else //XP
+ addEntry(_("Log off" ), Zstr("shutdown -l"));
+ addEntry(_("Standby" ), Zstr("rundll32.exe powrprof.dll,SetSuspendState")); //this triggers standby OR hibernate, depending on whether hibernate setting is active!
+ addEntry(_("Shut down"), Zstr("shutdown -s -t 60"));
+ //no suspend on XP?
+#endif
#elif defined ZEN_LINUX
addEntry(_("Log off" ), Zstr("gnome-session-quit --no-prompt")); //alternative requiring admin: sudo killall Xorg
diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp
index c5cc1f82..93e721ea 100644
--- a/FreeFileSync/Source/ui/progress_indicator.cpp
+++ b/FreeFileSync/Source/ui/progress_indicator.cpp
@@ -68,6 +68,7 @@ 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!
//http://www.freefilesync.org/forum/viewtopic.php?t=1426
class StopWatch
@@ -75,41 +76,42 @@ class StopWatch
public:
void pause()
{
- if (!paused)
+ if (!paused_)
{
- paused = true;
- elapsedUntilPause += numeric::dist(startTime, wxGetUTCTimeMillis().GetValue());
+ paused_ = true;
+ elapsedUntilPause_ += std::chrono::system_clock::now() - startTime_;
}
}
void resume()
{
- if (paused)
+ if (paused_)
{
- paused = false;
- startTime = wxGetUTCTimeMillis().GetValue();
+ paused_ = false;
+ startTime_ = std::chrono::system_clock::now();
}
}
void restart()
{
- startTime = wxGetUTCTimeMillis().GetValue(); //uses ::GetSystemTimeAsFileTime()
- paused = false;
- elapsedUntilPause = 0;
+ paused_ = false;
+ startTime_ = std::chrono::system_clock::now();
+ elapsedUntilPause_ = std::chrono::nanoseconds::zero();
}
int64_t timeMs() const
{
- int64_t msTotal = elapsedUntilPause;
- if (!paused)
- msTotal += numeric::dist(startTime, wxGetUTCTimeMillis().GetValue());
- return msTotal;
+ auto elapsedTotal = elapsedUntilPause_;
+ if (!paused_)
+ elapsedTotal += std::chrono::system_clock::now() - startTime_;
+
+ return std::chrono::duration_cast<std::chrono::milliseconds>(elapsedTotal).count();
}
private:
- wxLongLong_t startTime = wxGetUTCTimeMillis().GetValue(); //alas not a steady clock, but something's got to give!
- bool paused = false;
- int64_t elapsedUntilPause = 0;
+ bool paused_ = false;
+ std::chrono::system_clock::time_point startTime_ = std::chrono::system_clock::now(); //uses ::GetSystemTimePreciseAsFileTime()
+ std::chrono::nanoseconds elapsedUntilPause_{}; //std::chrono::duration is uninitialized by default! WTF! When will this stupidity end???
};
@@ -1020,7 +1022,7 @@ public:
lastSample = std::make_pair(timeNowMs, value);
- //allow for at most one sample per 100ms (handles duplicate inserts, too!) => this is unrelated to UI_UPDATE_INTERVAL!
+ //allow for at most one sample per 100ms (handles duplicate inserts, too!) => this is unrelated to UI_UPDATE_INTERVAL_MS!
if (!samples.empty()) //always unconditionally insert first sample!
if (timeNowMs / 100 == samples.rbegin()->first / 100)
return;
@@ -1765,7 +1767,7 @@ void SyncProgressDialogImpl<TopLevelDialog>::updateGuiInt(bool allowYield)
{
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));
+ std::this_thread::sleep_for(std::chrono::milliseconds(UI_UPDATE_INTERVAL_MS));
}
//after SyncProgressDialogImpl::OnClose() called wxWindow::Destroy() on OS X this instance is instantly toast!
if (wereDead)
@@ -2047,7 +2049,7 @@ void SyncProgressDialogImpl<TopLevelDialog>::processHasFinished(SyncResult resul
case SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS:
if (!soundFileSyncComplete_.empty())
{
- const Zstring soundFile = getResourceDir() + soundFileSyncComplete_;
+ const Zstring soundFile = getResourceDirPf() + soundFileSyncComplete_;
if (fileExists(soundFile))
wxSound::Play(utfCvrtTo<wxString>(soundFile), wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as batch!
}
diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp
index c59bf681..fdc28196 100644
--- a/FreeFileSync/Source/ui/small_dlgs.cpp
+++ b/FreeFileSync/Source/ui/small_dlgs.cpp
@@ -11,6 +11,7 @@
#include <zen/stl_tools.h>
#include <wx/wupdlock.h>
#include <wx/filedlg.h>
+#include <wx/clipbrd.h>
#include <wx+/choice_enum.h>
#include <wx+/bitmap_button.h>
#include <wx+/rtl.h>
@@ -23,11 +24,13 @@
#include "gui_generated.h"
#include "custom_grid.h"
#include "folder_selector.h"
+#include "version_check.h"
#include "../algorithm.h"
#include "../synchronization.h"
#include "../lib/help_provider.h"
#include "../lib/hard_filter.h"
#include "../version/version.h"
+#include "../lib/status_handler.h" //updateUiIsAllowed()
#ifdef ZEN_WIN
#include <wx+/mouse_move_dlg.h>
@@ -51,6 +54,9 @@ private:
void OnOK (wxCommandEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_OKAY); }
void OnClose (wxCloseEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
void OnDonate(wxCommandEvent& event) override { wxLaunchDefaultBrowser(L"http://www.freefilesync.org/donate.php"); }
+#ifdef ZEN_WIN
+ void OnShowDonationDetails(wxCommandEvent& event) override { openDonationEditionThankYouPage(this); }
+#endif
};
@@ -58,14 +64,43 @@ AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent)
{
setStandardButtonLayout(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonClose));
- setRelativeFontSize(*m_buttonDonate, 1.25);
-
assert(m_buttonClose->GetId() == wxID_OK); //we cannot use wxID_CLOSE else Esc key won't work: yet another wxWidgets bug??
m_bitmapHomepage->SetBitmap(getResourceImage(L"website"));
m_bitmapEmail ->SetBitmap(getResourceImage(L"email"));
m_bitmapGpl ->SetBitmap(getResourceImage(L"gpl"));
- m_bitmapDonate ->SetBitmap(getResourceImage(L"paypal"));
+
+#ifdef ZEN_WIN
+ std::wstring donorFullName;
+ try
+ {
+ if (Opt<DonationInfo> donationInfo = getDonationInfo()) //throw FileError
+ donorFullName = donationInfo->donorFullName;
+ }
+ catch (const FileError& e)
+ {
+ showNotificationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg().
+ setMainInstructions(_("Installation files are corrupted. Please reinstall FreeFileSync.")).
+ setDetailInstructions(e.toString()));
+ }
+
+ if (!donorFullName.empty())
+ {
+ m_panelDonate->Hide();
+ m_bitmapThanks->SetBitmap(getResourceImage(L"freefilesync-heart"));
+ m_staticTextThanks->SetLabel(replaceCpy(_("Thank you, %x, for your donation and support!"), L"%x", donorFullName));
+ setRelativeFontSize(*m_staticTextThanks, 1.25);
+ setRelativeFontSize(*m_buttonShowDonationDetails, 1.25);
+ m_staticTextThanks->Wrap(260); //*after* changing font size
+ }
+ else
+#endif
+ {
+ m_panelThankYou->Hide();
+ m_bitmapDonate->SetBitmap(getResourceImage(L"paypal"));
+ setRelativeFontSize(*m_staticTextDonate, 1.25);
+ setRelativeFontSize(*m_buttonDonate, 1.25);
+ }
//m_animCtrlWink->SetAnimation(getResourceAnimation(L"wink"));
//m_animCtrlWink->Play();
@@ -545,12 +580,12 @@ private:
void updateGui();
- const std::vector<const FileSystemObject*>& rowsToDeleteOnLeft;
- const std::vector<const FileSystemObject*>& rowsToDeleteOnRight;
- const std::chrono::steady_clock::time_point dlgStartTime = std::chrono::steady_clock::now();
+ const std::vector<const FileSystemObject*>& rowsToDeleteOnLeft_;
+ const std::vector<const FileSystemObject*>& rowsToDeleteOnRight_;
+ const std::chrono::steady_clock::time_point dlgStartTime_ = std::chrono::steady_clock::now();
//output-only parameters:
- bool& useRecycleBinOut;
+ bool& useRecycleBinOut_;
};
@@ -559,9 +594,9 @@ DeleteDialog::DeleteDialog(wxWindow* parent,
const std::vector<const FileSystemObject*>& rowsOnRight,
bool& useRecycleBin) :
DeleteDlgGenerated(parent),
- rowsToDeleteOnLeft(rowsOnLeft),
- rowsToDeleteOnRight(rowsOnRight),
- useRecycleBinOut(useRecycleBin)
+ rowsToDeleteOnLeft_(rowsOnLeft),
+ rowsToDeleteOnRight_(rowsOnRight),
+ useRecycleBinOut_(useRecycleBin)
{
#ifdef ZEN_WIN
new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this"
@@ -593,8 +628,8 @@ void DeleteDialog::updateGui()
wxWindowUpdateLocker dummy(this); //leads to GUI corruption problems on Linux/OS X!
#endif
- const std::pair<std::wstring, int> delInfo = zen::getSelectedItemsAsString(rowsToDeleteOnLeft,
- rowsToDeleteOnRight);
+ const std::pair<std::wstring, int> delInfo = zen::getSelectedItemsAsString(rowsToDeleteOnLeft_,
+ rowsToDeleteOnRight_);
wxString header;
if (m_checkBoxUseRecycler->GetValue())
{
@@ -630,10 +665,10 @@ void DeleteDialog::updateGui()
void DeleteDialog::OnOK(wxCommandEvent& event)
{
//additional safety net, similar to Windows Explorer: time delta between DEL and ENTER must be at least 50ms to avoid accidental deletion!
- if (std::chrono::steady_clock::now() < dlgStartTime + std::chrono::milliseconds(50))
+ if (std::chrono::steady_clock::now() < dlgStartTime_ + std::chrono::milliseconds(50)) //considers chrono-wrap-around!
return;
- useRecycleBinOut = m_checkBoxUseRecycler->GetValue();
+ useRecycleBinOut_ = m_checkBoxUseRecycler->GetValue();
EndModal(ReturnSmallDlg::BUTTON_OKAY);
}
@@ -1114,3 +1149,160 @@ ReturnSmallDlg::ButtonPressed zen::showSelectTimespanDlg(wxWindow* parent, std::
SelectTimespanDlg timeSpanDlg(parent, timeFrom, timeTo);
return static_cast<ReturnSmallDlg::ButtonPressed>(timeSpanDlg.ShowModal());
}
+
+//########################################################################################
+
+class ActivationDlg : public ActivationDlgGenerated
+{
+public:
+ ActivationDlg(wxWindow* parent, const std::wstring& lastErrorMsg, const std::wstring& manualActivationUrl, std::wstring& manualActivationKey);
+
+private:
+ void OnActivateOnline (wxCommandEvent& event) override;
+ void OnActivateOffline(wxCommandEvent& event) override;
+ void OnOfflineActivationEnter(wxCommandEvent& event) override { OnActivateOffline(event); }
+ void OnCopyUrl (wxCommandEvent& event) override;
+ void OnCancel(wxCommandEvent& event) override { EndModal(static_cast<int>(ReturnActivationDlg::CANCEL)); }
+ void OnClose (wxCloseEvent& event) override { EndModal(static_cast<int>(ReturnActivationDlg::CANCEL)); }
+
+ std::wstring& manualActivationKeyOut_; //in/out parameter
+};
+
+
+ActivationDlg::ActivationDlg(wxWindow* parent,
+ const std::wstring& lastErrorMsg,
+ const std::wstring& manualActivationUrl,
+ std::wstring& manualActivationKey) :
+ ActivationDlgGenerated(parent),
+ manualActivationKeyOut_(manualActivationKey)
+{
+#ifdef ZEN_WIN
+ new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this"
+#endif
+ setStandardButtonLayout(*bSizerStdButtons, StdButtons().setCancel(m_buttonCancel));
+
+ //setMainInstructionFont(*m_staticTextMain);
+
+ m_bitmapActivation->SetBitmap(getResourceImage(L"website"));
+
+ m_textCtrlLastError ->ChangeValue(lastErrorMsg);
+ m_textCtrlManualActivationUrl ->ChangeValue(manualActivationUrl);
+ m_textCtrlOfflineActivationKey->ChangeValue(manualActivationKey);
+
+ GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize()
+ //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!!
+ Center(); //needs to be re-applied after a dialog size change!
+
+ m_buttonActivateOnline->SetFocus();
+}
+
+
+void ActivationDlg::OnCopyUrl(wxCommandEvent& event)
+{
+ if (wxClipboard::Get()->Open())
+ {
+ ZEN_ON_SCOPE_EXIT(wxClipboard::Get()->Close());
+ wxClipboard::Get()->SetData(new wxTextDataObject(m_textCtrlManualActivationUrl->GetValue())); //ownership passed
+
+ m_textCtrlManualActivationUrl->SetFocus(); //[!] otherwise selection is lost
+ m_textCtrlManualActivationUrl->SelectAll(); //some visual feedback
+ }
+}
+
+
+void ActivationDlg::OnActivateOnline(wxCommandEvent& event)
+{
+ manualActivationKeyOut_ = m_textCtrlOfflineActivationKey->GetValue();
+ EndModal(static_cast<int>(ReturnActivationDlg::ACTIVATE_ONLINE));
+}
+
+
+void ActivationDlg::OnActivateOffline(wxCommandEvent& event)
+{
+ manualActivationKeyOut_ = m_textCtrlOfflineActivationKey->GetValue();
+ EndModal(static_cast<int>(ReturnActivationDlg::ACTIVATE_OFFLINE));
+}
+
+
+ReturnActivationDlg zen::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());
+}
+
+//########################################################################################
+
+class DownloadProgressWindow::Impl : public DownloadProgressDlgGenerated
+{
+public:
+ Impl(wxWindow* parent, const Zstring& fileName, std::uint64_t fileSize);
+
+ void notifyProgress(std::uint64_t delta) { bytesCurrent_ += delta; }
+
+ void requestUiRefresh() //throw CancelPressed
+ {
+ if (cancelled_)
+ throw CancelPressed();
+
+ if (updateUiIsAllowed())
+ {
+ updateGui();
+ //wxTheApp->Yield();
+ ::wxSafeYield(this); //disables user input except for "this" (using wxWindowDisabler instead would move the FFS main dialog into the background: why?)
+ }
+ }
+
+private:
+ void OnCancel(wxCommandEvent& event) override { cancelled_ = true; }
+
+ void updateGui()
+ {
+ const double fraction = bytesTotal_ == 0 ? 0 : 1.0 * bytesCurrent_ / bytesTotal_;
+ m_staticTextHeader->SetLabel(_("Downloading update...") + L" " +
+ numberTo<std::wstring>(numeric::round(fraction * 100)) + L"% (" + filesizeToShortString(bytesCurrent_) + L")");
+ m_gaugeProgress->SetValue(numeric::round(fraction * GAUGE_FULL_RANGE));
+ }
+
+ bool cancelled_ = false;
+ std::uint64_t bytesCurrent_ = 0;
+ const std::uint64_t bytesTotal_;
+ const int GAUGE_FULL_RANGE = 1000000;
+};
+
+
+DownloadProgressWindow::Impl::Impl(wxWindow* parent, const Zstring& filePath, std::uint64_t fileSize) :
+ DownloadProgressDlgGenerated(parent),
+ bytesTotal_(fileSize)
+{
+#ifdef ZEN_WIN
+ new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this"
+#endif
+
+ setStandardButtonLayout(*bSizerStdButtons, StdButtons().setCancel(m_buttonCancel));
+
+ setMainInstructionFont(*m_staticTextHeader);
+
+ m_bitmapDownloading->SetBitmap(getResourceImage(L"website"));
+
+ m_gaugeProgress->SetRange(GAUGE_FULL_RANGE);
+
+ m_staticTextDetails->SetLabel(utfCvrtTo<std::wstring>(filePath));
+
+ updateGui();
+
+ GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize()
+ //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!!
+ Center(); //needs to be re-applied after a dialog size change!
+ Show();
+
+ m_buttonCancel->SetFocus();
+}
+
+
+zen::DownloadProgressWindow::DownloadProgressWindow(wxWindow* parent, const Zstring& filePath, std::uint64_t fileSize) :
+ pimpl_(new DownloadProgressWindow::Impl(parent, filePath, fileSize)) {}
+
+zen::DownloadProgressWindow::~DownloadProgressWindow() { pimpl_->Destroy(); }
+
+void zen::DownloadProgressWindow::notifyProgress(std::uint64_t delta) { pimpl_->notifyProgress(delta); }
+void zen::DownloadProgressWindow::requestUiRefresh() { pimpl_->requestUiRefresh(); } //throw CancelPressed
diff --git a/FreeFileSync/Source/ui/small_dlgs.h b/FreeFileSync/Source/ui/small_dlgs.h
index 22a3012e..3eb23184 100644
--- a/FreeFileSync/Source/ui/small_dlgs.h
+++ b/FreeFileSync/Source/ui/small_dlgs.h
@@ -57,6 +57,30 @@ ReturnSmallDlg::ButtonPressed showSelectTimespanDlg(wxWindow* parent, std::int64
#if defined ZEN_WIN_VISTA_AND_LATER || defined ZEN_MAC
ReturnSmallDlg::ButtonPressed showSftpSetupDialog(wxWindow* parent, Zstring& folderPathPhrase);
#endif
+
+enum class ReturnActivationDlg
+{
+ CANCEL,
+ ACTIVATE_ONLINE,
+ ACTIVATE_OFFLINE,
+};
+ReturnActivationDlg showActivationDialog(wxWindow* parent, const std::wstring& lastErrorMsg, const std::wstring& manualActivationUrl, std::wstring& manualActivationKey);
+
+
+class DownloadProgressWindow //remporary progress info => life-time: stack
+{
+public:
+ DownloadProgressWindow(wxWindow* parent, const Zstring& filePath, std::uint64_t fileSize);
+ ~DownloadProgressWindow();
+
+ struct CancelPressed {};
+ void requestUiRefresh(); //throw CancelPressed
+ void notifyProgress(std::uint64_t delta);
+
+private:
+ class Impl;
+ Impl* const pimpl_;
+};
}
#endif //SMALL_DLGS_H_8321790875018750245
diff --git a/FreeFileSync/Source/ui/switch_to_gui.h b/FreeFileSync/Source/ui/switch_to_gui.h
deleted file mode 100644
index 3ef5f6e0..00000000
--- a/FreeFileSync/Source/ui/switch_to_gui.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef SWITCH_TO_GUI_H_132047815734845
-#define SWITCH_TO_GUI_H_132047815734845
-
-#include "../lib/process_xml.h"
-#include "main_dlg.h" //in "application.cpp" we have this dependency anyway!
-
-
-namespace zen
-{
-//switch from FreeFileSync Batch to GUI modus: opens a new FreeFileSync GUI session asynchronously
-class SwitchToGui
-{
-public:
- SwitchToGui(const Zstring& globalConfigFile,
- xmlAccess::XmlGlobalSettings& globalSettings,
- const Zstring& referenceFile,
- const xmlAccess::XmlBatchConfig& batchCfg) :
- globalConfigFile_(globalConfigFile),
- globalSettings_(globalSettings),
- guiCfg(xmlAccess::convertBatchToGui(batchCfg))
- {
- referenceFiles.push_back(referenceFile);
- }
-
- void execute() const
- {
- MainDialog::create(globalConfigFile_, &globalSettings_, guiCfg, referenceFiles, /*bool startComparison = */ true); //new toplevel window
- }
-
-private:
- const Zstring globalConfigFile_;
- xmlAccess::XmlGlobalSettings& globalSettings_;
-
- std::vector<Zstring> referenceFiles;
- const xmlAccess::XmlGuiConfig guiCfg;
-};
-}
-
-#endif //SWITCH_TO_GUI_H_132047815734845
diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp
index 69b35137..9bac8df3 100644
--- a/FreeFileSync/Source/ui/sync_cfg.cpp
+++ b/FreeFileSync/Source/ui/sync_cfg.cpp
@@ -72,9 +72,9 @@ private:
void OnHelpTimeShift (wxHyperlinkEvent& event) override { displayHelpEntry(L"daylight-saving-time", this); }
void OnToggleLocalCompSettings(wxCommandEvent& event) override { updateCompGui(); updateSyncGui(); /*affects sync settings, too!*/ }
- void OnCompByTimeSize (wxCommandEvent& event) override { localCmpVar = CompareVariant::TIME_SIZE; updateCompGui(); updateSyncGui(); } //
- void OnCompByContent (wxCommandEvent& event) override { localCmpVar = CompareVariant::CONTENT; updateCompGui(); updateSyncGui(); } //affects sync settings, too!
- void OnCompBySize (wxCommandEvent& event) override { localCmpVar = CompareVariant::SIZE; updateCompGui(); updateSyncGui(); } //
+ void OnCompByTimeSize (wxCommandEvent& event) override { localCmpVar_ = CompareVariant::TIME_SIZE; updateCompGui(); updateSyncGui(); } //
+ void OnCompByContent (wxCommandEvent& event) override { localCmpVar_ = CompareVariant::CONTENT; updateCompGui(); updateSyncGui(); } //affects sync settings, too!
+ void OnCompBySize (wxCommandEvent& event) override { localCmpVar_ = CompareVariant::SIZE; updateCompGui(); updateSyncGui(); } //
void OnCompByTimeSizeDouble (wxMouseEvent& event) override;
void OnCompBySizeDouble (wxMouseEvent& event) override;
void OnCompByContentDouble (wxMouseEvent& event) override;
@@ -86,7 +86,7 @@ private:
void updateCompGui();
- CompareVariant localCmpVar = CompareVariant::TIME_SIZE;
+ CompareVariant localCmpVar_ = CompareVariant::TIME_SIZE;
//------------- filter panel --------------------------
void OnHelpShowExamples(wxHyperlinkEvent& event) override { displayHelpEntry(L"exclude-items", this); }
@@ -100,17 +100,17 @@ private:
void updateFilterGui();
- EnumDescrList<UnitTime> enumTimeDescr;
- EnumDescrList<UnitSize> enumSizeDescr;
+ EnumDescrList<UnitTime> enumTimeDescr_;
+ EnumDescrList<UnitSize> enumSizeDescr_;
//------------- synchronization panel -----------------
- void OnSyncTwoWay(wxCommandEvent& event) override { directionCfg.var = DirectionConfig::TWO_WAY; updateSyncGui(); }
- void OnSyncMirror(wxCommandEvent& event) override { directionCfg.var = DirectionConfig::MIRROR; updateSyncGui(); }
- void OnSyncUpdate(wxCommandEvent& event) override { directionCfg.var = DirectionConfig::UPDATE; updateSyncGui(); }
- void OnSyncCustom(wxCommandEvent& event) override { directionCfg.var = DirectionConfig::CUSTOM; updateSyncGui(); }
+ void OnSyncTwoWay(wxCommandEvent& event) override { directionCfg_.var = DirectionConfig::TWO_WAY; updateSyncGui(); }
+ void OnSyncMirror(wxCommandEvent& event) override { directionCfg_.var = DirectionConfig::MIRROR; updateSyncGui(); }
+ void OnSyncUpdate(wxCommandEvent& event) override { directionCfg_.var = DirectionConfig::UPDATE; updateSyncGui(); }
+ void OnSyncCustom(wxCommandEvent& event) override { directionCfg_.var = DirectionConfig::CUSTOM; updateSyncGui(); }
void OnToggleLocalSyncSettings(wxCommandEvent& event) override { updateSyncGui(); }
- void OnToggleDetectMovedFiles (wxCommandEvent& event) override { directionCfg.detectMovedFiles = !directionCfg.detectMovedFiles; updateSyncGui(); } //parameter NOT owned by checkbox!
+ void OnToggleDetectMovedFiles (wxCommandEvent& event) override { directionCfg_.detectMovedFiles = !directionCfg_.detectMovedFiles; updateSyncGui(); } //parameter NOT owned by checkbox!
void OnChangeSyncOption (wxCommandEvent& event) override { updateSyncGui(); }
void OnSyncTwoWayDouble(wxMouseEvent& event) override;
@@ -125,11 +125,11 @@ private:
void OnDifferent (wxCommandEvent& event) override;
void OnConflict (wxCommandEvent& event) override;
- void OnDeletionPermanent (wxCommandEvent& event) override { handleDeletion = DeletionPolicy::PERMANENT; updateSyncGui(); }
- void OnDeletionRecycler (wxCommandEvent& event) override { handleDeletion = DeletionPolicy::RECYCLER; updateSyncGui(); }
- void OnDeletionVersioning (wxCommandEvent& event) override { handleDeletion = DeletionPolicy::VERSIONING; updateSyncGui(); }
+ void OnDeletionPermanent (wxCommandEvent& event) override { handleDeletion_ = DeletionPolicy::PERMANENT; updateSyncGui(); }
+ void OnDeletionRecycler (wxCommandEvent& event) override { handleDeletion_ = DeletionPolicy::RECYCLER; updateSyncGui(); }
+ void OnDeletionVersioning (wxCommandEvent& event) override { handleDeletion_ = DeletionPolicy::VERSIONING; updateSyncGui(); }
- void OnToggleDeletionType(wxCommandEvent& event) override { toggleDeletionPolicy(handleDeletion); updateSyncGui(); }
+ void OnToggleDeletionType(wxCommandEvent& event) override { toggleDeletionPolicy(handleDeletion_); updateSyncGui(); }
void OnHelpDetectMovedFiles(wxHyperlinkEvent& event) override { displayHelpEntry(L"synchronization-settings" , this); }
void OnHelpVersioning (wxHyperlinkEvent& event) override { displayHelpEntry(L"versioning", this); }
@@ -141,8 +141,8 @@ private:
//-----------------------------------------------------
- void OnErrorPopup (wxCommandEvent& event) override { onGuiError = ON_GUIERROR_POPUP; updateMiscGui(); } //parameter NOT owned by radio button
- void OnErrorIgnore(wxCommandEvent& event) override { onGuiError = ON_GUIERROR_IGNORE; updateMiscGui(); } //
+ void OnErrorPopup (wxCommandEvent& event) override { onGuiError_ = ON_GUIERROR_POPUP; updateMiscGui(); } //parameter NOT owned by radio button
+ void OnErrorIgnore(wxCommandEvent& event) override { onGuiError_ = ON_GUIERROR_IGNORE; updateMiscGui(); } //
MiscSyncConfig getMiscSyncOptions() const;
void setMiscSyncOptions(const MiscSyncConfig& miscCfg);
@@ -150,12 +150,12 @@ private:
void updateMiscGui();
//parameters with ownership NOT within GUI controls!
- DirectionConfig directionCfg;
- DeletionPolicy handleDeletion = DeletionPolicy::RECYCLER; //use Recycler, delete permanently or move to user-defined location
- OnGuiError onGuiError = ON_GUIERROR_POPUP;
+ DirectionConfig directionCfg_;
+ DeletionPolicy handleDeletion_ = DeletionPolicy::RECYCLER; //use Recycler, delete permanently or move to user-defined location
+ OnGuiError onGuiError_ = ON_GUIERROR_POPUP;
- EnumDescrList<VersioningStyle> enumVersioningStyle;
- FolderSelector versioningFolder;
+ EnumDescrList<VersioningStyle> enumVersioningStyle_;
+ FolderSelector versioningFolder_;
//-----------------------------------------------------
@@ -163,14 +163,14 @@ private:
bool unselectFolderPairConfig(); //returns false on error: shows message box!
//output-only parameters
- GlobalSyncConfig& globalCfgOut;
- std::vector<LocalPairConfig>& folderPairConfigOut;
+ GlobalSyncConfig& globalCfgOut_;
+ std::vector<LocalPairConfig>& folderPairConfigOut_;
//working copy of ALL config parameters: only one folder pair is selected at a time!
GlobalSyncConfig globalCfg_;
std::vector<LocalPairConfig> folderPairConfig_;
- int selectedPairIndexToShow = EMPTY_PAIR_INDEX_SELECTED;
+ int selectedPairIndexToShow_ = EMPTY_PAIR_INDEX_SELECTED;
static const int EMPTY_PAIR_INDEX_SELECTED = -2;
const size_t onCompletionHistoryMax_;
@@ -219,9 +219,9 @@ ConfigDialog::ConfigDialog(wxWindow* parent,
GlobalSyncConfig& globalCfg,
size_t onCompletionHistoryMax) :
ConfigDlgGenerated(parent),
- versioningFolder(*m_panelVersioning, *m_buttonSelectVersioningFolder, *m_bpButtonSelectAltFolder, *m_versioningFolderPath, nullptr /*staticText*/, nullptr /*wxWindow*/),
- globalCfgOut(globalCfg),
- folderPairConfigOut(folderPairConfig),
+ versioningFolder_(*m_panelVersioning, *m_buttonSelectVersioningFolder, *m_bpButtonSelectAltFolder, *m_versioningFolderPath, nullptr /*staticText*/, nullptr /*dropWindow2*/),
+ globalCfgOut_(globalCfg),
+ folderPairConfigOut_(folderPairConfig),
globalCfg_(globalCfg),
folderPairConfig_(folderPairConfig),
onCompletionHistoryMax_(onCompletionHistoryMax)
@@ -282,7 +282,7 @@ ConfigDialog::ConfigDialog(wxWindow* parent,
m_textCtrlInclude->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(ConfigDialog::onFilterKeyEvent), nullptr, this);
m_textCtrlExclude->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(ConfigDialog::onFilterKeyEvent), nullptr, this);
- enumTimeDescr.
+ enumTimeDescr_.
add(UnitTime::NONE, L"(" + _("None") + L")"). //meta options should be enclosed in parentheses
add(UnitTime::TODAY, _("Today")).
//add(UnitTime::THIS_WEEK, _("This week")).
@@ -290,7 +290,7 @@ ConfigDialog::ConfigDialog(wxWindow* parent,
add(UnitTime::THIS_YEAR, _("This year")).
add(UnitTime::LAST_X_DAYS, _("Last x days"));
- enumSizeDescr.
+ enumSizeDescr_.
add(UnitSize::NONE, L"(" + _("None") + L")"). //meta options should be enclosed in parentheses
add(UnitSize::BYTE, _("Byte")).
add(UnitSize::KB, _("KB")).
@@ -319,7 +319,7 @@ ConfigDialog::ConfigDialog(wxWindow* parent,
setRelativeFontSize(*m_toggleBtnUpdate, 1.25);
setRelativeFontSize(*m_toggleBtnCustom, 1.25);
- enumVersioningStyle.
+ enumVersioningStyle_.
add(VersioningStyle::REPLACE, _("Replace"), _("Move files and replace if existing")).
add(VersioningStyle::ADD_TIMESTAMP, _("Time stamp"), _("Append a time stamp to each file name"));
@@ -444,7 +444,7 @@ void ConfigDialog::OnSelectFolderPair(wxCommandEvent& event)
if (!unselectFolderPairConfig())
{
//restore old selection:
- m_listBoxFolderPair->SetSelection(selectedPairIndexToShow + 1);
+ m_listBoxFolderPair->SetSelection(selectedPairIndexToShow_ + 1);
return;
}
selectFolderPairConfig(selPos - 1);
@@ -493,7 +493,7 @@ std::shared_ptr<const CompConfig> ConfigDialog::getCompConfig() const
return nullptr;
CompConfig compCfg;
- compCfg.compareVar = localCmpVar;
+ compCfg.compareVar = localCmpVar_;
compCfg.handleSymlinks = !m_checkBoxSymlinksInclude->GetValue() ? SymLinkHandling::EXCLUDE : m_radioBtnSymlinksDirect->GetValue() ? SymLinkHandling::DIRECT : SymLinkHandling::FOLLOW;
compCfg.ignoreTimeShiftMinutes = fromTimeShiftPhrase(copyStringTo<std::wstring>(m_textCtrlTimeShift->GetValue()));
@@ -508,7 +508,7 @@ void ConfigDialog::setCompConfig(std::shared_ptr<const CompConfig> compCfg)
if (!compCfg) //when local settings are inactive, display (current) global settings instead:
compCfg = std::make_shared<const CompConfig>(globalCfg_.cmpConfig);
- localCmpVar = compCfg->compareVar;
+ localCmpVar_ = compCfg->compareVar;
switch (compCfg->handleSymlinks)
{
@@ -545,7 +545,7 @@ void ConfigDialog::updateCompGui()
m_toggleBtnByContent ->SetValue(false);
if (m_checkBoxUseLocalCmpOptions->GetValue()) //help wxWidgets a little to render inactive config state (need on Windows, NOT on Linux!)
- switch (localCmpVar)
+ switch (localCmpVar_)
{
case CompareVariant::TIME_SIZE:
m_toggleBtnByTimeSize->SetValue(true);
@@ -566,12 +566,12 @@ void ConfigDialog::updateCompGui()
else
bmpCtrl.SetBitmap(greyScale(bmp));
};
- setBitmap(*m_bitmapByTimeSize, localCmpVar == CompareVariant::TIME_SIZE, getResourceImage(L"file-time"));
- setBitmap(*m_bitmapByContent, localCmpVar == CompareVariant::CONTENT, getResourceImage(L"file-content"));
- setBitmap(*m_bitmapBySize, localCmpVar == CompareVariant::SIZE, getResourceImage(L"file-size"));
+ setBitmap(*m_bitmapByTimeSize, localCmpVar_ == CompareVariant::TIME_SIZE, getResourceImage(L"file-time"));
+ setBitmap(*m_bitmapByContent, localCmpVar_ == CompareVariant::CONTENT, getResourceImage(L"file-content"));
+ setBitmap(*m_bitmapBySize, localCmpVar_ == CompareVariant::SIZE, getResourceImage(L"file-size"));
//active variant description:
- setText(*m_textCtrlCompVarDescription, L"\n" + getCompVariantDescription(localCmpVar));
+ setText(*m_textCtrlCompVarDescription, L"\n" + getCompVariantDescription(localCmpVar_));
m_radioBtnSymlinksDirect->Enable(m_checkBoxSymlinksInclude->GetValue());
m_radioBtnSymlinksFollow->Enable(m_checkBoxSymlinksInclude->GetValue());
@@ -611,11 +611,11 @@ FilterConfig ConfigDialog::getFilterConfig() const
return FilterConfig(includeFilter, exludeFilter,
m_spinCtrlTimespan->GetValue(),
- getEnumVal(enumTimeDescr, *m_choiceUnitTimespan),
+ getEnumVal(enumTimeDescr_, *m_choiceUnitTimespan),
m_spinCtrlMinSize->GetValue(),
- getEnumVal(enumSizeDescr, *m_choiceUnitMinSize),
+ getEnumVal(enumSizeDescr_, *m_choiceUnitMinSize),
m_spinCtrlMaxSize->GetValue(),
- getEnumVal(enumSizeDescr, *m_choiceUnitMaxSize));
+ getEnumVal(enumSizeDescr_, *m_choiceUnitMaxSize));
}
@@ -624,9 +624,9 @@ void ConfigDialog::setFilterConfig(const FilterConfig& filter)
m_textCtrlInclude->ChangeValue(utfCvrtTo<wxString>(filter.includeFilter));
m_textCtrlExclude->ChangeValue(utfCvrtTo<wxString>(filter.excludeFilter));
- setEnumVal(enumTimeDescr, *m_choiceUnitTimespan, filter.unitTimeSpan);
- setEnumVal(enumSizeDescr, *m_choiceUnitMinSize, filter.unitSizeMin);
- setEnumVal(enumSizeDescr, *m_choiceUnitMaxSize, filter.unitSizeMax);
+ setEnumVal(enumTimeDescr_, *m_choiceUnitTimespan, filter.unitTimeSpan);
+ setEnumVal(enumSizeDescr_, *m_choiceUnitMinSize, filter.unitSizeMin);
+ setEnumVal(enumSizeDescr_, *m_choiceUnitMaxSize, filter.unitSizeMax);
m_spinCtrlTimespan->SetValue(static_cast<int>(filter.timeSpan));
m_spinCtrlMinSize ->SetValue(static_cast<int>(filter.sizeMin));
@@ -754,42 +754,42 @@ void toggleCustomSyncConfig(DirectionConfig& directionCfg, SyncDirection& custSy
void ConfigDialog::OnExLeftSideOnly(wxCommandEvent& event)
{
- toggleCustomSyncConfig(directionCfg, directionCfg.custom.exLeftSideOnly);
+ toggleCustomSyncConfig(directionCfg_, directionCfg_.custom.exLeftSideOnly);
updateSyncGui();
}
void ConfigDialog::OnExRightSideOnly(wxCommandEvent& event)
{
- toggleCustomSyncConfig(directionCfg, directionCfg.custom.exRightSideOnly);
+ toggleCustomSyncConfig(directionCfg_, directionCfg_.custom.exRightSideOnly);
updateSyncGui();
}
void ConfigDialog::OnLeftNewer(wxCommandEvent& event)
{
- toggleCustomSyncConfig(directionCfg, directionCfg.custom.leftNewer);
+ toggleCustomSyncConfig(directionCfg_, directionCfg_.custom.leftNewer);
updateSyncGui();
}
void ConfigDialog::OnRightNewer(wxCommandEvent& event)
{
- toggleCustomSyncConfig(directionCfg, directionCfg.custom.rightNewer);
+ toggleCustomSyncConfig(directionCfg_, directionCfg_.custom.rightNewer);
updateSyncGui();
}
void ConfigDialog::OnDifferent(wxCommandEvent& event)
{
- toggleCustomSyncConfig(directionCfg, directionCfg.custom.different);
+ toggleCustomSyncConfig(directionCfg_, directionCfg_.custom.different);
updateSyncGui();
}
void ConfigDialog::OnConflict(wxCommandEvent& event)
{
- toggleCustomSyncConfig(directionCfg, directionCfg.custom.conflict);
+ toggleCustomSyncConfig(directionCfg_, directionCfg_.custom.conflict);
updateSyncGui();
}
@@ -878,10 +878,10 @@ std::shared_ptr<const SyncConfig> ConfigDialog::getSyncConfig() const
return nullptr;
SyncConfig syncCfg;
- syncCfg.directionCfg = directionCfg;
- syncCfg.handleDeletion = handleDeletion;
- syncCfg.versioningFolderPhrase = versioningFolder.getPath();
- syncCfg.versioningStyle = getEnumVal(enumVersioningStyle, *m_choiceVersioningStyle);
+ syncCfg.directionCfg = directionCfg_;
+ syncCfg.handleDeletion = handleDeletion_;
+ syncCfg.versioningFolderPhrase = versioningFolder_.getPath();
+ syncCfg.versioningStyle = getEnumVal(enumVersioningStyle_, *m_choiceVersioningStyle);
return std::make_shared<const SyncConfig>(syncCfg);
}
@@ -894,10 +894,10 @@ void ConfigDialog::setSyncConfig(std::shared_ptr<const SyncConfig> syncCfg)
if (!syncCfg) //when local settings are inactive, display (current) global settings instead:
syncCfg = std::make_shared<const SyncConfig>(globalCfg_.syncCfg);
- directionCfg = syncCfg->directionCfg; //make working copy; ownership *not* on GUI
- handleDeletion = syncCfg->handleDeletion;
- versioningFolder.setPath(syncCfg->versioningFolderPhrase);
- setEnumVal(enumVersioningStyle, *m_choiceVersioningStyle, syncCfg->versioningStyle);
+ directionCfg_ = syncCfg->directionCfg; //make working copy; ownership *not* on GUI
+ handleDeletion_ = syncCfg->handleDeletion;
+ versioningFolder_.setPath(syncCfg->versioningFolderPhrase);
+ setEnumVal(enumVersioningStyle_, *m_choiceVersioningStyle, syncCfg->versioningStyle);
updateSyncGui();
}
@@ -921,7 +921,7 @@ void ConfigDialog::updateSyncGui()
m_notebook->SetPageImage(static_cast<size_t>(SyncConfigPanel::SYNC),
static_cast<int>(m_checkBoxUseLocalSyncOptions->GetValue() ? ConfigTypeImage::SYNC: ConfigTypeImage::SYNC_GREY));
- updateSyncDirectionIcons(directionCfg,
+ updateSyncDirectionIcons(directionCfg_,
*m_bpButtonLeftOnly,
*m_bpButtonRightOnly,
*m_bpButtonLeftNewer,
@@ -930,8 +930,8 @@ void ConfigDialog::updateSyncGui()
*m_bpButtonConflict);
//selecting "detect move files" does not always make sense:
- m_checkBoxDetectMove->Enable(detectMovedFilesSelectable(directionCfg));
- m_checkBoxDetectMove->SetValue(detectMovedFilesEnabled(directionCfg)); //parameter NOT owned by checkbox!
+ m_checkBoxDetectMove->Enable(detectMovedFilesSelectable(directionCfg_));
+ m_checkBoxDetectMove->SetValue(detectMovedFilesEnabled(directionCfg_)); //parameter NOT owned by checkbox!
auto setBitmap = [&](wxStaticBitmap& bmpCtrl, bool active, const wxBitmap& bmp)
{
@@ -943,14 +943,14 @@ void ConfigDialog::updateSyncGui()
};
//display only relevant sync options
- m_bitmapDatabase ->Show(directionCfg.var == DirectionConfig::TWO_WAY);
- fgSizerSyncDirections->Show(directionCfg.var != DirectionConfig::TWO_WAY);
+ m_bitmapDatabase ->Show(directionCfg_.var == DirectionConfig::TWO_WAY);
+ fgSizerSyncDirections->Show(directionCfg_.var != DirectionConfig::TWO_WAY);
- if (directionCfg.var == DirectionConfig::TWO_WAY)
+ if (directionCfg_.var == DirectionConfig::TWO_WAY)
setBitmap(*m_bitmapDatabase, true, getResourceImage(L"database"));
else
{
- const CompareVariant activeCmpVar = m_checkBoxUseLocalCmpOptions->GetValue() ? localCmpVar : globalCfg_.cmpConfig.compareVar;
+ const CompareVariant activeCmpVar = m_checkBoxUseLocalCmpOptions->GetValue() ? localCmpVar_ : globalCfg_.cmpConfig.compareVar;
m_bitmapLeftNewer ->Show(activeCmpVar == CompareVariant::TIME_SIZE);
m_bpButtonLeftNewer ->Show(activeCmpVar == CompareVariant::TIME_SIZE);
@@ -962,7 +962,7 @@ void ConfigDialog::updateSyncGui()
}
//active variant description:
- setText(*m_textCtrlSyncVarDescription, L"\n" + getSyncVariantDescription(directionCfg.var));
+ setText(*m_textCtrlSyncVarDescription, L"\n" + getSyncVariantDescription(directionCfg_.var));
//update toggle buttons -> they have no parameter-ownership at all!
m_toggleBtnTwoWay->SetValue(false);
@@ -971,7 +971,7 @@ void ConfigDialog::updateSyncGui()
m_toggleBtnCustom->SetValue(false);
if (m_checkBoxUseLocalSyncOptions->GetValue()) //help wxWidgets a little to render inactive config state (need on Windows, NOT on Linux!)
- switch (directionCfg.var)
+ switch (directionCfg_.var)
{
case DirectionConfig::TWO_WAY:
m_toggleBtnTwoWay->SetValue(true);
@@ -987,7 +987,7 @@ void ConfigDialog::updateSyncGui()
break;
}
- switch (handleDeletion)
+ switch (handleDeletion_)
{
case DeletionPolicy::PERMANENT:
m_radioBtnPermanent->SetValue(true);
@@ -1011,15 +1011,15 @@ void ConfigDialog::updateSyncGui()
m_bpButtonDeletionType->SetBitmapDisabled(greyScale(m_bpButtonDeletionType->GetBitmap())); //fix wxWidgets' all-too-clever multi-state!
- const bool versioningSelected = handleDeletion == DeletionPolicy::VERSIONING;
+ const bool versioningSelected = handleDeletion_ == DeletionPolicy::VERSIONING;
m_panelVersioning->Show(versioningSelected);
if (versioningSelected)
{
- updateTooltipEnumVal(enumVersioningStyle, *m_choiceVersioningStyle);
+ updateTooltipEnumVal(enumVersioningStyle_, *m_choiceVersioningStyle);
const std::wstring pathSep = utfCvrtTo<std::wstring>(FILE_NAME_SEPARATOR);
- switch (getEnumVal(enumVersioningStyle, *m_choiceVersioningStyle))
+ switch (getEnumVal(enumVersioningStyle_, *m_choiceVersioningStyle))
{
case VersioningStyle::REPLACE:
setText(*m_staticTextNamingCvtPart1, pathSep + _("Folder") + pathSep + _("File") + L".doc");
@@ -1043,10 +1043,10 @@ void ConfigDialog::updateSyncGui()
MiscSyncConfig ConfigDialog::getMiscSyncOptions() const
{
- assert(selectedPairIndexToShow == -1);
+ assert(selectedPairIndexToShow_ == -1);
MiscSyncConfig miscCfg;
- miscCfg.handleError = onGuiError;
+ miscCfg.handleError = onGuiError_;
miscCfg.onCompletionCommand = m_comboBoxOnCompletion->getValue();
miscCfg.onCompletionHistory = m_comboBoxOnCompletion->getHistory();
return miscCfg;
@@ -1055,7 +1055,7 @@ MiscSyncConfig ConfigDialog::getMiscSyncOptions() const
void ConfigDialog::setMiscSyncOptions(const MiscSyncConfig& miscCfg)
{
- onGuiError = miscCfg.handleError;
+ onGuiError_ = miscCfg.handleError;
m_comboBoxOnCompletion->setValue(miscCfg.onCompletionCommand);
m_comboBoxOnCompletion->setHistory(miscCfg.onCompletionHistory, onCompletionHistoryMax_);
@@ -1065,7 +1065,7 @@ void ConfigDialog::setMiscSyncOptions(const MiscSyncConfig& miscCfg)
void ConfigDialog::updateMiscGui()
{
- switch (onGuiError)
+ switch (onGuiError_)
{
case ON_GUIERROR_IGNORE:
m_radioBtnIgnoreErrors->SetValue(true);
@@ -1079,11 +1079,11 @@ void ConfigDialog::updateMiscGui()
void ConfigDialog::selectFolderPairConfig(int newPairIndexToShow)
{
- assert(selectedPairIndexToShow == EMPTY_PAIR_INDEX_SELECTED);
+ assert(selectedPairIndexToShow_ == EMPTY_PAIR_INDEX_SELECTED);
assert(newPairIndexToShow == -1 || makeUnsigned(newPairIndexToShow) < folderPairConfig_.size());
numeric::clamp(newPairIndexToShow, -1, static_cast<int>(folderPairConfig_.size()) - 1);
- selectedPairIndexToShow = newPairIndexToShow;
+ selectedPairIndexToShow_ = newPairIndexToShow;
m_listBoxFolderPair->SetSelection(newPairIndexToShow + 1);
//show/hide controls that are only relevant for main/local config
@@ -1116,16 +1116,16 @@ void ConfigDialog::selectFolderPairConfig(int newPairIndexToShow)
}
else
{
- setCompConfig (folderPairConfig_[selectedPairIndexToShow].altCmpConfig);
- setSyncConfig (folderPairConfig_[selectedPairIndexToShow].altSyncConfig);
- setFilterConfig(folderPairConfig_[selectedPairIndexToShow].localFilter);
+ setCompConfig (folderPairConfig_[selectedPairIndexToShow_].altCmpConfig);
+ setSyncConfig (folderPairConfig_[selectedPairIndexToShow_].altSyncConfig);
+ setFilterConfig(folderPairConfig_[selectedPairIndexToShow_].localFilter);
}
}
bool ConfigDialog::unselectFolderPairConfig()
{
- assert(selectedPairIndexToShow == -1 || makeUnsigned(selectedPairIndexToShow) < folderPairConfig_.size());
+ assert(selectedPairIndexToShow_ == -1 || makeUnsigned(selectedPairIndexToShow_) < folderPairConfig_.size());
auto compCfg = getCompConfig();
auto syncCfg = getSyncConfig();
@@ -1152,7 +1152,7 @@ bool ConfigDialog::unselectFolderPairConfig()
m_comboBoxOnCompletion->addItemHistory(); //commit current "on completion" history item
- if (selectedPairIndexToShow < 0)
+ if (selectedPairIndexToShow_ < 0)
{
globalCfg_.cmpConfig = *compCfg;
globalCfg_.syncCfg = *syncCfg;
@@ -1161,12 +1161,12 @@ bool ConfigDialog::unselectFolderPairConfig()
}
else
{
- folderPairConfig_[selectedPairIndexToShow].altCmpConfig = compCfg;
- folderPairConfig_[selectedPairIndexToShow].altSyncConfig = syncCfg;
- folderPairConfig_[selectedPairIndexToShow].localFilter = filterCfg;
+ folderPairConfig_[selectedPairIndexToShow_].altCmpConfig = compCfg;
+ folderPairConfig_[selectedPairIndexToShow_].altSyncConfig = syncCfg;
+ folderPairConfig_[selectedPairIndexToShow_].localFilter = filterCfg;
}
- selectedPairIndexToShow = EMPTY_PAIR_INDEX_SELECTED;
+ selectedPairIndexToShow_ = EMPTY_PAIR_INDEX_SELECTED;
//m_listBoxFolderPair->SetSelection(wxNOT_FOUND); not needed, selectedPairIndexToShow has parameter ownership
return true;
}
@@ -1177,8 +1177,8 @@ void ConfigDialog::OnOkay(wxCommandEvent& event)
if (!unselectFolderPairConfig())
return;
- globalCfgOut = globalCfg_;
- folderPairConfigOut = folderPairConfig_;
+ globalCfgOut_ = globalCfg_;
+ folderPairConfigOut_ = folderPairConfig_;
EndModal(ReturnSyncConfig::BUTTON_OKAY);
}
diff --git a/FreeFileSync/Source/ui/taskbar.cpp b/FreeFileSync/Source/ui/taskbar.cpp
index e8a61efa..1723957a 100644
--- a/FreeFileSync/Source/ui/taskbar.cpp
+++ b/FreeFileSync/Source/ui/taskbar.cpp
@@ -86,7 +86,7 @@ public:
void setProgress(double fraction)
{
const wchar_t* errorMsg = nullptr;
- setProgress_(assocWindow, fraction * 100000, 100000, errorMsg);
+ setProgress_(assocWindow, fraction * 1000000, 1000000, errorMsg);
if (errorMsg)
{
ZEN_ON_SCOPE_EXIT(freeString_(errorMsg));
diff --git a/FreeFileSync/Source/ui/tree_view.cpp b/FreeFileSync/Source/ui/tree_view.cpp
index 88d4dd49..cbfb5305 100644
--- a/FreeFileSync/Source/ui/tree_view.cpp
+++ b/FreeFileSync/Source/ui/tree_view.cpp
@@ -299,17 +299,15 @@ void TreeView::sortSingleLevel(std::vector<TreeLine>& items, ColumnTypeNavi colu
switch (columnType)
{
- case ColumnTypeNavi::BYTES:
- std::sort(items.begin(), items.end(), makeSortDirection(lessBytes, Int2Type<ascending>()));
- break;
-
- case ColumnTypeNavi::DIRECTORY:
+ case ColumnTypeNavi::FOLDER_NAME:
std::sort(items.begin(), items.end(), LessShortName<ascending>());
break;
-
case ColumnTypeNavi::ITEM_COUNT:
std::sort(items.begin(), items.end(), makeSortDirection(lessCount, Int2Type<ascending>()));
break;
+ case ColumnTypeNavi::BYTES:
+ std::sort(items.begin(), items.end(), makeSortDirection(lessBytes, Int2Type<ascending>()));
+ break;
}
}
@@ -467,12 +465,12 @@ bool TreeView::getDefaultSortDirection(ColumnTypeNavi colType)
{
switch (colType)
{
- case ColumnTypeNavi::BYTES:
- return false;
- case ColumnTypeNavi::DIRECTORY:
+ case ColumnTypeNavi::FOLDER_NAME:
return true;
case ColumnTypeNavi::ITEM_COUNT:
return false;
+ case ColumnTypeNavi::BYTES:
+ return false;
}
assert(false);
return true;
@@ -808,11 +806,7 @@ private:
{
switch (static_cast<ColumnTypeNavi>(colType))
{
- case ColumnTypeNavi::BYTES:
- case ColumnTypeNavi::ITEM_COUNT:
- break;
-
- case ColumnTypeNavi::DIRECTORY:
+ case ColumnTypeNavi::FOLDER_NAME:
if (treeDataView_)
if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row))
if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get()))
@@ -826,6 +820,10 @@ private:
return dirLeft + L" \u2212 \n" + dirRight; //\u2212 = unicode minus
}
break;
+
+ case ColumnTypeNavi::ITEM_COUNT:
+ case ColumnTypeNavi::BYTES:
+ break;
}
return std::wstring();
}
@@ -837,10 +835,7 @@ private:
if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row))
switch (static_cast<ColumnTypeNavi>(colType))
{
- case ColumnTypeNavi::BYTES:
- return filesizeToShortString(node->bytes_);
-
- case ColumnTypeNavi::DIRECTORY:
+ case ColumnTypeNavi::FOLDER_NAME:
if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get()))
return root->displayName_;
else if (const TreeView::DirNode* dir = dynamic_cast<const TreeView::DirNode*>(node.get()))
@@ -851,6 +846,9 @@ private:
case ColumnTypeNavi::ITEM_COUNT:
return toGuiString(node->itemCount_);
+
+ case ColumnTypeNavi::BYTES:
+ return filesizeToShortString(node->bytes_);
}
}
return std::wstring();
@@ -908,7 +906,7 @@ private:
// --------------------------------------------------------------------------------
// -> synchronize renderCell() <-> getBestSize() <-> getRowMouseHover()
- if (static_cast<ColumnTypeNavi>(colType) == ColumnTypeNavi::DIRECTORY && treeDataView_)
+ if (static_cast<ColumnTypeNavi>(colType) == ColumnTypeNavi::FOLDER_NAME && treeDataView_)
{
if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row))
{
@@ -1047,7 +1045,7 @@ private:
{
// -> synchronize renderCell() <-> getBestSize() <-> getRowMouseHover()
- if (static_cast<ColumnTypeNavi>(colType) == ColumnTypeNavi::DIRECTORY && treeDataView_)
+ if (static_cast<ColumnTypeNavi>(colType) == ColumnTypeNavi::FOLDER_NAME && treeDataView_)
{
if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row))
return node->level_ * widthLevelStep + GAP_SIZE + (showPercentBar ? WIDTH_PERCENTAGE_BAR + 2 * GAP_SIZE : 0) + widthNodeStatus + GAP_SIZE
@@ -1065,11 +1063,7 @@ private:
{
switch (static_cast<ColumnTypeNavi>(colType))
{
- case ColumnTypeNavi::BYTES:
- case ColumnTypeNavi::ITEM_COUNT:
- break;
-
- case ColumnTypeNavi::DIRECTORY:
+ case ColumnTypeNavi::FOLDER_NAME:
if (treeDataView_)
if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row))
{
@@ -1082,6 +1076,10 @@ private:
return static_cast<HoverArea>(HoverAreaNavi::NODE);
}
break;
+
+ case ColumnTypeNavi::ITEM_COUNT:
+ case ColumnTypeNavi::BYTES:
+ break;
}
return HoverArea::NONE;
}
@@ -1090,12 +1088,12 @@ private:
{
switch (static_cast<ColumnTypeNavi>(colType))
{
- case ColumnTypeNavi::BYTES:
- return _("Size");
- case ColumnTypeNavi::DIRECTORY:
+ case ColumnTypeNavi::FOLDER_NAME:
return _("Name");
case ColumnTypeNavi::ITEM_COUNT:
return _("Items");
+ case ColumnTypeNavi::BYTES:
+ return _("Size");
}
return std::wstring();
}
@@ -1206,19 +1204,33 @@ private:
{
auto colAttr = grid_.getColumnConfig();
+ Grid::ColumnAttribute* caFolderName = nullptr;
+ Grid::ColumnAttribute* caToggle = nullptr;
+
for (Grid::ColumnAttribute& ca : colAttr)
- if (ca.type_ == ct)
- {
- ca.visible_ = !ca.visible_;
- grid_.setColumnConfig(colAttr);
- return;
- }
+ if (ca.type_ == static_cast<ColumnType>(ColumnTypeNavi::FOLDER_NAME))
+ caFolderName = &ca;
+ else if (ca.type_ == ct)
+ caToggle = &ca;
+
+ assert(caFolderName && caFolderName->stretch_ > 0 && caFolderName->visible_);
+ assert(caToggle && caToggle->stretch_ == 0);
+
+ if (caFolderName && caToggle)
+ {
+ caToggle->visible_ = !caToggle->visible_;
+
+ //take width of newly visible column from stretched folder name column
+ caFolderName->offset_ -= caToggle->visible_ ? caToggle->offset_ : -caToggle->offset_;
+
+ grid_.setColumnConfig(colAttr);
+ }
};
for (const Grid::ColumnAttribute& ca : grid_.getColumnConfig())
{
- menu.addCheckBox(getColumnLabel(ca.type_), [ca, toggleColumn] { toggleColumn(ca.type_); },
- ca.visible_, ca.type_ != static_cast<ColumnType>(ColumnTypeNavi::DIRECTORY)); //do not allow user to hide file name column!
+ menu.addCheckBox(getColumnLabel(ca.type_), [ct = ca.type_, toggleColumn] { toggleColumn(ct); },
+ ca.visible_, ca.type_ != static_cast<ColumnType>(ColumnTypeNavi::FOLDER_NAME)); //do not allow user to hide file name column!
}
//--------------------------------------------------------------------------------------------------------
menu.addSeparator();
diff --git a/FreeFileSync/Source/ui/version_check.cpp b/FreeFileSync/Source/ui/version_check.cpp
index 442caf5e..da7b0e08 100644
--- a/FreeFileSync/Source/ui/version_check.cpp
+++ b/FreeFileSync/Source/ui/version_check.cpp
@@ -17,10 +17,15 @@
#include <wx+/http.h>
#include <wx+/image_resources.h>
#include "../lib/ffs_paths.h"
-#include "version_check_impl.h"
+#include "small_dlgs.h"
#ifdef ZEN_WIN
#include <zen/win_ver.h>
+ #include <ShlObj.h>
+ #include <zen/com_tools.h>
+ #include <zen/file_io.h>
+ #include <zen/long_path_prefix.h>
+ #include <zen/shell_execute.h>
#elif defined ZEN_MAC
#include <CoreServices/CoreServices.h> //Gestalt()
@@ -35,6 +40,8 @@ namespace
const std::thread::id mainThreadId = std::this_thread::get_id();
#endif
+const wchar_t ffsUpdateCheckUserAgent[] = L"FFS-Update-Check";
+
std::wstring getIso639Language()
{
@@ -43,23 +50,22 @@ std::wstring getIso639Language()
#ifdef ZEN_WIN //use a more reliable function than wxWidgets:
const int bufSize = 10;
wchar_t buf[bufSize] = {};
- int rv = ::GetLocaleInfo(LOCALE_USER_DEFAULT, //_In_ LCID Locale,
- LOCALE_SISO639LANGNAME, //_In_ LCTYPE LCType,
- buf, //_Out_opt_ LPTSTR lpLCData,
- bufSize); //_In_ int cchData
+ const int rv = ::GetLocaleInfo(LOCALE_USER_DEFAULT, //_In_ LCID Locale,
+ LOCALE_SISO639LANGNAME, //_In_ LCTYPE LCType,
+ buf, //_Out_opt_ LPTSTR lpLCData,
+ bufSize); //_In_ int cchData
if (0 < rv && rv < bufSize)
return buf; //MSDN: "This can be a 3-letter code for languages that don't have a 2-letter code"!
- assert(false);
- return std::wstring();
-
#else
const std::wstring localeName(wxLocale::GetLanguageCanonicalName(wxLocale::GetSystemLanguage()));
- if (localeName.empty())
- return std::wstring();
-
- assert(beforeLast(localeName, L"_", IF_MISSING_RETURN_ALL).size() == 2);
- return beforeLast(localeName, L"_", IF_MISSING_RETURN_ALL);
+ if (!localeName.empty())
+ {
+ assert(beforeLast(localeName, L"_", IF_MISSING_RETURN_ALL).size() == 2);
+ return beforeLast(localeName, L"_", IF_MISSING_RETURN_ALL);
+ }
#endif
+ assert(false);
+ return L"zz";
}
@@ -70,22 +76,22 @@ std::wstring getIso3166Country()
#ifdef ZEN_WIN //use a more reliable function than wxWidgets:
const int bufSize = 10;
wchar_t buf[bufSize] = {};
- int rv = ::GetLocaleInfo(LOCALE_USER_DEFAULT, //_In_ LCID Locale,
- LOCALE_SISO3166CTRYNAME, //_In_ LCTYPE LCType,
- buf, //_Out_opt_ LPTSTR lpLCData,
- bufSize); //_In_ int cchData
+ const int rv = ::GetLocaleInfo(LOCALE_USER_DEFAULT, //_In_ LCID Locale,
+ LOCALE_SISO3166CTRYNAME, //_In_ LCTYPE LCType,
+ buf, //_Out_opt_ LPTSTR lpLCData,
+ bufSize); //_In_ int cchData
if (0 < rv && rv < bufSize)
return buf; //MSDN: "This can also return a number, such as "029" for Caribbean."!
- assert(false);
- return std::wstring();
-
#else
const std::wstring localeName(wxLocale::GetLanguageCanonicalName(wxLocale::GetSystemLanguage()));
- if (localeName.empty())
- return std::wstring();
-
- return afterLast(localeName, L"_", IF_MISSING_RETURN_NONE);
+ if (!localeName.empty())
+ {
+ if (contains(localeName, L"_"))
+ return afterLast(localeName, L"_", IF_MISSING_RETURN_NONE);
+ }
#endif
+ assert(false);
+ return L"ZZ";
}
@@ -95,7 +101,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", utfCvrtTo<std::string>(zen::ffsVersion));
+ params.emplace_back("ffs_version", zen::ffsVersion);
params.emplace_back("ffs_type", isPortableVersion() ? "Portable" : "Local");
#ifdef ZEN_WIN
@@ -136,53 +142,169 @@ std::vector<std::pair<std::string, std::string>> geHttpPostParameters()
#endif
#endif
- const std::string isoLang = utfCvrtTo<std::string>(getIso639Language());
- const std::string isoCountry = utfCvrtTo<std::string>(getIso3166Country());
-
- params.emplace_back("language", !isoLang .empty() ? isoLang : "zz");
- params.emplace_back("country" , !isoCountry.empty() ? isoCountry : "ZZ");
+ params.emplace_back("language", utfCvrtTo<std::string>(getIso639Language()));
+ params.emplace_back("country" , utfCvrtTo<std::string>(getIso3166Country()));
return params;
}
-//access is thread-safe on Windows (WinInet), but not on Linux/OS X (wxWidgets)
-std::wstring getOnlineVersion(const std::vector<std::pair<std::string, std::string>>& postParams) //throw SysError
+#ifdef ZEN_WIN
+void runFreeFileSyncAutoUpdate(wxWindow* parent, const std::wstring& directDownloadUrl, const Zstring& fileName, std::uint64_t fileSize)
{
- //harmonize with wxHTTP: get_latest_version_number.php must be accessible without https!!!
- const std::string buffer = sendHttpPost(L"http://www.freefilesync.org/get_latest_version_number.php", L"FFS-Update-Check", postParams); //throw SysError
- const auto version = utfCvrtTo<std::wstring>(buffer);
- return trimCpy(version);
-}
+ try
+ {
+#ifdef ZEN_WIN_VISTA_AND_LATER
+ PWSTR buf = nullptr;
+ ZEN_COM_CHECK(::SHGetKnownFolderPath(FOLDERID_Downloads, //_In_ REFKNOWNFOLDERID rfid,
+ KF_FLAG_DONT_VERIFY, //_In_ DWORD dwFlags,
+ nullptr, //_In_opt_ HANDLE hToken,
+ &buf)); //_Out_ PWSTR *ppszPath
+ ZEN_ON_SCOPE_EXIT(::CoTaskMemFree(buf));
+#else
+ wchar_t buf[MAX_PATH] = {};
+ ZEN_COM_CHECK(::SHGetFolderPath(nullptr, //__in HWND hwndOwner,
+ CSIDL_DESKTOPDIRECTORY |
+ CSIDL_FLAG_DONT_VERIFY, //__in int nFolder,
+ nullptr, //__in HANDLE hToken,
+ 0 /* == SHGFP_TYPE_CURRENT*/, //__in DWORD dwFlags,
+ buf)); //__out LPTSTR pszPath
+#endif
+ const Zstring dirPath = buf;
+ const Zstring installerLocalPath = dirPath.empty() ? fileName : appendSeparator(dirPath) + fileName;
+ const Zstring installerLocalPathTmp = installerLocalPath + L".tmp";
-std::vector<size_t> parseVersion(const std::wstring& version)
-{
- std::vector<size_t> output;
- for (const std::wstring& digit : split(version, FFS_VERSION_SEPARATOR))
- output.push_back(stringTo<size_t>(digit));
- return output;
-}
+ if (!fileExists(installerLocalPath))
+ {
+ ZEN_ON_SCOPE_FAIL(try { removeFile(installerLocalPathTmp); }
+ catch (FileError&) {});
+ {
+ DownloadProgressWindow progressWin(parent, installerLocalPath, fileSize);
-std::wstring getOnlineChangelogDelta()
-{
- try //harmonize with wxHTTP: get_latest_changes.php must be accessible without https!!!
+ auto notifyProgress = [&](std::int64_t bytesDelta)
+ {
+ progressWin.notifyProgress(bytesDelta);
+ progressWin.requestUiRefresh(); //throw DownloadProgressWindow::CancelPressed
+ };
+
+ HttpInputStream httpStreamIn = sendHttpGet(directDownloadUrl, ffsUpdateCheckUserAgent); //throw SysError
+
+ FileOutput fileStreamOut(installerLocalPathTmp, FileOutput::ACC_OVERWRITE); //throw FileError
+
+ unbufferedStreamCopy(httpStreamIn, //throw SysError
+ fileStreamOut, //throw FileError
+ notifyProgress); //optional
+ }
+
+ //operation finished: move temp file -> this should work transactionally:
+ if (!::MoveFileEx(applyLongPathPrefix(installerLocalPathTmp).c_str(), //__in LPCTSTR lpExistingFileName,
+ applyLongPathPrefix(installerLocalPath ).c_str(), //__in_opt LPCTSTR lpNewFileName,
+ MOVEFILE_REPLACE_EXISTING)) //__in DWORD dwFlags
+ THROW_LAST_FILE_ERROR(replaceCpy(replaceCpy(_("Cannot move file %x to %y."),
+ L"%x", L"\n" + fmtPath(installerLocalPathTmp)),
+ L"%y", L"\n" + fmtPath(installerLocalPath)), L"MoveFileEx");
+ }
+
+ const Zstring commandLine = Zstr("\"") + installerLocalPath + Zstr("\"") +
+ Zstr(" /silent") +
+ Zstr(" /dir=\"") + getInstallDirPath() + Zstr("\"") +
+ Zstr(" /ffs_type=") + (isPortableVersion() ? Zstr("portable") : Zstr("local")) +
+ Zstr(" /autoupdate");
+#ifndef NDEBUG
+ __debugbreak(); //don't mess up the dev-build running a silent install
+#endif
+ shellExecute(commandLine, ExecutionType::EXEC_TYPE_SYNC); //throw FileError
+ }
+ catch (const SysError& e)
+ {
+ showNotificationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg().setMainInstructions(_("Failed to retrieve update information.")).
+ setDetailInstructions(e.toString()));
+ }
+ catch (const FileError& e)
{
- const std::string buffer = sendHttpPost(L"http://www.freefilesync.org/get_latest_changes.php", L"FFS-Update-Check", { { "since", utfCvrtTo<std::string>(zen::ffsVersion) } }); //throw SysError
- return utfCvrtTo<std::wstring>(buffer);
+ showNotificationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString()));
}
- catch (zen::SysError&) { assert(false); return std::wstring(); }
+ catch (DownloadProgressWindow::CancelPressed&) { return; }
}
+#endif
-void showUpdateAvailableDialog(wxWindow* parent, const std::wstring& onlineVersion, const std::wstring& onlineChangeLog)
+void showUpdateAvailableDialog(wxWindow* parent, const std::string& onlineVersion)
{
+ std::wstring updateDetailsMsg;
+ try
+ {
+ try //harmonize with wxHTTP: get_latest_changes.php must be accessible without https!!!
+ {
+ const std::string buf = sendHttpPost(L"http://www.freefilesync.org/get_latest_changes.php", ffsUpdateCheckUserAgent,
+ { { "since", zen::ffsVersion } }).readAll(); //throw SysError
+ updateDetailsMsg = utfCvrtTo<std::wstring>(buf);
+ }
+ catch (const zen::SysError& e) { throw FileError(_("Failed to retrieve update information."), e.toString()); }
+
+#ifdef ZEN_WIN
+ Opt<DonationInfo> donationInfo = getDonationInfo(); //throw FileError
+ if (!donationInfo)
+ throw FileError(_("Automatic updates:") + L" " + _("Disabled") + L"\n(" + _("Requires FreeFileSync Donation Edition") + L")");
+
+ std::wstring directDownloadUrl;
+ Zstring installerFileName;
+ std::uint64_t installerFileSize = 0;
+ try
+ {
+ const std::string buf = sendHttpPost(L"http://www.freefilesync.org/donate/get_installer_url.php", ffsUpdateCheckUserAgent,
+ xWwwFormUrlDecode(donationInfo->installerDirectDownloadUrlParamsEncoded)).readAll(); //throw SysError
+
+ //contains "download_page_url, direct_download_url, file_name, file_size" or "error"; coordinate with donate/get_installer_url.php
+ for (const std::pair<std::string, std::string>& nv : xWwwFormUrlDecode(buf))
+ if (nv.first == "direct_download_url")
+ directDownloadUrl = utfCvrtTo<std::wstring>(nv.second);
+ else if (nv.first == "file_name")
+ installerFileName = utfCvrtTo<Zstring>(nv.second);
+ else if (nv.first == "file_size")
+ installerFileSize = stringTo<std::uint64_t>(nv.second);
+ else if (nv.first == "error")
+ throw SysError(utfCvrtTo<std::wstring>(nv.second)); //e.g. expiration info
+
+ //check for empty/corrupted HTTP POST result (AV-software!)
+ if (directDownloadUrl.empty() || installerFileName.empty() || installerFileSize == 0)
+ throw SysError(L"Update server returned incomplete data.");
+ }
+ catch (const SysError& e) { throw FileError(_("Failed to retrieve update information.") + L"\n" + e.toString()); }
+
+ switch (showConfirmationDialog3(parent, DialogInfoType::INFO, PopupDialogCfg3().
+ setIcon(getResourceImage(L"update_available")).
+ setTitle(_("Check for Program Updates")).
+ setMainInstructions(_("A new version of FreeFileSync is available:") + L" " + utfCvrtTo<std::wstring>(onlineVersion) + L"\n"
+ + _("Auto-update now or download manually from the FreeFileSync home page?")).
+ setDetailInstructions(updateDetailsMsg),
+ _("&Auto-update"),
+ _("&Home page")))
+ {
+ case ConfirmationButton3::DO_IT:
+ runFreeFileSyncAutoUpdate(parent, directDownloadUrl, installerFileName, installerFileSize);
+ break;
+ case ConfirmationButton3::DONT_DO_IT:
+ openDonationEditionThankYouPage(parent);
+ break;
+ case ConfirmationButton3::CANCEL:
+ break;
+ }
+ return;
+#endif
+ }
+ catch (const FileError& e) //fall back to regular update info dialog:
+ {
+ updateDetailsMsg = e.toString() + L"\n\n" + updateDetailsMsg;
+ }
+
switch (showConfirmationDialog(parent, DialogInfoType::INFO, PopupDialogCfg().
- setIcon(getResourceImage(L"download_update")).
+ setIcon(getResourceImage(L"update_available")).
setTitle(_("Check for Program Updates")).
- setMainInstructions(_("A new version of FreeFileSync is available:") + L" " + onlineVersion + L"\n" + _("Download now?")).
- setDetailInstructions(onlineChangeLog),
+ setMainInstructions(_("A new version of FreeFileSync is available:") + L" " + utfCvrtTo<std::wstring>(onlineVersion) + L"\n" + _("Download now?")).
+ setDetailInstructions(updateDetailsMsg),
_("&Download")))
{
case ConfirmationButton::DO_IT:
@@ -192,13 +314,31 @@ void showUpdateAvailableDialog(wxWindow* parent, const std::wstring& onlineVersi
break;
}
}
+
+
+//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!!!
+ const std::string buffer = sendHttpPost(L"http://www.freefilesync.org/get_latest_version_number.php", ffsUpdateCheckUserAgent, postParams).readAll(); //throw SysError
+ return trimCpy(buffer);
+}
+
+
+std::vector<size_t> parseVersion(const std::string& version)
+{
+ std::vector<size_t> output;
+ for (const std::string& digit : split(version, FFS_VERSION_SEPARATOR))
+ output.push_back(stringTo<size_t>(digit));
+ return output;
+}
}
-bool zen::haveNewerVersionOnline(const std::wstring& onlineVersion)
+bool zen::haveNewerVersionOnline(const std::string& onlineVersion)
{
- std::vector<size_t> current = parseVersion(zen::ffsVersion);
- std::vector<size_t> online = parseVersion(onlineVersion);
+ const std::vector<size_t> current = parseVersion(zen::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
return true;
@@ -220,18 +360,18 @@ void zen::disableUpdateCheck(time_t& lastUpdateCheck)
}
-void zen::checkForUpdateNow(wxWindow* parent, std::wstring& lastOnlineVersion, std::wstring& lastOnlineChangeLog)
+void zen::checkForUpdateNow(wxWindow* parent, std::string& lastOnlineVersion)
{
try
{
- const std::wstring onlineVersion = getOnlineVersion(geHttpPostParameters()); //throw SysError
- lastOnlineVersion = onlineVersion;
- lastOnlineChangeLog = haveNewerVersionOnline(onlineVersion) ? getOnlineChangelogDelta() : L"";
+ const std::string onlineVersion = getOnlineVersion(geHttpPostParameters()); //throw SysError
+ lastOnlineVersion = onlineVersion;
if (haveNewerVersionOnline(onlineVersion))
- showUpdateAvailableDialog(parent, lastOnlineVersion, lastOnlineChangeLog);
+ showUpdateAvailableDialog(parent, lastOnlineVersion);
else
showNotificationDialog(parent, DialogInfoType::INFO, PopupDialogCfg().
+ setIcon(getResourceImage(L"update_check")).
setTitle(_("Check for Program Updates")).
setMainInstructions(_("FreeFileSync is up to date.")));
}
@@ -239,8 +379,7 @@ void zen::checkForUpdateNow(wxWindow* parent, std::wstring& lastOnlineVersion, s
{
if (internetIsAlive())
{
- lastOnlineVersion = L"Unknown";
- lastOnlineChangeLog.clear();
+ lastOnlineVersion = "Unknown";
switch (showConfirmationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg().
setTitle(_("Check for Program Updates")).
@@ -284,9 +423,9 @@ std::shared_ptr<UpdateCheckResultPrep> zen::periodicUpdateCheckPrepare()
struct zen::UpdateCheckResult
{
UpdateCheckResult() {}
- UpdateCheckResult(const std::wstring& ver, const Opt<zen::SysError>& err, bool alive) : onlineVersion(ver), error(err), internetIsAlive(alive) {}
+ UpdateCheckResult(const std::string& ver, const Opt<zen::SysError>& err, bool alive) : onlineVersion(ver), error(err), internetIsAlive(alive) {}
- std::wstring onlineVersion;
+ std::string onlineVersion;
Opt<zen::SysError> error;
bool internetIsAlive = false;
};
@@ -298,12 +437,12 @@ std::shared_ptr<UpdateCheckResult> zen::periodicUpdateCheckRunAsync(const Update
try
{
//access is thread-safe on Windows only!
- const std::wstring onlineVersion = getOnlineVersion(resultPrep->postParameters); //throw SysError
+ const std::string onlineVersion = getOnlineVersion(resultPrep->postParameters); //throw SysError
return std::make_shared<UpdateCheckResult>(onlineVersion, NoValue(), true);
}
catch (const zen::SysError& e)
{
- return std::make_shared<UpdateCheckResult>(L"", e, internetIsAlive());
+ return std::make_shared<UpdateCheckResult>("", e, internetIsAlive());
}
#else
return nullptr;
@@ -312,7 +451,7 @@ std::shared_ptr<UpdateCheckResult> zen::periodicUpdateCheckRunAsync(const Update
//run on main thread:
-void zen::periodicUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, std::wstring& lastOnlineVersion, std::wstring& lastOnlineChangeLog, const UpdateCheckResult* resultAsync)
+void zen::periodicUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, std::string& lastOnlineVersion, const UpdateCheckResult* resultAsync)
{
#ifdef ZEN_WIN
const UpdateCheckResult& result = *resultAsync;
@@ -332,19 +471,17 @@ void zen::periodicUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, std
if (!result.error)
{
- lastUpdateCheck = getVersionCheckCurrentTime();
- lastOnlineVersion = result.onlineVersion;
- lastOnlineChangeLog = haveNewerVersionOnline(result.onlineVersion) ? getOnlineChangelogDelta() : L"";
+ lastUpdateCheck = getVersionCheckCurrentTime();
+ lastOnlineVersion = result.onlineVersion;
if (haveNewerVersionOnline(result.onlineVersion))
- showUpdateAvailableDialog(parent, lastOnlineVersion, lastOnlineChangeLog);
+ showUpdateAvailableDialog(parent, lastOnlineVersion);
}
else
{
if (result.internetIsAlive)
{
- lastOnlineVersion = L"Unknown";
- lastOnlineChangeLog.clear();
+ lastOnlineVersion = "Unknown";
switch (showConfirmationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg().
setTitle(_("Check for Program Updates")).
@@ -362,3 +499,21 @@ void zen::periodicUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, std
//else: ignore this error
}
}
+
+
+#ifdef ZEN_WIN
+void zen::openDonationEditionThankYouPage(wxWindow* parent)
+{
+ try
+ {
+ if (Opt<DonationInfo> donationInfo = getDonationInfo()) //throw FileError
+ wxLaunchDefaultBrowser(donationInfo->ffsThankYouPageUrl); //don't need urlencode()
+ else
+ throw FileError(L"Corrupted installation: transaction ID not found");
+ }
+ catch (const FileError& e)
+ {
+ showNotificationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString()));
+ }
+}
+#endif
diff --git a/FreeFileSync/Source/ui/version_check.h b/FreeFileSync/Source/ui/version_check.h
index 01c19519..6dfe1ff9 100644
--- a/FreeFileSync/Source/ui/version_check.h
+++ b/FreeFileSync/Source/ui/version_check.h
@@ -10,14 +10,14 @@
#include <functional>
#include <memory>
#include <wx/window.h>
-
+#include "version_check_impl.h"
namespace zen
{
bool updateCheckActive (time_t lastUpdateCheck);
void disableUpdateCheck(time_t& lastUpdateCheck);
-bool haveNewerVersionOnline(const std::wstring& onlineVersion);
-
+bool haveNewerVersionOnline(const std::string& onlineVersion);
+//----------------------------------------------------------------------------
//periodic update check:
bool shouldRunPeriodicUpdateCheck(time_t lastUpdateCheck);
@@ -29,15 +29,15 @@ std::shared_ptr<UpdateCheckResultPrep> periodicUpdateCheckPrepare();
//run on worker thread: (long-running part of the check)
std::shared_ptr<UpdateCheckResult> periodicUpdateCheckRunAsync(const UpdateCheckResultPrep* resultPrep);
//run on main thread:
-void periodicUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck,
- std::wstring& lastOnlineVersion,
- std::wstring& lastOnlineChangeLog,
+void periodicUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, std::string& lastOnlineVersion,
const UpdateCheckResult* resultAsync);
-
//----------------------------------------------------------------------------
-
//call from main thread:
-void checkForUpdateNow(wxWindow* parent, std::wstring& lastOnlineVersion, std::wstring& lastOnlineChangeLog);
+void checkForUpdateNow(wxWindow* parent, std::string& lastOnlineVersion);
+//----------------------------------------------------------------------------
+#ifdef ZEN_WIN
+ void openDonationEditionThankYouPage(wxWindow* parent);
+#endif
}
#endif //VERSION_CHECK_H_324872374893274983275
diff --git a/FreeFileSync/Source/ui/version_check_impl.h b/FreeFileSync/Source/ui/version_check_impl.h
index 904da735..58412517 100644
--- a/FreeFileSync/Source/ui/version_check_impl.h
+++ b/FreeFileSync/Source/ui/version_check_impl.h
@@ -16,14 +16,14 @@ 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!)
- time_t id = 0;
- const wchar_t* first = zen::ffsVersion;
- const wchar_t* last = first + zen::strLength(ffsVersion);
- std::for_each(first, last, [&](wchar_t c)
+ int id = 0;
+ const char* first = zen::ffsVersion;
+ const char* last = first + zen::strLength(ffsVersion);
+ std::for_each(first, last, [&](char c)
{
id *= 11;
- if (L'0' <= c && c <= L'9')
- id += c - L'0';
+ if ('0' <= c && c <= '9')
+ id += c - '0';
else
{
assert(c == FFS_VERSION_SEPARATOR);
diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h
index 3aefebd8..f34673b8 100644
--- a/FreeFileSync/Source/version/version.h
+++ b/FreeFileSync/Source/version/version.h
@@ -3,8 +3,8 @@
namespace zen
{
-const wchar_t ffsVersion[] = L"8.6"; //internal linkage!
-const wchar_t FFS_VERSION_SEPARATOR = L'.';
+const char ffsVersion[] = "8.7"; //internal linkage!
+const char FFS_VERSION_SEPARATOR = '.';
}
#endif
diff --git a/wx+/async_task.h b/wx+/async_task.h
index 915f9602..7ac03949 100644
--- a/wx+/async_task.h
+++ b/wx+/async_task.h
@@ -24,6 +24,9 @@ Run a task in an async thread, but process result in GUI event loop
2. schedule async task and synchronous continuation:
guiQueue.processAsync(evalAsync, evalOnGui);
+
+Alternative: use wxWidgets' inter-thread communication (wxEvtHandler::QueueEvent) https://wiki.wxwidgets.org/Inter-Thread_and_Inter-Process_communication
+ => don't bother, probably too many MT race conditions lurking around
*/
namespace impl
@@ -74,7 +77,7 @@ public:
void add(Fun&& evalAsync, Fun2&& evalOnGui)
{
using ResultType = decltype(evalAsync());
- tasks.push_back(std::make_unique<ConcreteTask<ResultType, Fun2>>(zen::runAsync(std::forward<Fun>(evalAsync)), std::forward<Fun2>(evalOnGui)));
+ tasks_.push_back(std::make_unique<ConcreteTask<ResultType, Fun2>>(zen::runAsync(std::forward<Fun>(evalAsync)), std::forward<Fun2>(evalOnGui)));
}
//equivalent to "evalOnGui(evalAsync())"
// -> evalAsync: the usual thread-safety requirements apply!
@@ -82,14 +85,14 @@ public:
void evalResults() //call from gui thread repreatedly
{
- if (!inRecursion) //prevent implicit recursion, e.g. if we're called from an idle event and spawn another one within the callback below
+ if (!inRecursion_) //prevent implicit recursion, e.g. if we're called from an idle event and spawn another one within the callback below
{
- inRecursion = true;
- ZEN_ON_SCOPE_EXIT(inRecursion = false);
+ inRecursion_ = true;
+ ZEN_ON_SCOPE_EXIT(inRecursion_ = false);
std::vector<std::unique_ptr<Task>> readyTasks; //Reentrancy; access to AsyncTasks::add is not protected! => evaluate outside erase_if
- erase_if(tasks, [&](std::unique_ptr<Task>& task)
+ erase_if(tasks_, [&](std::unique_ptr<Task>& task)
{
if (task->resultReady())
{
@@ -104,14 +107,14 @@ public:
}
}
- bool empty() const { return tasks.empty(); }
+ bool empty() const { return tasks_.empty(); }
private:
AsyncTasks (const AsyncTasks&) = delete;
AsyncTasks& operator=(const AsyncTasks&) = delete;
- bool inRecursion = false;
- std::vector<std::unique_ptr<Task>> tasks;
+ bool inRecursion_ = false;
+ std::vector<std::unique_ptr<Task>> tasks_;
};
}
@@ -119,27 +122,27 @@ private:
class AsyncGuiQueue : private wxEvtHandler
{
public:
- AsyncGuiQueue() { timer.Connect(wxEVT_TIMER, wxEventHandler(AsyncGuiQueue::onTimerEvent), nullptr, this); }
+ AsyncGuiQueue() { timer_.Connect(wxEVT_TIMER, wxEventHandler(AsyncGuiQueue::onTimerEvent), nullptr, this); }
template <class Fun, class Fun2>
void processAsync(Fun&& evalAsync, Fun2&& evalOnGui)
{
- asyncTasks.add(std::forward<Fun >(evalAsync),
- std::forward<Fun2>(evalOnGui));
- if (!timer.IsRunning())
- timer.Start(50 /*unit: [ms]*/);
+ asyncTasks_.add(std::forward<Fun >(evalAsync),
+ std::forward<Fun2>(evalOnGui));
+ if (!timer_.IsRunning())
+ timer_.Start(50 /*unit: [ms]*/);
}
private:
void onTimerEvent(wxEvent& event) //schedule and run long-running tasks asynchronously
{
- asyncTasks.evalResults(); //process results on GUI queue
- if (asyncTasks.empty())
- timer.Stop();
+ asyncTasks_.evalResults(); //process results on GUI queue
+ if (asyncTasks_.empty())
+ timer_.Stop();
}
- impl::AsyncTasks asyncTasks;
- wxTimer timer; //don't use wxWidgets' idle handling => repeated idle requests/consumption hogs 100% cpu!
+ impl::AsyncTasks asyncTasks_;
+ wxTimer timer_; //don't use wxWidgets' idle handling => repeated idle requests/consumption hogs 100% cpu!
};
}
diff --git a/wx+/file_drop.h b/wx+/file_drop.h
index c3ffe09c..57880ce2 100644
--- a/wx+/file_drop.h
+++ b/wx+/file_drop.h
@@ -134,6 +134,13 @@ public:
private:
bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& fileArray) override
{
+ /*Linux, MTP: we get an empty file array
+ => switching to wxTextDropTarget won't help (much): we'd get the format
+ mtp://[usb:001,002]/Telefonspeicher/Folder/file.txt
+ instead of
+ /run/user/1000/gvfs/mtp:host=%5Busb%3A001%2C002%5D/Telefonspeicher/Folder/file.txt
+ */
+
//wxPoint clientDropPos(x, y)
std::vector<Zstring> filePaths;
for (const wxString& file : fileArray)
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index 13c06704..5d393f08 100644
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -359,8 +359,18 @@ private:
void onPaintEvent(wxPaintEvent& event)
{
+#ifndef NDEBUG
+#ifdef ZEN_WIN
+ if (runningPaintEvent_ == true) //looks like showing the assert window here would quit the debug session
+ __debugbreak();
+#else
+ assert(runningPaintEvent_ == false); //catch unexpected recursion, e.g.: getIconByIndex() seems to run a message loop in rare cases!
+#endif
+ runningPaintEvent_ = true;
+ ZEN_ON_SCOPE_EXIT(runningPaintEvent_ = false);
+#endif
//wxAutoBufferedPaintDC dc(this); -> this one happily fucks up for RTL layout by not drawing the first column (x = 0)!
- BufferedPaintDC dc(*this, doubleBuffer);
+ BufferedPaintDC dc(*this, doubleBuffer_);
assert(GetSize() == GetClientSize());
@@ -378,7 +388,10 @@ private:
void onEraseBackGround(wxEraseEvent& event) {}
Grid& parent_;
- Opt<wxBitmap> doubleBuffer;
+ Opt<wxBitmap> doubleBuffer_;
+#ifndef NDEBUG
+ bool runningPaintEvent_ = false;
+#endif
};
//----------------------------------------------------------------------------------------------------------------
@@ -427,7 +440,7 @@ class Grid::RowLabelWin : public SubWindow
public:
RowLabelWin(Grid& parent) :
SubWindow(parent),
- rowHeight(parent.GetCharHeight() + 2 + 1) {} //default height; don't call any functions on "parent" other than those from wxWindow during construction!
+ rowHeight_(parent.GetCharHeight() + 2 + 1) {} //default height; don't call any functions on "parent" other than those from wxWindow during construction!
//2 for some more space, 1 for bottom border (gives 15 + 2 + 1 on Windows, 17 + 2 + 1 on Ubuntu)
int getBestWidth(ptrdiff_t rowFrom, ptrdiff_t rowTo)
@@ -444,27 +457,27 @@ public:
return bestWidth;
}
- size_t getLogicalHeight() const { return refParent().getRowCount() * rowHeight; }
+ size_t getLogicalHeight() const { return refParent().getRowCount() * rowHeight_; }
ptrdiff_t getRowAtPos(ptrdiff_t posY) const //returns < 0 on invalid input, else row number within: [0, rowCount]; rowCount if out of range
{
- if (posY >= 0 && rowHeight > 0)
+ if (posY >= 0 && rowHeight_ > 0)
{
- const size_t row = posY / rowHeight;
+ const size_t row = posY / rowHeight_;
return std::min(row, refParent().getRowCount());
}
return -1;
}
- int getRowHeight() const { return rowHeight; } //guarantees to return size >= 1 !
- void setRowHeight(int height) { assert(height > 0); rowHeight = std::max(1, height); }
+ int getRowHeight() const { return rowHeight_; } //guarantees to return size >= 1 !
+ void setRowHeight(int height) { assert(height > 0); rowHeight_ = std::max(1, height); }
wxRect getRowLabelArea(size_t row) const //returns empty rect if row not found
{
assert(GetClientAreaOrigin() == wxPoint());
if (row < refParent().getRowCount())
- return wxRect(wxPoint(0, rowHeight * row),
- wxSize(GetClientSize().GetWidth(), rowHeight));
+ return wxRect(wxPoint(0, rowHeight_ * row),
+ wxSize(GetClientSize().GetWidth(), rowHeight_));
return wxRect();
}
@@ -473,8 +486,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::make_pair(std::max(yFrom / rowHeight_, 0),
+ std::min<ptrdiff_t>((yTo / rowHeight_) + 1, refParent().getRowCount()));
}
private:
@@ -552,7 +565,7 @@ private:
void onMouseMovement(wxMouseEvent& event) override { refParent().redirectRowLabelEvent(event); }
void onMouseLeftUp (wxMouseEvent& event) override { refParent().redirectRowLabelEvent(event); }
- int rowHeight;
+ int rowHeight_;
};
@@ -670,21 +683,21 @@ private:
{
if (auto dataView = refParent().getDataProvider())
{
- const bool isHighlighted = activeResizing ? col == activeResizing ->getColumn () : //highlight column on mouse-over
- activeClickOrMove ? col == activeClickOrMove->getColumnFrom() :
- highlightCol ? col == *highlightCol :
+ const bool isHighlighted = activeResizing_ ? col == activeResizing_ ->getColumn () : //highlight_ column on mouse-over
+ activeClickOrMove_ ? col == activeClickOrMove_->getColumnFrom() :
+ highlightCol_ ? col == *highlightCol_ :
false;
RecursiveDcClipper clip(dc, rect);
dataView->renderColumnLabel(refParent(), dc, rect, colType, isHighlighted);
//draw move target location
- if (refParent().allowColumnMove)
- if (activeClickOrMove && activeClickOrMove->isRealMove())
+ if (refParent().allowColumnMove_)
+ if (activeClickOrMove_ && activeClickOrMove_->isRealMove())
{
- if (col + 1 == activeClickOrMove->refColumnTo()) //handle pos 1, 2, .. up to "at end" position
+ if (col + 1 == activeClickOrMove_->refColumnTo()) //handle pos 1, 2, .. up to "at end" position
dc.GradientFillLinear(wxRect(rect.GetTopRight(), rect.GetBottomRight() + wxPoint(-2, 0)), getColorLabelGradientFrom(), *wxBLUE, wxSOUTH);
- else if (col == activeClickOrMove->refColumnTo() && col == 0) //pos 0
+ else if (col == activeClickOrMove_->refColumnTo() && col == 0) //pos 0
dc.GradientFillLinear(wxRect(rect.GetTopLeft(), rect.GetBottomLeft() + wxPoint(2, 0)), getColorLabelGradientFrom(), *wxBLUE, wxSOUTH);
}
}
@@ -695,8 +708,8 @@ private:
if (FindFocus() != &refParent().getMainWin())
refParent().getMainWin().SetFocus();
- activeResizing.reset();
- activeClickOrMove.reset();
+ activeResizing_.reset();
+ activeClickOrMove_.reset();
if (Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition()))
{
@@ -704,26 +717,26 @@ private:
{
if (!event.LeftDClick()) //double-clicks never seem to arrive here; why is this checked at all???
if (Opt<int> colWidth = refParent().getColWidth(action->col))
- activeResizing = std::make_unique<ColumnResizing>(*this, action->col, *colWidth, event.GetPosition().x);
+ activeResizing_ = std::make_unique<ColumnResizing>(*this, action->col, *colWidth, event.GetPosition().x);
}
else //a move or single click
- activeClickOrMove = std::make_unique<ColumnMove>(*this, action->col, event.GetPosition().x);
+ activeClickOrMove_ = std::make_unique<ColumnMove>(*this, action->col, event.GetPosition().x);
}
event.Skip();
}
void onMouseLeftUp(wxMouseEvent& event) override
{
- activeResizing.reset(); //nothing else to do, actual work done by onMouseMovement()
+ activeResizing_.reset(); //nothing else to do, actual work done by onMouseMovement()
- if (activeClickOrMove)
+ if (activeClickOrMove_)
{
- if (activeClickOrMove->isRealMove())
+ if (activeClickOrMove_->isRealMove())
{
- if (refParent().allowColumnMove)
+ if (refParent().allowColumnMove_)
{
- const size_t colFrom = activeClickOrMove->getColumnFrom();
- size_t colTo = activeClickOrMove->refColumnTo();
+ const size_t colFrom = activeClickOrMove_->getColumnFrom();
+ size_t colTo = activeClickOrMove_->refColumnTo();
if (colTo > colFrom) //simulate "colFrom" deletion
--colTo;
@@ -733,10 +746,10 @@ private:
}
else //notify single label click
{
- if (const Opt<ColumnType> colType = refParent().colToType(activeClickOrMove->getColumnFrom()))
+ if (const Opt<ColumnType> colType = refParent().colToType(activeClickOrMove_->getColumnFrom()))
sendEventNow(GridLabelClickEvent(EVENT_GRID_COL_LABEL_MOUSE_LEFT, event, *colType));
}
- activeClickOrMove.reset();
+ activeClickOrMove_.reset();
}
refParent().updateWindowSizes(); //looks strange if done during onMouseMovement()
@@ -746,8 +759,8 @@ private:
void onMouseCaptureLost(wxMouseCaptureLostEvent& event) override
{
- activeResizing.reset();
- activeClickOrMove.reset();
+ activeResizing_.reset();
+ activeClickOrMove_.reset();
Refresh();
//event.Skip(); -> we DID handle it!
}
@@ -770,10 +783,10 @@ private:
void onMouseMovement(wxMouseEvent& event) override
{
- if (activeResizing)
+ if (activeResizing_)
{
- const auto col = activeResizing->getColumn();
- const int newWidth = activeResizing->getStartWidth() + event.GetPosition().x - activeResizing->getStartPosX();
+ const auto col = activeResizing_->getColumn();
+ const int newWidth = activeResizing_->getStartWidth() + event.GetPosition().x - activeResizing_->getStartPosX();
//set width tentatively
refParent().setColumnWidth(newWidth, col, ALLOW_GRID_EVENT);
@@ -785,23 +798,23 @@ private:
refParent().Refresh(); //refresh columns on main grid as well!
}
- else if (activeClickOrMove)
+ else if (activeClickOrMove_)
{
const int clientPosX = event.GetPosition().x;
- if (std::abs(clientPosX - activeClickOrMove->getStartPosX()) > COLUMN_MOVE_DELAY) //real move (not a single click)
+ if (std::abs(clientPosX - activeClickOrMove_->getStartPosX()) > COLUMN_MOVE_DELAY) //real move (not a single click)
{
- activeClickOrMove->setRealMove();
+ activeClickOrMove_->setRealMove();
const ptrdiff_t col = refParent().clientPosToMoveTargetColumn(event.GetPosition());
if (col >= 0)
- activeClickOrMove->refColumnTo() = col;
+ activeClickOrMove_->refColumnTo() = col;
}
}
else
{
if (const Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition()))
{
- highlightCol = action->col;
+ highlightCol_ = action->col;
if (action->wantResize)
SetCursor(wxCURSOR_SIZEWE); //set window-local only! :)
@@ -810,7 +823,7 @@ private:
}
else
{
- highlightCol = NoValue();
+ highlightCol_ = NoValue();
SetCursor(*wxSTANDARD_CURSOR);
}
}
@@ -832,7 +845,7 @@ private:
void onLeaveWindow(wxMouseEvent& event) override
{
- highlightCol = NoValue(); //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight is drawn unconditionally during move/resize!
+ highlightCol_ = NoValue(); //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight_ is drawn unconditionally during move/resize!
Refresh();
event.Skip();
}
@@ -853,9 +866,9 @@ private:
event.Skip();
}
- std::unique_ptr<ColumnResizing> activeResizing;
- std::unique_ptr<ColumnMove> activeClickOrMove;
- Opt<size_t> highlightCol; //column during mouse-over
+ std::unique_ptr<ColumnResizing> activeResizing_;
+ std::unique_ptr<ColumnMove> activeClickOrMove_;
+ Opt<size_t> highlightCol_; //column during mouse-over
};
//----------------------------------------------------------------------------------------------------------------
@@ -877,16 +890,16 @@ public:
Connect(EVENT_GRID_HAS_SCROLLED, wxEventHandler(MainWin::onRequestWindowUpdate), nullptr, this);
}
- ~MainWin() { assert(!gridUpdatePending); }
+ ~MainWin() { assert(!gridUpdatePending_); }
- size_t getCursor() const { return cursorRow; }
- size_t getAnchor() const { return selectionAnchor; }
+ size_t getCursor() const { return cursorRow_; }
+ size_t getAnchor() const { return selectionAnchor_; }
void setCursor(size_t newCursorRow, size_t newAnchorRow)
{
- cursorRow = newCursorRow;
- selectionAnchor = newAnchorRow;
- activeSelection.reset(); //e.g. user might search with F3 while holding down left mouse button
+ cursorRow_ = newCursorRow;
+ selectionAnchor_ = newAnchorRow;
+ activeSelection_.reset(); //e.g. user might search with F3 while holding down left mouse button
}
private:
@@ -948,25 +961,25 @@ private:
HoverArea getRowHoverToDraw(ptrdiff_t row) const
{
- if (activeSelection)
+ if (activeSelection_)
{
- if (activeSelection->getFirstClick().row_ == row)
- return activeSelection->getFirstClick().hoverArea_;
+ if (activeSelection_->getFirstClick().row_ == row)
+ return activeSelection_->getFirstClick().hoverArea_;
}
- else if (highlight.row == row)
- return highlight.rowHover;
+ else if (highlight_.row == row)
+ return highlight_.rowHover;
return HoverArea::NONE;
}
bool drawAsSelected(size_t row) const
{
- if (activeSelection) //check if user is currently selecting with mouse
+ if (activeSelection_) //check if user is currently selecting with mouse
{
- const size_t rowFrom = std::min(activeSelection->getStartRow(), activeSelection->getCurrentRow());
- const size_t rowTo = std::max(activeSelection->getStartRow(), activeSelection->getCurrentRow());
+ const size_t rowFrom = std::min(activeSelection_->getStartRow(), activeSelection_->getCurrentRow());
+ const size_t rowTo = std::max(activeSelection_->getStartRow(), activeSelection_->getCurrentRow());
if (rowFrom <= row && row <= rowTo)
- return activeSelection->isPositiveSelect(); //overwrite default
+ return activeSelection_->isPositiveSelect(); //overwrite default
}
return refParent().isSelected(row);
}
@@ -1009,15 +1022,15 @@ private:
if (!event.RightDown() || !refParent().isSelected(row)) //do NOT start a new selection if user right-clicks on a selected area!
{
if (event.ControlDown())
- activeSelection = std::make_unique<MouseSelection>(*this, row, !refParent().isSelected(row), mouseEvent);
+ activeSelection_ = std::make_unique<MouseSelection>(*this, row, !refParent().isSelected(row), mouseEvent);
else if (event.ShiftDown())
{
- activeSelection = std::make_unique<MouseSelection>(*this, selectionAnchor, true, mouseEvent);
+ activeSelection_ = std::make_unique<MouseSelection>(*this, selectionAnchor_, true, mouseEvent);
refParent().clearSelection(ALLOW_GRID_EVENT);
}
else
{
- activeSelection = std::make_unique<MouseSelection>(*this, row, true, mouseEvent);
+ activeSelection_ = std::make_unique<MouseSelection>(*this, row, true, mouseEvent);
refParent().clearSelection(ALLOW_GRID_EVENT);
}
}
@@ -1032,31 +1045,31 @@ private:
void onMouseUp(wxMouseEvent& event)
{
- if (activeSelection)
+ if (activeSelection_)
{
const size_t rowCount = refParent().getRowCount();
if (rowCount > 0)
{
- if (activeSelection->getCurrentRow() < rowCount)
+ if (activeSelection_->getCurrentRow() < rowCount)
{
- cursorRow = activeSelection->getCurrentRow();
- selectionAnchor = activeSelection->getStartRow(); //allowed to be "out of range"
+ cursorRow_ = activeSelection_->getCurrentRow();
+ selectionAnchor_ = activeSelection_->getStartRow(); //allowed to be "out of range"
}
- else if (activeSelection->getStartRow() < rowCount) //don't change cursor if "to" and "from" are out of range
+ else if (activeSelection_->getStartRow() < rowCount) //don't change cursor if "to" and "from" are out of range
{
- cursorRow = rowCount - 1;
- selectionAnchor = activeSelection->getStartRow(); //allowed to be "out of range"
+ cursorRow_ = rowCount - 1;
+ selectionAnchor_ = activeSelection_->getStartRow(); //allowed to be "out of range"
}
else //total selection "out of range"
- selectionAnchor = cursorRow;
+ selectionAnchor_ = cursorRow_;
}
//slight deviation from Explorer: change cursor while dragging mouse! -> unify behavior with shift + direction keys
- refParent().selectRangeAndNotify(activeSelection->getStartRow (), //from
- activeSelection->getCurrentRow(), //to
- activeSelection->isPositiveSelect(),
- &activeSelection->getFirstClick());
- activeSelection.reset();
+ refParent().selectRangeAndNotify(activeSelection_->getStartRow (), //from
+ activeSelection_->getCurrentRow(), //to
+ activeSelection_->isPositiveSelect(),
+ &activeSelection_->getFirstClick());
+ activeSelection_.reset();
}
if (auto prov = refParent().getDataProvider())
@@ -1070,7 +1083,7 @@ private:
sendEventNow(GridClickEvent(event.RightUp() ? EVENT_GRID_MOUSE_RIGHT_UP : EVENT_GRID_MOUSE_LEFT_UP, event, row, rowHover));
}
- //update highlight and tooltip: on OS X no mouse movement event is generated after a mouse button click (unlike on Windows)
+ //update highlight_ and tooltip: on OS X no mouse movement event is generated after a mouse button click (unlike on Windows)
event.SetPosition(ScreenToClient(wxGetMousePosition())); //mouse position may have changed within above callbacks (e.g. context menu was shown)!
onMouseMovement(event);
@@ -1080,8 +1093,8 @@ private:
void onMouseCaptureLost(wxMouseCaptureLostEvent& event) override
{
- activeSelection.reset();
- highlight.row = -1;
+ activeSelection_.reset();
+ highlight_.row = -1;
Refresh();
//event.Skip(); -> we DID handle it!
}
@@ -1104,14 +1117,14 @@ private:
}();
setToolTip(toolTip); //show even during mouse selection!
- if (activeSelection)
- activeSelection->evalMousePos(); //call on both mouse movement + timer event!
+ if (activeSelection_)
+ activeSelection_->evalMousePos(); //call on both mouse movement + timer event!
else
{
- refreshHighlight(highlight);
- highlight.row = row;
- highlight.rowHover = rowHover;
- refreshHighlight(highlight); //multiple Refresh() calls are condensed into single one!
+ refreshHighlight(highlight_);
+ highlight_.row = row;
+ highlight_.rowHover = rowHover;
+ refreshHighlight(highlight_); //multiple Refresh() calls are condensed into single one!
}
}
event.Skip();
@@ -1119,10 +1132,10 @@ private:
void onLeaveWindow(wxMouseEvent& event) override //wxEVT_LEAVE_WINDOW does not respect mouse capture!
{
- if (!activeSelection)
+ if (!activeSelection_)
{
- refreshHighlight(highlight);
- highlight.row = -1;
+ refreshHighlight(highlight_);
+ highlight_.row = -1;
}
event.Skip();
@@ -1138,8 +1151,8 @@ private:
wnd_(wnd), rowStart_(rowStart), rowCurrent_(rowStart), positiveSelect_(positiveSelect), firstClick_(firstClick)
{
wnd_.CaptureMouse();
- timer.Connect(wxEVT_TIMER, wxEventHandler(MouseSelection::onTimer), nullptr, this);
- timer.Start(100); //timer interval in ms
+ timer_.Connect(wxEVT_TIMER, wxEventHandler(MouseSelection::onTimer), nullptr, this);
+ timer_.Start(100); //timer interval in ms
evalMousePos();
}
~MouseSelection() { if (wnd_.HasCapture()) wnd_.ReleaseMouse(); }
@@ -1152,8 +1165,8 @@ private:
void evalMousePos()
{
const auto now = std::chrono::steady_clock::now();
- const double deltaSecs = std::chrono::duration<double>(now - lastEvalTime).count(); //unit: [sec]
- lastEvalTime = now;
+ const double deltaSecs = std::chrono::duration<double>(now - lastEvalTime_).count(); //unit: [sec]
+ lastEvalTime_ = now;
const wxPoint clientPos = wnd_.ScreenToClient(wxGetMousePosition());
const wxSize clientSize = wnd_.GetClientSize();
@@ -1182,14 +1195,14 @@ private:
toScroll = 0;
};
- autoScroll(overlapPixX, toScrollX);
- autoScroll(overlapPixY, toScrollY);
+ autoScroll(overlapPixX, toScrollX_);
+ autoScroll(overlapPixY, toScrollY_);
- if (static_cast<int>(toScrollX) != 0 || static_cast<int>(toScrollY) != 0)
+ if (static_cast<int>(toScrollX_) != 0 || static_cast<int>(toScrollY_) != 0)
{
- wnd_.refParent().scrollDelta(static_cast<int>(toScrollX), static_cast<int>(toScrollY)); //
- toScrollX -= static_cast<int>(toScrollX); //rounds down for positive numbers, up for negative,
- toScrollY -= static_cast<int>(toScrollY); //exactly what we want
+ wnd_.refParent().scrollDelta(static_cast<int>(toScrollX_), static_cast<int>(toScrollY_)); //
+ toScrollX_ -= static_cast<int>(toScrollX_); //rounds down for positive numbers, up for negative,
+ toScrollY_ -= static_cast<int>(toScrollY_); //exactly what we want
}
//select current row *after* scrolling
@@ -1214,10 +1227,10 @@ private:
ptrdiff_t rowCurrent_;
const bool positiveSelect_;
const GridClickEvent firstClick_;
- wxTimer timer;
- double toScrollX = 0; //count outstanding scroll unit fractions while dragging mouse
- double toScrollY = 0; //
- std::chrono::steady_clock::time_point lastEvalTime = std::chrono::steady_clock::now();
+ wxTimer timer_;
+ double toScrollX_ = 0; //count outstanding scroll unit fractions while dragging mouse
+ double toScrollY_ = 0; //
+ std::chrono::steady_clock::time_point lastEvalTime_ = std::chrono::steady_clock::now();
};
struct MouseHighlight
@@ -1236,12 +1249,12 @@ private:
//which *first* calls us, MainWin::ScrollWindow(), and *then* internally updates m_yScrollPosition
//=> we cannot use CalcUnscrolledPosition() here which gives the wrong/outdated value!!!
//=> we need to update asynchronously:
- //=> don't use plain async event => severe performance issues on wxGTK!
+ //=> don't send async event repeatedly => severe performance issues on wxGTK!
//=> can't use idle event neither: too few idle events on Windows, e.g. NO idle events while mouse drag-scrolling!
//=> solution: send single async event at most!
- if (!gridUpdatePending) //without guarding, the number of outstanding async events can become very high during scrolling!! test case: Ubuntu: 170; Windows: 20
+ if (!gridUpdatePending_) //without guarding, the number of outstanding async events can become very high during scrolling!! test case: Ubuntu: 170; Windows: 20
{
- gridUpdatePending = true;
+ gridUpdatePending_ = true;
wxCommandEvent scrollEvent(EVENT_GRID_HAS_SCROLLED);
AddPendingEvent(scrollEvent); //asynchronously call updateAfterScroll()
}
@@ -1249,8 +1262,8 @@ private:
void onRequestWindowUpdate(wxEvent& event)
{
- assert(gridUpdatePending);
- ZEN_ON_SCOPE_EXIT(gridUpdatePending = false);
+ assert(gridUpdatePending_);
+ ZEN_ON_SCOPE_EXIT(gridUpdatePending_ = false);
refParent().updateWindowSizes(false); //row label width has changed -> do *not* update scrollbars: recursion on wxGTK! -> still a problem, now that we're called async??
rowLabelWin_.Update(); //update while dragging scroll thumb
@@ -1267,19 +1280,19 @@ private:
void refreshHighlight(const MouseHighlight& hl)
{
const ptrdiff_t rowCount = refParent().getRowCount();
- if (0 <= hl.row && hl.row < rowCount && hl.rowHover != HoverArea::NONE) //no highlight? => NOP!
+ if (0 <= hl.row && hl.row < rowCount && hl.rowHover != HoverArea::NONE) //no highlight_? => NOP!
refreshRow(hl.row);
}
RowLabelWin& rowLabelWin_;
ColLabelWin& colLabelWin_;
- std::unique_ptr<MouseSelection> activeSelection; //bound while user is selecting with mouse
- MouseHighlight highlight; //current mouse highlight (superseeded by activeSelection if available)
+ std::unique_ptr<MouseSelection> activeSelection_; //bound while user is selecting with mouse
+ MouseHighlight highlight_; //current mouse highlight_ (superseeded by activeSelection_ if available)
- ptrdiff_t cursorRow = 0;
- size_t selectionAnchor = 0;
- bool gridUpdatePending = false;
+ ptrdiff_t cursorRow_ = 0;
+ size_t selectionAnchor_ = 0;
+ bool gridUpdatePending_ = false;
};
//----------------------------------------------------------------------------------------------------------------
@@ -1353,7 +1366,7 @@ void Grid::updateWindowSizes(bool updateScrollbar)
const ptrdiff_t logicalHeight = rowLabelWin_->getLogicalHeight(); //
int rowLabelWidth = 0;
- if (drawRowLabel && logicalHeight > 0)
+ if (drawRowLabel_ && logicalHeight > 0)
{
ptrdiff_t yFrom = CalcUnscrolledPosition(wxPoint(0, 0)).y;
ptrdiff_t yTo = CalcUnscrolledPosition(wxPoint(0, mainWinHeightGross - 1)).y ;
@@ -1406,8 +1419,8 @@ void Grid::updateWindowSizes(bool updateScrollbar)
if (logicalHeight <= mainWinHeightGross &&
getColWidthsSum(mainWinWidthGross) <= mainWinWidthGross &&
//this special case needs to be considered *only* when both scrollbars are flexible:
- showScrollbarX == SB_SHOW_AUTOMATIC &&
- showScrollbarY == SB_SHOW_AUTOMATIC)
+ showScrollbarX_ == SB_SHOW_AUTOMATIC &&
+ showScrollbarY_ == SB_SHOW_AUTOMATIC)
setScrollbars2(0, 0); //no scrollbars required at all! -> wxScrolledWindow requires active help to detect this special case!
else
{
@@ -1461,7 +1474,7 @@ wxSize Grid::GetSizeAvailableForScrollTarget(const wxSize& size)
const ptrdiff_t logicalHeight = rowLabelWin_->getLogicalHeight(); //
int rowLabelWidth = 0;
- if (drawRowLabel && logicalHeight > 0)
+ if (drawRowLabel_ && logicalHeight > 0)
{
ptrdiff_t yFrom = CalcUnscrolledPosition(wxPoint(0, 0)).y;
ptrdiff_t yTo = CalcUnscrolledPosition(wxPoint(0, mainWinHeightGross - 1)).y ;
@@ -1624,14 +1637,14 @@ void Grid::setColumnLabelHeight(int height)
void Grid::showRowLabel(bool show)
{
- drawRowLabel = show;
+ drawRowLabel_ = show;
updateWindowSizes();
}
void Grid::selectAllRows(GridEventPolicy rangeEventPolicy)
{
- selection.selectAll();
+ selection_.selectAll();
mainWin_->Refresh();
if (rangeEventPolicy == ALLOW_GRID_EVENT) //notify event, even if we're not triggered by user interaction
@@ -1645,7 +1658,7 @@ void Grid::selectAllRows(GridEventPolicy rangeEventPolicy)
void Grid::clearSelection(GridEventPolicy rangeEventPolicy)
{
- selection.clear();
+ selection_.clear();
mainWin_->Refresh();
if (rangeEventPolicy == ALLOW_GRID_EVENT) //notify event, even if we're not triggered by user interaction
@@ -1694,14 +1707,14 @@ size_t Grid::getRowCount() const
void Grid::Refresh(bool eraseBackground, const wxRect* rect)
{
const size_t rowCountNew = getRowCount();
- if (rowCountOld != rowCountNew)
+ if (rowCountOld_ != rowCountNew)
{
- rowCountOld = rowCountNew;
+ rowCountOld_ = rowCountNew;
updateWindowSizes();
}
- if (selection.maxSize() != rowCountNew) //clear selection only when needed (consider setSelectedRows())
- selection.init(rowCountNew);
+ if (selection_.maxSize() != rowCountNew) //clear selection only when needed (consider setSelectedRows())
+ selection_.init(rowCountNew);
wxScrolledWindow::Refresh(eraseBackground, rect);
}
@@ -1718,7 +1731,7 @@ void Grid::setRowHeight(int height)
void Grid::setColumnConfig(const std::vector<Grid::ColumnAttribute>& attr)
{
//hold ownership of non-visible columns
- oldColAttributes = attr;
+ oldColAttributes_ = attr;
std::vector<VisibleColumn> visCols;
for (const ColumnAttribute& ca : attr)
@@ -1729,7 +1742,7 @@ void Grid::setColumnConfig(const std::vector<Grid::ColumnAttribute>& attr)
}
//"ownership" of visible columns is now within Grid
- visibleCols = visCols;
+ visibleCols_ = visCols;
updateWindowSizes();
Refresh();
@@ -1739,10 +1752,10 @@ void Grid::setColumnConfig(const std::vector<Grid::ColumnAttribute>& attr)
std::vector<Grid::ColumnAttribute> Grid::getColumnConfig() const
{
//get non-visible columns (+ outdated visible ones)
- std::vector<ColumnAttribute> output = oldColAttributes;
+ std::vector<ColumnAttribute> output = oldColAttributes_;
- auto iterVcols = visibleCols.begin();
- auto iterVcolsend = visibleCols.end();
+ auto iterVcols = visibleCols_.begin();
+ auto iterVcolsend = visibleCols_.end();
//update visible columns but keep order of non-visible ones!
for (ColumnAttribute& ca : output)
@@ -1766,11 +1779,11 @@ std::vector<Grid::ColumnAttribute> Grid::getColumnConfig() const
void Grid::showScrollBars(Grid::ScrollBarStatus horizontal, Grid::ScrollBarStatus vertical)
{
- if (showScrollbarX == horizontal &&
- showScrollbarY == vertical) return; //support polling!
+ if (showScrollbarX_ == horizontal &&
+ showScrollbarY_ == vertical) return; //support polling!
- showScrollbarX = horizontal;
- showScrollbarY = vertical;
+ showScrollbarX_ = horizontal;
+ showScrollbarY_ = vertical;
#if defined ZEN_WIN || defined ZEN_MAC
//handled by Grid::SetScrollbar
@@ -1812,9 +1825,9 @@ void Grid::SetScrollbar(int orientation, int position, int thumbSize, int range,
ScrollBarStatus sbStatus = SB_SHOW_AUTOMATIC;
if (orientation == wxHORIZONTAL)
- sbStatus = showScrollbarX;
+ sbStatus = showScrollbarX_;
else if (orientation == wxVERTICAL)
- sbStatus = showScrollbarY;
+ sbStatus = showScrollbarY_;
else
assert(false);
@@ -1851,7 +1864,7 @@ Opt<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const
const int absPosX = CalcUnscrolledPosition(pos).x;
if (absPosX >= 0)
{
- const int resizeTolerance = allowColumnResize ? COLUMN_RESIZE_TOLERANCE : 0;
+ const int resizeTolerance = allowColumnResize_ ? COLUMN_RESIZE_TOLERANCE : 0;
std::vector<ColumnWidth> absWidths = getColWidths(); //resolve stretched widths
int accuWidth = 0;
@@ -1880,13 +1893,13 @@ Opt<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const
void Grid::moveColumn(size_t colFrom, size_t colTo)
{
- if (colFrom < visibleCols.size() &&
- colTo < visibleCols.size() &&
+ if (colFrom < visibleCols_.size() &&
+ colTo < visibleCols_.size() &&
colTo != colFrom)
{
- const VisibleColumn colAtt = visibleCols[colFrom];
- visibleCols.erase (visibleCols.begin() + colFrom);
- visibleCols.insert(visibleCols.begin() + colTo, colAtt);
+ const VisibleColumn colAtt = visibleCols_[colFrom];
+ visibleCols_.erase (visibleCols_.begin() + colFrom);
+ visibleCols_.insert(visibleCols_.begin() + colTo, colAtt);
}
}
@@ -1912,8 +1925,8 @@ ptrdiff_t Grid::clientPosToMoveTargetColumn(const wxPoint& pos) const
ColumnType Grid::colToType(size_t col) const
{
- if (col < visibleCols.size())
- return visibleCols[col].type_;
+ if (col < visibleCols_.size())
+ return visibleCols_[col].type_;
return ColumnType::NONE;
}
@@ -1976,7 +1989,7 @@ void Grid::setGridCursor(size_t row)
mainWin_->setCursor(row, row);
makeRowVisible(row);
- selection.clear(); //clear selection, do NOT fire event
+ selection_.clear(); //clear selection, do NOT fire event
selectRangeAndNotify(row, row, true /*positive*/, nullptr /*mouseInitiated*/); //set new selection + fire event
mainWin_->Refresh();
@@ -1991,7 +2004,7 @@ void Grid::selectWithCursor(ptrdiff_t row)
mainWin_->setCursor(row, anchorRow);
makeRowVisible(row);
- selection.clear(); //clear selection, do NOT fire event
+ selection_.clear(); //clear selection, do NOT fire event
selectRangeAndNotify(anchorRow, row, true /*positive*/, nullptr /*mouseInitiated*/); //set new selection + fire event
mainWin_->Refresh();
@@ -2050,7 +2063,7 @@ void Grid::selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positiv
numeric::clamp<ptrdiff_t>(rowFirst, 0, rowCount);
numeric::clamp<ptrdiff_t>(rowLast, 0, rowCount);
- selection.selectRange(rowFirst, rowLast, positive);
+ selection_.selectRange(rowFirst, rowLast, positive);
//notify event
GridRangeSelectEvent selectionEvent(rowFirst, rowLast, positive, mouseInitiated);
@@ -2101,9 +2114,9 @@ size_t Grid::getGridCursor() const
int Grid::getBestColumnSize(size_t col) const
{
- if (dataView_ && col < visibleCols.size())
+ if (dataView_ && col < visibleCols_.size())
{
- const ColumnType type = visibleCols[col].type_;
+ const ColumnType type = visibleCols_[col].type_;
wxClientDC dc(mainWin_);
dc.SetFont(mainWin_->GetFont()); //harmonize with MainWin::render()
@@ -2122,12 +2135,12 @@ int Grid::getBestColumnSize(size_t col) const
void Grid::setColumnWidth(int width, size_t col, GridEventPolicy columnResizeEventPolicy, bool notifyAsync)
{
- if (col < visibleCols.size())
+ if (col < visibleCols_.size())
{
- VisibleColumn& vcRs = visibleCols[col];
+ VisibleColumn& vcRs = visibleCols_[col];
const std::vector<int> stretchedWidths = getColStretchedWidths(mainWin_->GetClientSize().GetWidth());
- if (stretchedWidths.size() != visibleCols.size())
+ if (stretchedWidths.size() != visibleCols_.size())
{
assert(false);
return;
@@ -2146,9 +2159,9 @@ void Grid::setColumnWidth(int width, size_t col, GridEventPolicy columnResizeEve
//2. shrink main window width so that horizontal scrollbars are shown despite the streched column
//3. shrink a fixed-size column so that the scrollbars vanish and columns cover full width again
//4. now verify that the stretched column is resizing immediately if main window is enlarged again
- for (size_t col2 = 0; col2 < visibleCols.size(); ++col2)
- if (visibleCols[col2].stretch_ > 0) //normalize stretched columns only
- visibleCols[col2].offset_ = std::max(visibleCols[col2].offset_, COLUMN_MIN_WIDTH - stretchedWidths[col2]);
+ for (size_t col2 = 0; col2 < visibleCols_.size(); ++col2)
+ if (visibleCols_[col2].stretch_ > 0) //normalize stretched columns only
+ visibleCols_[col2].offset_ = std::max(visibleCols_[col2].offset_, COLUMN_MIN_WIDTH - stretchedWidths[col2]);
if (columnResizeEventPolicy == ALLOW_GRID_EVENT)
{
@@ -2169,9 +2182,9 @@ void Grid::setColumnWidth(int width, size_t col, GridEventPolicy columnResizeEve
void Grid::autoSizeColumns(GridEventPolicy columnResizeEventPolicy)
{
- if (allowColumnResize)
+ if (allowColumnResize_)
{
- for (size_t col = 0; col < visibleCols.size(); ++col)
+ for (size_t col = 0; col < visibleCols_.size(); ++col)
{
const int bestWidth = getBestColumnSize(col); //return -1 on error
if (bestWidth >= 0)
@@ -2188,7 +2201,7 @@ std::vector<int> Grid::getColStretchedWidths(int clientWidth) const //final widt
assert(clientWidth >= 0);
clientWidth = std::max(clientWidth, 0);
int stretchTotal = 0;
- for (const VisibleColumn& vc : visibleCols)
+ for (const VisibleColumn& vc : visibleCols_)
{
assert(vc.stretch_ >= 0);
stretchTotal += vc.stretch_;
@@ -2199,28 +2212,27 @@ std::vector<int> Grid::getColStretchedWidths(int clientWidth) const //final widt
std::vector<int> output;
if (stretchTotal <= 0)
- output.resize(visibleCols.size()); //fill with zeros
+ output.resize(visibleCols_.size()); //fill with zeros
else
- for (const VisibleColumn& vc : visibleCols)
+ {
+ for (const VisibleColumn& vc : visibleCols_)
{
const int width = clientWidth * vc.stretch_ / stretchTotal; //rounds down!
output.push_back(width);
remainingWidth -= width;
}
- //distribute *all* of clientWidth: should suffice to enlarge the first few stretched columns; no need to minimize total absolute error of distribution
- if (stretchTotal > 0)
+ //distribute *all* of clientWidth: should suffice to enlarge the first few stretched columns; no need to minimize total absolute error of distribution
if (remainingWidth > 0)
- {
- for (size_t col2 = 0; col2 < visibleCols.size(); ++col2)
- if (visibleCols[col2].stretch_ > 0)
+ for (size_t col2 = 0; col2 < visibleCols_.size(); ++col2)
+ if (visibleCols_[col2].stretch_ > 0)
{
++output[col2];
if (--remainingWidth == 0)
- return output;
+ break;
}
- assert(false);
- }
+ assert(remainingWidth == 0);
+ }
return output;
}
@@ -2234,12 +2246,12 @@ std::vector<Grid::ColumnWidth> Grid::getColWidths() const
std::vector<Grid::ColumnWidth> Grid::getColWidths(int mainWinWidth) const //evaluate stretched columns
{
const std::vector<int> stretchedWidths = getColStretchedWidths(mainWinWidth);
- assert(stretchedWidths.size() == visibleCols.size());
+ assert(stretchedWidths.size() == visibleCols_.size());
std::vector<ColumnWidth> output;
- for (size_t col2 = 0; col2 < visibleCols.size(); ++col2)
+ for (size_t col2 = 0; col2 < visibleCols_.size(); ++col2)
{
- const auto& vc = visibleCols[col2];
+ const auto& vc = visibleCols_[col2];
int width = stretchedWidths[col2] + vc.offset_;
if (vc.stretch_ > 0)
diff --git a/wx+/grid.h b/wx+/grid.h
index b8b6711e..f08d6e41 100644
--- a/wx+/grid.h
+++ b/wx+/grid.h
@@ -178,7 +178,7 @@ public:
//alternative until wxScrollHelper::ShowScrollbars() becomes available in wxWidgets 2.9
void showScrollBars(ScrollBarStatus horizontal, ScrollBarStatus vertical);
- std::vector<size_t> getSelectedRows() const { return selection.get(); }
+ std::vector<size_t> getSelectedRows() const { return selection_.get(); }
void selectAllRows (GridEventPolicy rangeEventPolicy);
void clearSelection(GridEventPolicy rangeEventPolicy); //turn off range selection event when calling this function in an event handler to avoid recursion!
@@ -202,8 +202,8 @@ public:
void refreshCell(size_t row, ColumnType colType);
- void enableColumnMove (bool value) { allowColumnMove = value; }
- void enableColumnResize(bool value) { allowColumnResize = value; }
+ void enableColumnMove (bool value) { allowColumnMove_ = value; }
+ void enableColumnResize(bool value) { allowColumnResize_ = value; }
void setGridCursor(size_t row); //set + show + select cursor (+ emit range selection event)
size_t getGridCursor() const; //returns row
@@ -301,7 +301,7 @@ private:
};
std::vector<ColumnWidth> getColWidths() const; //
std::vector<ColumnWidth> getColWidths(int mainWinWidth) const; //evaluate stretched columns
- int getColWidthsSum(int mainWinWidth) const;
+ int getColWidthsSum(int mainWinWidth) const;
std::vector<int> getColStretchedWidths(int clientWidth) const; //final width = (normalized) (stretchedWidth + offset)
Opt<int> getColWidth(size_t col) const
@@ -318,7 +318,7 @@ private:
void selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive, const GridClickEvent* mouseInitiated); //select inclusive range [rowFrom, rowTo] + notify event!
- bool isSelected(size_t row) const { return selection.isSelected(row); }
+ bool isSelected(size_t row) const { return selection_.isSelected(row); }
struct ColAction
{
@@ -345,21 +345,21 @@ private:
ColLabelWin* colLabelWin_;
MainWin* mainWin_;
- ScrollBarStatus showScrollbarX = SB_SHOW_AUTOMATIC;
- ScrollBarStatus showScrollbarY = SB_SHOW_AUTOMATIC;
+ ScrollBarStatus showScrollbarX_ = SB_SHOW_AUTOMATIC;
+ ScrollBarStatus showScrollbarY_ = SB_SHOW_AUTOMATIC;
int colLabelHeight_ = 0;
- bool drawRowLabel = true;
+ bool drawRowLabel_ = true;
std::shared_ptr<GridData> dataView_;
- Selection selection;
- bool allowColumnMove = true;
- bool allowColumnResize = true;
+ Selection selection_;
+ bool allowColumnMove_ = true;
+ bool allowColumnResize_ = true;
- std::vector<VisibleColumn> visibleCols; //individual widths, type and total column count
- std::vector<ColumnAttribute> oldColAttributes; //visible + nonvisible columns; use for conversion in setColumnConfig()/getColumnConfig() *only*!
+ std::vector<VisibleColumn> visibleCols_; //individual widths, type and total column count
+ std::vector<ColumnAttribute> oldColAttributes_; //visible + nonvisible columns; use for conversion in setColumnConfig()/getColumnConfig() *only*!
- size_t rowCountOld = 0; //at the time of last Grid::Refresh()
+ size_t rowCountOld_ = 0; //at the time of last Grid::Refresh()
};
}
diff --git a/wx+/http.cpp b/wx+/http.cpp
index ce3de482..3428546e 100644
--- a/wx+/http.cpp
+++ b/wx+/http.cpp
@@ -31,186 +31,259 @@ namespace
#endif
#endif
+struct UrlRedirectError
+{
+ UrlRedirectError(const std::wstring& url) : newUrl(url) {}
+ std::wstring newUrl;
+};
+}
-std::string sendHttpRequestImpl(const std::wstring& url, //throw SysError
- const std::wstring& userAgent,
- const std::string* postParams, //issue POST if bound, GET otherwise
- int level = 0)
+
+class HttpInputStream::Impl
{
- assert(!startsWith(makeUpperCopy(url), L"HTTPS:")); //not supported by wxHTTP!
- const std::wstring urlFmt = startsWith(makeUpperCopy(url), L"HTTP://") ? afterFirst(url, L"://", IF_MISSING_RETURN_NONE) : url;
- const std::wstring server = beforeFirst(urlFmt, L'/', IF_MISSING_RETURN_ALL);
- const std::wstring page = L'/' + afterFirst(urlFmt, L'/', IF_MISSING_RETURN_NONE);
+public:
+ Impl(const std::wstring& url, const std::wstring& userAgent, //throw SysError, UrlRedirectError
+ const std::string* postParams) //issue POST if bound, GET otherwise
+ {
+ ZEN_ON_SCOPE_FAIL( cleanup(); /*destructor call would lead to member double clean-up!!!*/ );
+
+ assert(!startsWith(makeUpperCopy(url), L"HTTPS:")); //not supported by wxHTTP!
+ const std::wstring urlFmt = startsWith(makeUpperCopy(url), L"HTTP://") ||
+ startsWith(makeUpperCopy(url), L"HTTPS://") ? afterFirst(url, L"://", IF_MISSING_RETURN_NONE) : url;
+ const std::wstring server = beforeFirst(urlFmt, L'/', IF_MISSING_RETURN_ALL);
+ const std::wstring page = L'/' + afterFirst(urlFmt, L'/', IF_MISSING_RETURN_NONE);
#ifdef ZEN_WIN
- //WinInet: 1. uses IE proxy settings! :) 2. follows HTTP redirects by default 3. swallows HTTPS if needed
- HINTERNET hInternet = ::InternetOpen(userAgent.c_str(), //_In_ LPCTSTR lpszAgent,
- INTERNET_OPEN_TYPE_PRECONFIG, //_In_ DWORD dwAccessType,
- nullptr, //_In_ LPCTSTR lpszProxyName,
- nullptr, //_In_ LPCTSTR lpszProxyBypass,
- 0); //_In_ DWORD dwFlags
- if (!hInternet)
- THROW_LAST_SYS_ERROR(L"InternetOpen");
- ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hInternet));
+ //WinInet: 1. uses IE proxy settings! :) 2. follows HTTP redirects by default 3. swallows HTTPS if needed
+ hInternet_ = ::InternetOpen(userAgent.c_str(), //_In_ LPCTSTR lpszAgent,
+ INTERNET_OPEN_TYPE_PRECONFIG, //_In_ DWORD dwAccessType,
+ nullptr, //_In_ LPCTSTR lpszProxyName,
+ nullptr, //_In_ LPCTSTR lpszProxyBypass,
+ 0); //_In_ DWORD dwFlags
+ if (!hInternet_)
+ THROW_LAST_SYS_ERROR(L"InternetOpen");
+
+ hSession_ = ::InternetConnect(hInternet_, //_In_ HINTERNET hInternet,
+ server.c_str(), //_In_ LPCTSTR lpszServerName,
+ INTERNET_DEFAULT_HTTP_PORT, //_In_ INTERNET_PORT nServerPort,
+ nullptr, //_In_ LPCTSTR lpszUsername,
+ nullptr, //_In_ LPCTSTR lpszPassword,
+ INTERNET_SERVICE_HTTP, //_In_ DWORD dwService,
+ 0, //_In_ DWORD dwFlags,
+ 0); //_In_ DWORD_PTR dwContext
+ if (!hSession_)
+ THROW_LAST_SYS_ERROR(L"InternetConnect");
+
+ const wchar_t* acceptTypes[] = { L"*/*", nullptr };
+ DWORD requestFlags =
+ //INTERNET_FLAG_KEEP_CONNECTION |
+ // the combination 1. INTERNET_FLAG_KEEP_CONNECTION (= adds "Connection: Keep-Alive" but NOT "Keep-Alive: timeout" to the header)
+ // 2. *no* "Keep-Alive: timeout" header entry 3. call from within VM and 4. *no* Fiddler running 5. HTTP POST
+ // leads to Godaddy blocking the IP: http://www.freefilesync.org/forum/viewtopic.php?t=3855
+ // => it seems a broken keep alive header is the trigger: But why is it then working outside the VM or when Fiddler is running??? Why not a problem for HTTP GET?
+ // note: HTTP/1.1 has keep-alive semantics by default, so this flag is probably useless anyway
+ INTERNET_FLAG_NO_UI;
+
+ if (postParams)
+ {
+ requestFlags |= INTERNET_FLAG_NO_AUTO_REDIRECT; //POST would be re-issued as GET during auto-redirect => handle ourselves!
+ }
+ else //HTTP GET
+ {
+ requestFlags |= INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP;
+ requestFlags |= INTERNET_FLAG_RELOAD; //not relevant for POST (= never cached)
+ }
- HINTERNET hSession = ::InternetConnect(hInternet, //_In_ HINTERNET hInternet,
- server.c_str(), //_In_ LPCTSTR lpszServerName,
- INTERNET_DEFAULT_HTTP_PORT, //_In_ INTERNET_PORT nServerPort,
- nullptr, //_In_ LPCTSTR lpszUsername,
- nullptr, //_In_ LPCTSTR lpszPassword,
- INTERNET_SERVICE_HTTP, //_In_ DWORD dwService,
- 0, //_In_ DWORD dwFlags,
- 0); //_In_ DWORD_PTR dwContext
- if (!hSession)
- THROW_LAST_SYS_ERROR(L"InternetConnect");
- ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hSession));
-
- const wchar_t* acceptTypes[] = { L"*/*", nullptr };
- DWORD requestFlags =
- //INTERNET_FLAG_KEEP_CONNECTION |
- // the combination 1. INTERNET_FLAG_KEEP_CONNECTION (= adds "Connection: Keep-Alive" but NOT "Keep-Alive: timeout" to the header)
- // 2. *no* "Keep-Alive: timeout" header entry 3. call from within VM and 4. *no* Fiddler running 5. HTTP POST
- // leads to Godaddy blocking the IP: http://www.freefilesync.org/forum/viewtopic.php?t=3855
- // => it seems a broken keep alive header is the trigger: But why is it then working outside the VM or when Fiddler is running??? Why not a problem for HTTP GET?
- // note: HTTP/1.1 has keep-alive semantics by default, so this flag is probably useless anyway
- INTERNET_FLAG_NO_UI;
-
- if (postParams)
- {
- requestFlags |= INTERNET_FLAG_NO_AUTO_REDIRECT; //POST would be re-issued as GET during auto-redirect => handle ourselves!
- }
- else //HTTP GET
- {
- requestFlags |= INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP;
- requestFlags |= INTERNET_FLAG_RELOAD; //not relevant for POST (= never cached)
- }
-
- HINTERNET hRequest = ::HttpOpenRequest(hSession, //_In_ HINTERNET hConnect,
- postParams ? L"POST" : L"GET", //_In_ LPCTSTR lpszVerb,
- page.c_str(), //_In_ LPCTSTR lpszObjectName,
- nullptr, //_In_ LPCTSTR lpszVersion,
- nullptr, //_In_ LPCTSTR lpszReferer,
- acceptTypes, //_In_ LPCTSTR *lplpszAcceptTypes,
- requestFlags, //_In_ DWORD dwFlags,
- 0); //_In_ DWORD_PTR dwContext
- if (!hRequest)
- THROW_LAST_SYS_ERROR(L"HttpOpenRequest");
- ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hRequest));
+ hRequest_ = ::HttpOpenRequest(hSession_, //_In_ HINTERNET hConnect,
+ postParams ? L"POST" : L"GET", //_In_ LPCTSTR lpszVerb,
+ page.c_str(), //_In_ LPCTSTR lpszObjectName,
+ nullptr, //_In_ LPCTSTR lpszVersion,
+ nullptr, //_In_ LPCTSTR lpszReferer,
+ acceptTypes, //_In_ LPCTSTR *lplpszAcceptTypes,
+ requestFlags, //_In_ DWORD dwFlags,
+ 0); //_In_ DWORD_PTR dwContext
+ if (!hRequest_)
+ THROW_LAST_SYS_ERROR(L"HttpOpenRequest");
- const std::wstring headers = postParams ? L"Content-type: application/x-www-form-urlencoded" : L"";
+ const std::wstring headers = postParams ? L"Content-type: application/x-www-form-urlencoded" : L"";
- assert(std::all_of(headers.begin(), headers.end(), [](wchar_t c){ return makeUnsigned(c) < 128; }));
- //HttpSendRequest has finicky behavior for non-ASCII headers: https://msdn.microsoft.com/en-us/library/windows/desktop/aa384247
+ assert(std::all_of(headers.begin(), headers.end(), [](wchar_t c) { return makeUnsigned(c) < 128; }));
+ //HttpSendRequest has finicky behavior for non-ASCII headers: https://msdn.microsoft.com/en-us/library/windows/desktop/aa384247
- std::string postParamsBuf = postParams ? *postParams : "";
+ std::string postParamsBuf = postParams ? *postParams : "";
- if (!::HttpSendRequest(hRequest, //_In_ HINTERNET hRequest,
- headers.c_str(), //_In_ LPCTSTR lpszHeaders,
- static_cast<DWORD>(headers.size()), //_In_ DWORD dwHeadersLength,
- postParamsBuf.empty() ? nullptr : &postParamsBuf[0], //_In_ LPVOID lpOptional,
- static_cast<DWORD>(postParamsBuf.size()))) //_In_ DWORD dwOptionalLength
- THROW_LAST_SYS_ERROR(L"HttpSendRequest");
+ if (!::HttpSendRequest(hRequest_, //_In_ HINTERNET hRequest,
+ headers.c_str(), //_In_ LPCTSTR lpszHeaders,
+ static_cast<DWORD>(headers.size()), //_In_ DWORD dwHeadersLength,
+ postParamsBuf.empty() ? nullptr : &postParamsBuf[0], //_In_ LPVOID lpOptional,
+ static_cast<DWORD>(postParamsBuf.size()))) //_In_ DWORD dwOptionalLength
+ THROW_LAST_SYS_ERROR(L"HttpSendRequest");
- DWORD sc = 0;
- {
- DWORD bufLen = sizeof(sc);
- if (!::HttpQueryInfo(hRequest, //_In_ HINTERNET hRequest,
- HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, //_In_ DWORD dwInfoLevel,
- &sc, //_Inout_ LPVOID lpvBuffer,
- &bufLen, //_Inout_ LPDWORD lpdwBufferLength,
- nullptr)) //_Inout_ LPDWORD lpdwIndex
- THROW_LAST_SYS_ERROR(L"HttpQueryInfo: HTTP_QUERY_STATUS_CODE");
- }
+ DWORD sc = 0;
+ {
+ DWORD bufLen = sizeof(sc);
+ if (!::HttpQueryInfo(hRequest_, //_In_ HINTERNET hRequest,
+ HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, //_In_ DWORD dwInfoLevel,
+ &sc, //_Inout_ LPVOID lpvBuffer,
+ &bufLen, //_Inout_ LPDWORD lpdwBufferLength,
+ nullptr)) //_Inout_ LPDWORD lpdwIndex
+ THROW_LAST_SYS_ERROR(L"HttpQueryInfo: HTTP_QUERY_STATUS_CODE");
+ }
- //http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
- if (sc / 100 == 3) //e.g. 301, 302, 303, 307... we're not too greedy since we check location, too!
- {
- if (level < 5) //"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop."
+ //http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
+ if (sc / 100 == 3) //e.g. 301, 302, 303, 307... we're not too greedy since we check location, too!
{
DWORD bufLen = 10000;
std::wstring location(bufLen, L'\0');
- if (!::HttpQueryInfo(hRequest, HTTP_QUERY_LOCATION, &*location.begin(), &bufLen, nullptr))
+ if (!::HttpQueryInfo(hRequest_, HTTP_QUERY_LOCATION, &*location.begin(), &bufLen, nullptr))
THROW_LAST_SYS_ERROR(L"HttpQueryInfo: HTTP_QUERY_LOCATION");
if (bufLen >= location.size()) //HttpQueryInfo expected to write terminating zero
throw SysError(L"HttpQueryInfo: HTTP_QUERY_LOCATION, buffer overflow");
location.resize(bufLen);
- if (!location.empty())
- return sendHttpRequestImpl(location, userAgent, postParams, level + 1);
+ if (location.empty())
+ throw SysError(L"Unresolvable redirect. Empty target Location.");
+
+ throw UrlRedirectError(location);
+ }
+
+ if (sc != HTTP_STATUS_OK) //200
+ throw SysError(replaceCpy<std::wstring>(L"HTTP status code %x.", L"%x", numberTo<std::wstring>(sc)));
+ //e.g. 404 - HTTP_STATUS_NOT_FOUND
+
+#else
+ assert(std::this_thread::get_id() == mainThreadId);
+ assert(wxApp::IsMainLoopRunning());
+
+ webAccess_.SetHeader(L"User-Agent", userAgent);
+ webAccess_.SetTimeout(10 /*[s]*/); //default: 10 minutes: WTF are these wxWidgets people thinking???
+
+ if (!webAccess_.Connect(server)) //will *not* fail for non-reachable url here!
+ throw SysError(L"wxHTTP::Connect");
+
+ if (postParams)
+ if (!webAccess_.SetPostText(L"application/x-www-form-urlencoded", utfCvrtTo<wxString>(*postParams)))
+ throw SysError(L"wxHTTP::SetPostText");
+
+ httpStream_.reset(webAccess_.GetInputStream(page)); //pass ownership
+ const int sc = webAccess_.GetResponse();
+
+ //http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
+ if (sc / 100 == 3) //e.g. 301, 302, 303, 307... we're not too greedy since we check location, too!
+ {
+ const std::wstring newUrl(webAccess_.GetHeader(L"Location"));
+ if (newUrl.empty())
+ throw SysError(L"Unresolvable redirect. Empty target Location.");
+
+ throw UrlRedirectError(newUrl);
}
- throw SysError(L"Unresolvable redirect.");
+
+ if (sc != 200) //HTTP_STATUS_OK
+ throw SysError(replaceCpy<std::wstring>(L"HTTP status code %x.", L"%x", numberTo<std::wstring>(sc)));
+
+ if (!httpStream_ || webAccess_.GetError() != wxPROTO_NOERR)
+ throw SysError(L"wxHTTP::GetError (" + numberTo<std::wstring>(webAccess_.GetError()) + L")");
+#endif
}
- if (sc != HTTP_STATUS_OK) //200
- throw SysError(replaceCpy<std::wstring>(L"HTTP status code %x.", L"%x", numberTo<std::wstring>(sc)));
- //e.g. 404 - HTTP_STATUS_NOT_FOUND
+ ~Impl() { cleanup(); }
- std::string buffer;
- const DWORD blockSize = 64 * 1024;
- //internet says "HttpQueryInfo() + HTTP_QUERY_CONTENT_LENGTH" not supported by all http servers...
- for (;;)
+ size_t tryRead(void* buffer, size_t bytesToRead) //throw SysError; may return short, only 0 means EOF!
{
- buffer.resize(buffer.size() + blockSize);
+ if (bytesToRead == 0) //"read() with a count of 0 returns zero" => indistinguishable from end of file! => check!
+ throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+#ifdef ZEN_WIN
+ //"HttpQueryInfo() + HTTP_QUERY_CONTENT_LENGTH" not supported by all http servers...
DWORD bytesRead = 0;
- if (!::InternetReadFile(hRequest, //_In_ HINTERNET hFile,
- &*(buffer.begin() + buffer.size() - blockSize), //_Out_ LPVOID lpBuffer,
- blockSize, //_In_ DWORD dwNumberOfBytesToRead,
+ if (!::InternetReadFile(hRequest_, //_In_ HINTERNET hFile,
+ buffer, //_Out_ LPVOID lpBuffer,
+ static_cast<DWORD>(bytesToRead), //_In_ DWORD dwNumberOfBytesToRead,
&bytesRead)) //_Out_ LPDWORD lpdwNumberOfBytesRead
THROW_LAST_SYS_ERROR(L"InternetReadFile");
+#else
+ httpStream_->Read(buffer, bytesToRead);
- if (bytesRead > blockSize) //better safe than sorry
+ const wxStreamError ec = httpStream_->GetLastError();
+ if (ec != wxSTREAM_NO_ERROR && ec != wxSTREAM_EOF)
+ throw SysError(L"wxInputStream::GetLastError (" + numberTo<std::wstring>(httpStream_->GetLastError()) + L")");
+
+ const size_t bytesRead = httpStream_->LastRead();
+ //"if there are not enough bytes in the stream right now, LastRead() value will be
+ // less than size but greater than 0. If it is 0, it means that EOF has been reached."
+ assert(bytesRead > 0 || ec == wxSTREAM_EOF);
+#endif
+ if (bytesRead > bytesToRead) //better safe than sorry
throw SysError(L"InternetReadFile: buffer overflow.");
- if (bytesRead < blockSize)
- buffer.resize(buffer.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics
+ return bytesRead; //"zero indicates end of file"
+ }
- if (bytesRead == 0)
- return buffer;
+private:
+ Impl (const Impl&) = delete;
+ Impl& operator=(const Impl&) = delete;
+
+ void cleanup()
+ {
+#ifdef ZEN_WIN
+ if (hRequest_ ) ::InternetCloseHandle(hRequest_);
+ if (hSession_ ) ::InternetCloseHandle(hSession_);
+ if (hInternet_) ::InternetCloseHandle(hInternet_);
+#endif
}
+#ifdef ZEN_WIN
+ HINTERNET hInternet_ = nullptr;
+ HINTERNET hSession_ = nullptr;
+ HINTERNET hRequest_ = nullptr;
#else
- assert(std::this_thread::get_id() == mainThreadId);
- assert(wxApp::IsMainLoopRunning());
+ wxHTTP webAccess_;
+ std::unique_ptr<wxInputStream> httpStream_; //must be deleted BEFORE webAccess is closed
+#endif
+};
- wxHTTP webAccess;
- webAccess.SetHeader(L"User-Agent", userAgent);
- webAccess.SetTimeout(10 /*[s]*/); //default: 10 minutes: WTF are these wxWidgets people thinking???
- if (!webAccess.Connect(server)) //will *not* fail for non-reachable url here!
- throw SysError(L"wxHTTP::Connect");
+HttpInputStream::HttpInputStream(std::unique_ptr<Impl>&& pimpl) : pimpl_(std::move(pimpl)) {}
- if (postParams)
- if (!webAccess.SetPostText(L"application/x-www-form-urlencoded", utfCvrtTo<wxString>(*postParams)))
- throw SysError(L"wxHTTP::SetPostText");
+HttpInputStream::~HttpInputStream() {}
- std::unique_ptr<wxInputStream> httpStream(webAccess.GetInputStream(page)); //must be deleted BEFORE webAccess is closed
- const int sc = webAccess.GetResponse();
+size_t HttpInputStream::tryRead(void* buffer, size_t bytesToRead) { return pimpl_->tryRead(buffer, bytesToRead); } //throw SysError
+
+
+std::string HttpInputStream::readAll() //throw SysError
+{
+ std::string buffer;
+ const size_t blockSize = getBlockSize();
- //http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
- if (sc / 100 == 3) //e.g. 301, 302, 303, 307... we're not too greedy since we check location, too!
+ for (;;)
{
- if (level < 5) //"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop."
- {
- const std::wstring newUrl(webAccess.GetHeader(L"Location"));
- if (!newUrl.empty())
- return sendHttpRequestImpl(newUrl, userAgent, postParams, level + 1);
- }
- throw SysError(L"Unresolvable redirect.");
- }
+ buffer.resize(buffer.size() + blockSize);
- if (sc != 200) //HTTP_STATUS_OK
- throw SysError(replaceCpy<std::wstring>(L"HTTP status code %x.", L"%x", numberTo<std::wstring>(sc)));
+ const size_t bytesRead = pimpl_->tryRead(&*(buffer.end() - blockSize), blockSize); //throw SysError
- if (!httpStream || webAccess.GetError() != wxPROTO_NOERR)
- throw SysError(L"wxHTTP::GetError");
+ if (bytesRead < blockSize)
+ buffer.resize(buffer.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics
- std::string buffer;
- int newValue = 0;
- while ((newValue = httpStream->GetC()) != wxEOF)
- buffer.push_back(static_cast<char>(newValue));
- return buffer;
-#endif
+ if (bytesRead == 0)
+ return buffer;
+ }
+}
+
+
+namespace
+{
+std::unique_ptr<HttpInputStream::Impl> sendHttpRequestImpl(const std::wstring& url, const std::wstring& userAgent, //throw SysError
+ const std::string* postParams) //issue POST if bound, GET otherwise
+{
+ std::wstring urlRed = url;
+ //"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop."
+ for (int redirects = 0; redirects < 6; ++redirects)
+ try
+ {
+ return std::make_unique<HttpInputStream::Impl>(urlRed, userAgent, postParams); //throw SysError, UrlRedirectError
+ }
+ catch (const UrlRedirectError& e) { urlRed = e.newUrl; }
+ throw SysError(L"Too many redirects.");
}
@@ -228,31 +301,72 @@ std::string urlencode(const std::string& str)
out += c;
else
{
- const char hexDigits[] = "0123456789ABCDEF";
+ const std::pair<char, char> hex = hexify(c);
+
out += '%';
- out += hexDigits[static_cast<unsigned char>(c) / 16];
- out += hexDigits[static_cast<unsigned char>(c) % 16];
+ out += hex.first;
+ out += hex.second;
+ }
+ return out;
+}
+
+
+std::string urldecode(const std::string& str)
+{
+ std::string out;
+ for (size_t i = 0; i < str.size(); ++i)
+ {
+ const char c = str[i];
+ if (c == '+')
+ out += ' ';
+ else if (c == '%' && str.size() - i >= 3 &&
+ isHexDigit(str[i + 1]) &&
+ isHexDigit(str[i + 2]))
+ {
+ out += unhexify(str[i + 1], str[i + 2]);
+ i += 2;
}
+ else
+ out += c;
+ }
return out;
}
}
-std::string zen::sendHttpPost(const std::wstring& url, const std::wstring& userAgent, const std::vector<std::pair<std::string, std::string>>& postParams) //throw SysError
+std::string zen::xWwwFormUrlEncode(const std::vector<std::pair<std::string, std::string>>& paramPairs)
{
- //convert post parameters into "application/x-www-form-urlencoded"
- std::string flatParams;
- for (const auto& pair : postParams)
- flatParams += urlencode(pair.first) + '=' + urlencode(pair.second) + '&';
+ std::string output;
+ for (const auto& pair : paramPairs)
+ output += urlencode(pair.first) + '=' + urlencode(pair.second) + '&';
//encode both key and value: https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
- if (!flatParams.empty())
- flatParams.pop_back();
+ if (!output.empty())
+ output.pop_back();
+ return output;
+}
- return sendHttpRequestImpl(url, userAgent, &flatParams); //throw SysError
+
+std::vector<std::pair<std::string, std::string>> zen::xWwwFormUrlDecode(const std::string& str)
+{
+ std::vector<std::pair<std::string, std::string>> output;
+
+ for (const std::string& nvPair : split(str, '&'))
+ if (!nvPair.empty())
+ output.emplace_back(urldecode(beforeFirst(nvPair, '=', IF_MISSING_RETURN_ALL)),
+ urldecode(afterFirst (nvPair, '=', IF_MISSING_RETURN_NONE)));
+ return output;
+}
+
+
+HttpInputStream zen::sendHttpPost(const std::wstring& url, const std::wstring& userAgent,
+ const std::vector<std::pair<std::string, std::string>>& postParams) //throw SysError
+{
+ const std::string encodedParams = xWwwFormUrlEncode(postParams);
+ return sendHttpRequestImpl(url, userAgent, &encodedParams); //throw SysError
}
-std::string zen::sendHttpGet(const std::wstring& url, const std::wstring& userAgent) //throw SysError
+HttpInputStream zen::sendHttpGet(const std::wstring& url, const std::wstring& userAgent) //throw SysError
{
return sendHttpRequestImpl(url, userAgent, nullptr); //throw SysError
}
diff --git a/wx+/http.h b/wx+/http.h
index febe8f24..cf385d5e 100644
--- a/wx+/http.h
+++ b/wx+/http.h
@@ -17,9 +17,32 @@ namespace zen
Windows: WinInet-based => may be called from worker thread
Linux: wxWidgets-based => don't call from worker thread
*/
-std::string sendHttpPost(const std::wstring& url, const std::wstring& userAgent, const std::vector<std::pair<std::string, std::string>>& postParams); //throw SysError
-std::string sendHttpGet (const std::wstring& url, const std::wstring& userAgent); //throw SysError
+class HttpInputStream
+{
+public:
+ std::string readAll(); //throw SysError
+
+ //support zen/serialize.h Unbuffered Input Stream Concept
+ size_t tryRead(void* buffer, size_t bytesToRead); //throw SysError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0!
+ size_t getBlockSize() const { return 64 * 1024; }
+
+ class Impl;
+ HttpInputStream(std::unique_ptr<Impl>&& pimpl);
+ HttpInputStream(HttpInputStream&&) = default;
+ ~HttpInputStream();
+
+private:
+ std::unique_ptr<Impl> pimpl_;
+};
+
+
+HttpInputStream sendHttpGet (const std::wstring& url, const std::wstring& userAgent); //throw SysError
+HttpInputStream sendHttpPost(const std::wstring& url, const std::wstring& userAgent,
+ const std::vector<std::pair<std::string, std::string>>& postParams); //throw SysError
bool internetIsAlive(); //noexcept
+
+std::string xWwwFormUrlEncode(const std::vector<std::pair<std::string, std::string>>& paramPairs);
+std::vector<std::pair<std::string, std::string>> xWwwFormUrlDecode(const std::string& str);
}
#endif //HTTP_h_879083425703425702
diff --git a/wx+/image_tools.cpp b/wx+/image_tools.cpp
index a6ecc3d3..90945a44 100644
--- a/wx+/image_tools.cpp
+++ b/wx+/image_tools.cpp
@@ -160,7 +160,7 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const
//for some reason wxDC::DrawText messes up "weak" bidi characters even when wxLayout_RightToLeft is set! (--> arrows in hebrew/arabic)
//=> use mark characters instead:
- const wchar_t rtlMark = L'\u200F';
+ const wchar_t rtlMark = L'\u200F'; //UTF-8: E2 80 8F
if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft)
textFmt = rtlMark + textFmt + rtlMark;
diff --git a/wx+/popup_dlg.cpp b/wx+/popup_dlg.cpp
index 918f44a5..f96884d9 100644
--- a/wx+/popup_dlg.cpp
+++ b/wx+/popup_dlg.cpp
@@ -86,27 +86,31 @@ public:
#ifdef ZEN_WIN
new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this"
#endif
- wxString titleTmp = cfg.title;
+ wxBitmap iconTmp;
+ wxString titleTmp;
switch (type)
{
case DialogInfoType::INFO:
- //"information" is meaningless as caption text!
+ //"Information" is meaningless as caption text!
//confirmation doesn't use info icon
- //m_bitmapMsgType->Hide();
- //m_bitmapMsgType->SetSize(30, -1);
- //m_bitmapMsgType->SetBitmap(getResourceImage(L"msg_info"));
+ //iconTmp = getResourceImage(L"msg_info");
break;
case DialogInfoType::WARNING:
- if (titleTmp.empty()) titleTmp = _("Warning");
- m_bitmapMsgType->SetBitmap(getResourceImage(L"msg_warning"));
+ iconTmp = getResourceImage(L"msg_warning");
+ titleTmp = _("Warning");
break;
case DialogInfoType::ERROR2:
- if (titleTmp.empty()) titleTmp = _("Error");
- m_bitmapMsgType->SetBitmap(getResourceImage(L"msg_error"));
+ iconTmp = getResourceImage(L"msg_error");
+ titleTmp = _("Error");
break;
}
if (cfg.icon.IsOk())
- m_bitmapMsgType->SetBitmap(cfg.icon);
+ iconTmp = cfg.icon;
+
+ if (!cfg.title.empty())
+ titleTmp = cfg.title;
+ //-----------------------------------------------
+ m_bitmapMsgType->SetBitmap(iconTmp);
if (titleTmp.empty())
SetTitle(wxTheApp->GetAppDisplayName());
@@ -140,7 +144,7 @@ public:
if (!cfg.textDetail.empty())
{
- const wxString& text = L"\n" + cfg.textDetail + L"\n"; //add empty top/bottom lines *instead* of using border space!
+ const wxString& text = L"\n" + trimCpy(cfg.textDetail) + L"\n"; //add empty top/bottom lines *instead* of using border space!
setBestInitialSize(*m_textCtrlTextDetail, text, wxSize(maxWidth, maxHeight));
m_textCtrlTextDetail->ChangeValue(text);
}
@@ -185,14 +189,14 @@ private:
void OnButtonAffirmative(wxCommandEvent& event) override
{
if (checkBoxValue_)
- * checkBoxValue_ = m_checkBoxCustom->GetValue();
+ *checkBoxValue_ = m_checkBoxCustom->GetValue();
EndModal(static_cast<int>(ConfirmationButton3::DO_IT));
}
void OnButtonNegative(wxCommandEvent& event) override
{
if (checkBoxValue_)
- * checkBoxValue_ = m_checkBoxCustom->GetValue();
+ *checkBoxValue_ = m_checkBoxCustom->GetValue();
EndModal(static_cast<int>(ConfirmationButton3::DONT_DO_IT));
}
@@ -244,8 +248,8 @@ class zen::ConfirmationDialog3 : public StandardPopupDialog
{
public:
ConfirmationDialog3(wxWindow* parent, DialogInfoType type, const PopupDialogCfg3& cfg, const wxString& labelDoIt, const wxString& labelDontDoIt) :
- StandardPopupDialog(parent, type, cfg.pdCfg),
- buttonToDisableWhenChecked(cfg.buttonToDisableWhenChecked)
+ StandardPopupDialog(parent, type, cfg.pdCfg_),
+ buttonToDisableWhenChecked_(cfg.buttonToDisableWhenChecked_)
{
assert(contains(labelDoIt, L"&"));
assert(contains(labelDontDoIt, L"&"));
@@ -269,7 +273,7 @@ private:
void updateGui()
{
- switch (buttonToDisableWhenChecked)
+ switch (buttonToDisableWhenChecked_)
{
case ConfirmationButton3::DO_IT:
m_buttonAffirmative->Enable(!m_checkBoxCustom->GetValue());
@@ -282,7 +286,7 @@ private:
}
}
- const ConfirmationButton3 buttonToDisableWhenChecked;
+ const ConfirmationButton3 buttonToDisableWhenChecked_;
};
//########################################################################################
diff --git a/wx+/popup_dlg.h b/wx+/popup_dlg.h
index 892c7a83..67f73a11 100644
--- a/wx+/popup_dlg.h
+++ b/wx+/popup_dlg.h
@@ -39,7 +39,7 @@ enum class ConfirmationButton3
enum class ConfirmationButton
{
- DO_IT = static_cast<int>(ConfirmationButton3::DO_IT), //[!]
+ DO_IT = static_cast<int>(ConfirmationButton3::DO_IT ), //[!]
CANCEL = static_cast<int>(ConfirmationButton3::CANCEL), //Clang requires a "static_cast"
};
@@ -73,23 +73,24 @@ private:
struct PopupDialogCfg3
{
- PopupDialogCfg3& setTitle (const wxString& label) { pdCfg.setTitle (label); return *this; }
- PopupDialogCfg3& setMainInstructions (const wxString& label) { pdCfg.setMainInstructions (label); return *this; } //set at least one of these!
- PopupDialogCfg3& setDetailInstructions(const wxString& label) { pdCfg.setDetailInstructions(label); return *this; } //
- PopupDialogCfg3& setCheckBox(bool& value, const wxString& label) { pdCfg.setCheckBox(value, label); return *this; }
+ PopupDialogCfg3& setIcon (const wxBitmap& bmp ) { pdCfg_.setIcon (bmp); return *this; }
+ PopupDialogCfg3& setTitle (const wxString& label) { pdCfg_.setTitle (label); return *this; }
+ PopupDialogCfg3& setMainInstructions (const wxString& label) { pdCfg_.setMainInstructions (label); return *this; } //set at least one of these!
+ PopupDialogCfg3& setDetailInstructions(const wxString& label) { pdCfg_.setDetailInstructions(label); return *this; } //
+ PopupDialogCfg3& setCheckBox(bool& value, const wxString& label) { pdCfg_.setCheckBox(value, label); return *this; }
PopupDialogCfg3& setCheckBox(bool& value, const wxString& label, ConfirmationButton3 disableWhenChecked)
{
assert(disableWhenChecked != ConfirmationButton3::CANCEL);
setCheckBox(value, label);
- buttonToDisableWhenChecked = disableWhenChecked;
+ buttonToDisableWhenChecked_ = disableWhenChecked;
return *this;
}
private:
friend class ConfirmationDialog3;
- PopupDialogCfg pdCfg;
- ConfirmationButton3 buttonToDisableWhenChecked = ConfirmationButton3::CANCEL;
+ PopupDialogCfg pdCfg_;
+ ConfirmationButton3 buttonToDisableWhenChecked_ = ConfirmationButton3::CANCEL;
};
}
diff --git a/wx+/popup_dlg_generated.cpp b/wx+/popup_dlg_generated.cpp
index b726aa9a..6df18dce 100644
--- a/wx+/popup_dlg_generated.cpp
+++ b/wx+/popup_dlg_generated.cpp
@@ -34,7 +34,10 @@ PopupDialogGenerated::PopupDialogGenerated( wxWindow* parent, wxWindowID id, con
m_staticTextMain = new wxStaticText( m_panel33, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextMain->Wrap( -1 );
- bSizer16->Add( m_staticTextMain, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 );
+ bSizer16->Add( m_staticTextMain, 0, wxRIGHT, 10 );
+
+
+ bSizer16->Add( 0, 5, 0, 0, 5 );
m_textCtrlTextDetail = new wxTextCtrl( m_panel33, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER );
bSizer16->Add( m_textCtrlTextDetail, 1, wxEXPAND, 5 );
diff --git a/wx+/std_button_layout.h b/wx+/std_button_layout.h
index 59fda260..3e335882 100644
--- a/wx+/std_button_layout.h
+++ b/wx+/std_button_layout.h
@@ -16,14 +16,13 @@ namespace zen
{
struct StdButtons
{
- StdButtons() : btnYes(nullptr), btnNo(nullptr), btnCancel(nullptr) {}
StdButtons& setAffirmative (wxButton* btn) { btnYes = btn; return *this; }
StdButtons& setNegative (wxButton* btn) { btnNo = btn; return *this; }
StdButtons& setCancel (wxButton* btn) { btnCancel = btn; return *this; }
- wxButton* btnYes;
- wxButton* btnNo;
- wxButton* btnCancel;
+ wxButton* btnYes = nullptr;
+ wxButton* btnNo = nullptr;
+ wxButton* btnCancel = nullptr;
};
void setStandardButtonLayout(wxBoxSizer& sizer, const StdButtons& buttons = StdButtons());
diff --git a/wx+/tooltip.cpp b/wx+/tooltip.cpp
index c2c562ce..8dc79d73 100644
--- a/wx+/tooltip.cpp
+++ b/wx+/tooltip.cpp
@@ -16,15 +16,15 @@
using namespace zen;
-class Tooltip::TooltipDialogGenerated : public wxDialog
+class Tooltip::TooltipDlgGenerated : public wxDialog
{
public:
- TooltipDialogGenerated(wxWindow* parent,
- wxWindowID id = wxID_ANY,
- const wxString& title = {},
- const wxPoint& pos = wxDefaultPosition,
- const wxSize& size = wxDefaultSize,
- long style = 0) : wxDialog(parent, id, title, pos, size, style)
+ TooltipDlgGenerated(wxWindow* parent,
+ wxWindowID id = wxID_ANY,
+ const wxString& title = {},
+ const wxPoint& pos = wxDefaultPosition,
+ const wxSize& size = wxDefaultSize,
+ long style = 0) : wxDialog(parent, id, title, pos, size, style)
{
//Suse Linux/X11: needs parent window, else there are z-order issues
@@ -33,11 +33,11 @@ public:
this->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOTEXT)); //
wxBoxSizer* bSizer158 = new wxBoxSizer(wxHORIZONTAL);
- m_bitmapLeft = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0);
- bSizer158->Add(m_bitmapLeft, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
+ bitmapLeft_ = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0);
+ bSizer158->Add(bitmapLeft_, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
- m_staticTextMain = new wxStaticText(this, wxID_ANY, wxString(), wxDefaultPosition, wxDefaultSize, 0);
- bSizer158->Add(m_staticTextMain, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
+ staticTextMain_ = new wxStaticText(this, wxID_ANY, wxString(), wxDefaultPosition, wxDefaultSize, 0);
+ bSizer158->Add(staticTextMain_, 0, wxALL | wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL, 5);
this->SetSizer(bSizer158);
this->Layout();
@@ -48,57 +48,57 @@ public:
#endif
}
- wxStaticText* m_staticTextMain;
- wxStaticBitmap* m_bitmapLeft;
+ wxStaticText* staticTextMain_;
+ wxStaticBitmap* bitmapLeft_;
};
void Tooltip::show(const wxString& text, wxPoint mousePos, const wxBitmap* bmp)
{
- if (!tipWindow)
- tipWindow = new TooltipDialogGenerated(&parent_); //ownership passed to parent
+ if (!tipWindow_)
+ tipWindow_ = new TooltipDlgGenerated(&parent_); //ownership passed to parent
const wxBitmap& newBmp = bmp ? *bmp : wxNullBitmap;
- if (!isEqual(tipWindow->m_bitmapLeft->GetBitmap(), newBmp))
+ if (!isEqual(tipWindow_->bitmapLeft_->GetBitmap(), newBmp))
{
- tipWindow->m_bitmapLeft->SetBitmap(newBmp);
- tipWindow->Refresh(); //needed if bitmap size changed!
+ tipWindow_->bitmapLeft_->SetBitmap(newBmp);
+ tipWindow_->Refresh(); //needed if bitmap size changed!
}
- if (text != tipWindow->m_staticTextMain->GetLabel())
+ if (text != tipWindow_->staticTextMain_->GetLabel())
{
- tipWindow->m_staticTextMain->SetLabel(text);
- tipWindow->m_staticTextMain->Wrap(600);
+ tipWindow_->staticTextMain_->SetLabel(text);
+ tipWindow_->staticTextMain_->Wrap(600);
}
- tipWindow->GetSizer()->SetSizeHints(tipWindow); //~=Fit() + SetMinSize()
+ tipWindow_->GetSizer()->SetSizeHints(tipWindow_); //~=Fit() + SetMinSize()
//Linux: Fit() seems to be broken => this needs to be called EVERY time inside show, not only if text or bmp change
const wxPoint newPos = wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft ?
- mousePos - wxPoint(30 + tipWindow->GetSize().GetWidth(), 0) :
+ mousePos - wxPoint(30 + tipWindow_->GetSize().GetWidth(), 0) :
mousePos + wxPoint(30, 0);
- if (newPos != tipWindow->GetScreenPosition())
- tipWindow->SetSize(newPos.x, newPos.y, wxDefaultCoord, wxDefaultCoord);
+ if (newPos != tipWindow_->GetScreenPosition())
+ tipWindow_->SetSize(newPos.x, newPos.y, wxDefaultCoord, wxDefaultCoord);
//attention!!! possible endless loop: mouse pointer must NOT be within tipWindow!
//else it will trigger a wxEVT_LEAVE_WINDOW on middle grid which will hide the window, causing the window to be shown again via this method, etc.
- if (!tipWindow->IsShown())
- tipWindow->Show();
+ if (!tipWindow_->IsShown())
+ tipWindow_->Show();
}
void Tooltip::hide()
{
- if (tipWindow)
+ if (tipWindow_)
{
#ifdef ZEN_LINUX
//on wxGTK the tooltip is sometimes not shown again after it was hidden: e.g. drag-selection on middle grid
- tipWindow->Destroy(); //apply brute force:
- tipWindow = nullptr; //
+ tipWindow_->Destroy(); //apply brute force:
+ tipWindow_ = nullptr; //
#else
- tipWindow->Hide();
+ tipWindow_->Hide();
#endif
}
}
diff --git a/wx+/tooltip.h b/wx+/tooltip.h
index 23c7adb1..f2a7043e 100644
--- a/wx+/tooltip.h
+++ b/wx+/tooltip.h
@@ -23,8 +23,8 @@ public:
void hide();
private:
- class TooltipDialogGenerated;
- TooltipDialogGenerated* tipWindow = nullptr;
+ class TooltipDlgGenerated;
+ TooltipDlgGenerated* tipWindow_ = nullptr;
wxWindow& parent_;
};
}
diff --git a/zen/basic_math.h b/zen/basic_math.h
index e9e17466..eed23477 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -18,7 +18,7 @@
namespace numeric
{
template <class T> T abs(T value);
-template <class T> T dist(T a, T b);
+template <class T> auto dist(T a, T b);
template <class T> int sign(T value); //returns one of {-1, 0, 1}
template <class T> T min(T a, T b, T c);
template <class T> T max(T a, T b, T c);
@@ -90,7 +90,7 @@ T abs(T value)
}
template <class T> inline
-T dist(T a, T b)
+auto dist(T a, T b) //return type might be different than T, e.g. std::chrono::duration instead of std::chrono::time_point
{
return a > b ? a - b : b - a;
}
diff --git a/zen/crc.h b/zen/crc.h
index b617bdd0..5c950efc 100644
--- a/zen/crc.h
+++ b/zen/crc.h
@@ -19,6 +19,19 @@
namespace zen
{
+uint16_t getCrc16(const std::string& str);
+uint32_t getCrc32(const std::string& str);
+template <class ByteIterator> uint16_t getCrc16(ByteIterator first, ByteIterator last);
+template <class ByteIterator> uint32_t getCrc32(ByteIterator first, ByteIterator last);
+
+
+
+
+//------------------------- implementation -------------------------------
+inline uint16_t getCrc16(const std::string& str) { return getCrc16(str.begin(), str.end()); }
+inline uint32_t getCrc32(const std::string& str) { return getCrc32(str.begin(), str.end()); }
+
+
template <class ByteIterator> inline
uint16_t getCrc16(ByteIterator first, ByteIterator last)
{
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index 12a6a9f4..98190bba 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -9,6 +9,7 @@
#include <set>
#include "thread.h"
#include "scope_guard.h"
+#include "basic_math.h"
#ifdef ZEN_WIN
#include "device_notify.h"
@@ -358,10 +359,11 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()
//wait until device removal is confirmed, to prevent locking hDir again by some new watch!
if (pimpl_->volRemoval->requestReceived())
{
- const std::chrono::steady_clock::time_point stopTime = std::chrono::steady_clock::now() + std::chrono::seconds(15);
+ const auto startTime = std::chrono::steady_clock::now();
//HandleVolumeRemoval::finished() not guaranteed! note: Windows gives unresponsive applications ca. 10 seconds until unmounting the usb stick in worst case
- while (!pimpl_->volRemoval->finished() && std::chrono::steady_clock::now() < stopTime)
+ while (!pimpl_->volRemoval->finished() &&
+ numeric::dist(std::chrono::steady_clock::now(), startTime) < std::chrono::seconds(15)) //handle potential chrono wrap-around!
{
processGuiMessages(); //DBT_DEVICEREMOVECOMPLETE message is sent here!
std::this_thread::sleep_for(std::chrono::milliseconds(50));
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index b4599d03..3eb284e1 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -62,7 +62,7 @@ void zen::traverseFolder(const Zstring& dirPath,
if (ec == ERROR_NO_MORE_FILES) //not an error situation
return;
//else we have a problem... report it:
- throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), formatSystemError(L"FindNextFile", ec));
+ throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), formatSystemError(L"FindNextFile", ec));
}
//skip "." and ".."
@@ -73,7 +73,7 @@ void zen::traverseFolder(const Zstring& dirPath,
continue;
if (itemNameRaw[0] == 0)
- throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile: Data corruption; item with empty name.");
+ throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile: Data corruption; item with empty name.");
const Zstring& itemPath = appendSeparator(dirPath) + itemNameRaw;
@@ -112,7 +112,7 @@ void zen::traverseFolder(const Zstring& dirPath,
{
struct ::dirent* dirEntry = nullptr;
if (::readdir_r(folder, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r");
+ 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
@@ -133,14 +133,14 @@ void zen::traverseFolder(const Zstring& dirPath,
}
catch (const SysError& e) //failure is not an item-level error since we don't know the normalized name yet!!!
{
- throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)),
+ throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)),
L"Failed to generate normalized file name: " + fmtPath(itemNameRaw) + L"\n" + e.toString()); //too obscure to warrant translation
}
#else
const Zstring& itemName = itemNameRaw;
#endif
if (itemName.empty()) //checks result of osx::normalizeUtfForPosix, too!
- throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name.");
+ 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/fixed_list.h b/zen/fixed_list.h
index 4376c13f..81197eb4 100644
--- a/zen/fixed_list.h
+++ b/zen/fixed_list.h
@@ -15,7 +15,7 @@ namespace zen
{
//std::list(C++11)-like class for inplace element construction supporting non-copyable/non-movable types
//-> no iterator invalidation after emplace_back()
-
+
template <class T>
class FixedList
{
@@ -69,7 +69,7 @@ public:
const_reference& back() const { return lastInsert_->val; }
template <class... Args>
- void emplace_back(Args&&... args)
+ void emplace_back(Args&& ... args)
{
Node* newNode = new Node(std::forward<Args>(args)...);
@@ -160,10 +160,10 @@ class FixedVector
public:
FixedVector() {}
- /*
- class EndIterator {}; //just like FixedList: no iterator invalidation after emplace_back()
+ /*
+ class EndIterator {}; //just like FixedList: no iterator invalidation after emplace_back()
- template <class V>
+ template <class V>
class FixedIterator : public std::iterator<std::forward_iterator_tag, V> //could make this random-access if needed
{
public:
@@ -174,10 +174,10 @@ public:
V& operator* () const { return *cont_[pos_]; }
V* operator->() const { return &*cont_[pos_]; }
private:
- std::vector<std::unique_ptr<T>>& cont_;
- size_t pos_ = 0;
+ std::vector<std::unique_ptr<T>>& cont_;
+ size_t pos_ = 0;
};
- */
+ */
template <class IterImpl, class V>
class FixedIterator : public std::iterator<std::forward_iterator_tag, V> //could make this bidirectional if needed
@@ -188,7 +188,7 @@ public:
inline friend bool operator==(const FixedIterator& lhs, const FixedIterator& rhs) { return lhs.it_ == rhs.it_; }
inline friend bool operator!=(const FixedIterator& lhs, const FixedIterator& rhs) { return !(lhs == rhs); }
V& operator* () const { return **it_; }
- V* operator->() const { return &**it_; }
+ V* operator->() const { return &** it_; }
private:
IterImpl it_; //TODO: avoid iterator invalidation after emplace_back(); caveat: end() must not store old length!
};
@@ -199,10 +199,10 @@ public:
using reference = T&;
using const_reference = const T&;
- iterator begin() { return items_.begin(); }
+ iterator begin() { return items_.begin(); }
iterator end () { return items_.end (); }
- const_iterator begin() const { return items_.begin(); }
+ const_iterator begin() const { return items_.begin(); }
const_iterator end () const { return items_.end (); }
reference front() { return *items_.front(); }
@@ -212,15 +212,15 @@ public:
const_reference& back() const { return *items_.back(); }
template <class... Args>
- void emplace_back(Args&&... args)
+ void emplace_back(Args&& ... args)
{
- items_.push_back(std::make_unique<T>(std::forward<Args>(args)...));
+ items_.push_back(std::make_unique<T>(std::forward<Args>(args)...));
}
template <class Predicate>
void remove_if(Predicate pred)
{
- erase_if(items_, [&](const std::unique_ptr<T>& p){ return pred(*p); });
+ erase_if(items_, [&](const std::unique_ptr<T>& p) { return pred(*p); });
}
void clear() { items_.clear(); }
@@ -232,7 +232,7 @@ private:
FixedVector (const FixedVector&) = delete;
FixedVector& operator=(const FixedVector&) = delete;
- std::vector<std::unique_ptr<T>> items_;
+ std::vector<std::unique_ptr<T>> items_;
};
}
diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp
index d87a1643..08463778 100644
--- a/zen/format_unit.cpp
+++ b/zen/format_unit.cpp
@@ -16,7 +16,7 @@
#ifdef ZEN_WIN
#include "int64.h"
#include "win.h" //includes "windows.h"
- #include "win_ver.h"
+ // #include "win_ver.h"
#elif defined ZEN_LINUX || defined ZEN_MAC
#include <clocale> //thousands separator
@@ -155,6 +155,12 @@ std::wstring zen::remainingTimeToString(double timeInSec)
}
+//std::wstring zen::fractionToString1Dec(double fraction)
+//{
+// return printNumber<std::wstring>(L"%.1f", fraction * 100.0) + L'%'; //no need to internationalize fraction!?
+//}
+
+
std::wstring zen::fractionToString(double fraction)
{
return printNumber<std::wstring>(L"%.2f", fraction * 100.0) + L'%'; //no need to internationalize fraction!?
@@ -299,32 +305,29 @@ std::wstring zen::utcToLocalTimeString(std::int64_t utcTime)
SYSTEMTIME systemTimeLocal = {};
- static const bool useNewLocalTimeCalculation = zen::vistaOrLater();
-
//https://msdn.microsoft.com/en-us/library/ms724277
- if (useNewLocalTimeCalculation) //DST conversion like in Windows 7: NTFS stays fixed, but FAT jumps by one hour
- {
- SYSTEMTIME systemTimeUtc = {};
- if (!::FileTimeToSystemTime(&lastWriteTimeUtc, //__in const FILETIME *lpFileTime,
- &systemTimeUtc)) //__out LPSYSTEMTIME lpSystemTime
- return errorMsg();
-
- if (!::SystemTimeToTzSpecificLocalTime(nullptr, //__in_opt LPTIME_ZONE_INFORMATION lpTimeZone,
- &systemTimeUtc, //__in LPSYSTEMTIME lpUniversalTime,
- &systemTimeLocal)) //__out LPSYSTEMTIME lpLocalTime
- return errorMsg();
- }
- else //DST conversion like in Windows 2000 and XP: FAT times stay fixed, while NTFS jumps
- {
- FILETIME fileTimeLocal = {};
- if (!::FileTimeToLocalFileTime(&lastWriteTimeUtc, //_In_ const FILETIME *lpFileTime,
- &fileTimeLocal)) //_Out_ LPFILETIME lpLocalFileTime
- return errorMsg();
-
- if (!::FileTimeToSystemTime(&fileTimeLocal, //__in const FILETIME *lpFileTime,
- &systemTimeLocal)) //__out LPSYSTEMTIME lpSystemTime
- return errorMsg();
- }
+#ifdef ZEN_WIN_VISTA_AND_LATER
+ //DST conversion like in Vista and later: NTFS stays fixed, but FAT jumps by one hour
+ SYSTEMTIME systemTimeUtc = {};
+ if (!::FileTimeToSystemTime(&lastWriteTimeUtc, //__in const FILETIME *lpFileTime,
+ &systemTimeUtc)) //__out LPSYSTEMTIME lpSystemTime
+ return errorMsg();
+
+ if (!::SystemTimeToTzSpecificLocalTime(nullptr, //__in_opt LPTIME_ZONE_INFORMATION lpTimeZone,
+ &systemTimeUtc, //__in LPSYSTEMTIME lpUniversalTime,
+ &systemTimeLocal)) //__out LPSYSTEMTIME lpLocalTime
+ return errorMsg();
+#else
+ //DST conversion like in Windows 2000 and XP: FAT times stay fixed, while NTFS jumps
+ FILETIME fileTimeLocal = {};
+ if (!::FileTimeToLocalFileTime(&lastWriteTimeUtc, //_In_ const FILETIME *lpFileTime,
+ &fileTimeLocal)) //_Out_ LPFILETIME lpLocalFileTime
+ return errorMsg();
+
+ if (!::FileTimeToSystemTime(&fileTimeLocal, //__in const FILETIME *lpFileTime,
+ &systemTimeLocal)) //__out LPSYSTEMTIME lpSystemTime
+ return errorMsg();
+#endif
zen::TimeComp loc;
loc.year = systemTimeLocal.wYear;
diff --git a/zen/i18n.h b/zen/i18n.h
index 4f62fd4c..e5b0ab2c 100644
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -98,7 +98,7 @@ std::wstring translate(const std::wstring& singular, const std::wstring& plural,
inline
-Global<const TranslationHandler>& getGlobalTranslationHandler()
+Global<const TranslationHandler>& refGlobalTranslationHandler()
{
//getTranslator() may be called even after static objects of this translation unit are destroyed!
static Global<const TranslationHandler> inst; //external linkage even in header!
@@ -110,14 +110,14 @@ Global<const TranslationHandler>& getGlobalTranslationHandler()
inline
void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler)
{
- implementation::getGlobalTranslationHandler().set(std::move(newHandler));
+ implementation::refGlobalTranslationHandler().set(std::move(newHandler));
}
inline
std::shared_ptr<const TranslationHandler> getTranslator()
{
- return implementation::getGlobalTranslationHandler().get();
+ return implementation::refGlobalTranslationHandler().get();
}
}
diff --git a/zen/perf.h b/zen/perf.h
index e3827fd1..6cd874b8 100644
--- a/zen/perf.h
+++ b/zen/perf.h
@@ -7,12 +7,13 @@
#ifndef PERF_H_83947184145342652456
#define PERF_H_83947184145342652456
+#include <chrono>
#include "deprecate.h"
-#include "tick_count.h"
#include "scope_guard.h"
#ifdef ZEN_WIN
#include <sstream>
+ #include "win.h"
#else
#include <iostream>
#endif
@@ -28,54 +29,47 @@ namespace zen
class PerfTimer
{
public:
- class TimerError {};
+ ZEN_DEPRECATE PerfTimer() {}
- ZEN_DEPRECATE
- PerfTimer() : startTime(getTicksNow()) //throw TimerError
- {
- //std::clock() - "counts CPU time in Linux GCC and wall time in VC++" - WTF!???
- if (ticksPerSec_ == 0)
- throw TimerError();
- }
-
- ~PerfTimer() { if (!resultShown) try { showResult(); } catch (TimerError&) { assert(false); } }
+ ~PerfTimer() { if (!resultShown_) showResult(); }
void pause()
{
- if (!paused)
+ if (!paused_)
{
- paused = true;
- elapsedUntilPause += dist(startTime, getTicksNow());
+ paused_ = true;
+ elapsedUntilPause_ += std::chrono::steady_clock::now() - startTime_; //ignore potential ::QueryPerformanceCounter() wrap-around!
}
}
void resume()
{
- if (paused)
+ if (paused_)
{
- paused = false;
- startTime = getTicksNow();
+ paused_ = false;
+ startTime_ = std::chrono::steady_clock::now();
}
}
void restart()
{
- startTime = getTicksNow();
- paused = false;
- elapsedUntilPause = 0;
+ paused_ = false;
+ startTime_ = std::chrono::steady_clock::now();
+ elapsedUntilPause_ = std::chrono::nanoseconds::zero();
}
int64_t timeMs() const
{
- int64_t ticksTotal = elapsedUntilPause;
- if (!paused)
- ticksTotal += dist(startTime, getTicksNow());
- return 1000 * ticksTotal / ticksPerSec_;
+ auto elapsedTotal = elapsedUntilPause_;
+ if (!paused_)
+ elapsedTotal += std::chrono::steady_clock::now() - startTime_;
+
+ return std::chrono::duration_cast<std::chrono::milliseconds>(elapsedTotal).count();
}
void showResult()
{
- const bool wasRunning = !paused;
+ const bool wasRunning = !paused_;
if (wasRunning) pause(); //don't include call to MessageBox()!
ZEN_ON_SCOPE_EXIT(if (wasRunning) resume());
@@ -86,23 +80,14 @@ public:
#else
std::clog << "Perf: duration: " << timeMs() << " ms\n";
#endif
- resultShown = true;
+ resultShown_ = true;
}
private:
- TickVal getTicksNow() const
- {
- const TickVal now = getTicks();
- if (!now.isValid())
- throw TimerError();
- return now;
- }
-
- const std::int64_t ticksPerSec_ = ticksPerSec(); //return 0 on error
- bool resultShown = false;
- TickVal startTime;
- bool paused = false;
- int64_t elapsedUntilPause = 0;
+ bool resultShown_ = false;
+ bool paused_ = false;
+ std::chrono::steady_clock::time_point startTime_ = std::chrono::steady_clock::now(); //uses ::QueryPerformanceCounter()
+ std::chrono::nanoseconds elapsedUntilPause_{}; //std::chrono::duration is uninitialized by default! WTF! When will this stupidity end???
};
}
diff --git a/zen/serialize.h b/zen/serialize.h
index bc047fee..7322cb07 100644
--- a/zen/serialize.h
+++ b/zen/serialize.h
@@ -72,8 +72,8 @@ struct UnbufferedOutputStream
size_t tryWrite(const void* buffer, size_t bytesToWrite); //may return short! CONTRACT: bytesToWrite > 0
};
*/
-//functions based on unbuffered stream abstraction
+//functions based on unbuffered stream abstraction
template <class UnbufferedInputStream, class UnbufferedOutputStream>
void unbufferedStreamCopy(UnbufferedInputStream& streamIn, UnbufferedOutputStream& streamOut, const std::function<void(std::int64_t bytesDelta)>& notifyProgress); //throw X
@@ -104,7 +104,6 @@ struct BufferedOutputStream
template <class N, class BufferedOutputStream> void writeNumber (BufferedOutputStream& stream, const N& num); //
template <class C, class BufferedOutputStream> void writeContainer(BufferedOutputStream& stream, const C& str); //throw ()
template < class BufferedOutputStream> void writeArray (BufferedOutputStream& stream, const void* data, size_t len); //
-
//----------------------------------------------------------------------
class UnexpectedEndOfStreamError {};
template <class N, class BufferedInputStream> N readNumber (BufferedInputStream& stream); //throw UnexpectedEndOfStreamError (corrupted data)
@@ -115,21 +114,23 @@ template < class BufferedInputStream> void readArray (BufferedInputSt
template <class BinContainer>
struct MemoryStreamIn
{
- MemoryStreamIn(const BinContainer& cont) : buffer(cont) {} //this better be cheap!
+ MemoryStreamIn(const BinContainer& cont) : buffer_(cont) {} //this better be cheap!
size_t read(void* data, size_t len) //return "len" bytes unless end of stream!
{
static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
- const size_t bytesRead = std::min(len, buffer.size() - pos);
- auto itFirst = buffer.begin() + pos;
+ const size_t bytesRead = std::min(len, buffer_.size() - pos_);
+ auto itFirst = buffer_.begin() + pos_;
std::copy(itFirst, itFirst + bytesRead, static_cast<char*>(data));
- pos += bytesRead;
+ pos_ += bytesRead;
return bytesRead;
}
+ size_t pos() const { return pos_; }
+
private:
- const BinContainer buffer;
- size_t pos = 0;
+ const BinContainer buffer_;
+ size_t pos_ = 0;
};
template <class BinContainer>
@@ -138,15 +139,15 @@ struct MemoryStreamOut
void write(const void* data, size_t len)
{
static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
- const size_t oldSize = buffer.size();
- buffer.resize(oldSize + len);
- std::copy(static_cast<const char*>(data), static_cast<const char*>(data) + len, buffer.begin() + oldSize);
+ const size_t oldSize = buffer_.size();
+ buffer_.resize(oldSize + len);
+ std::copy(static_cast<const char*>(data), static_cast<const char*>(data) + len, buffer_.begin() + oldSize);
}
- const BinContainer& ref() const { return buffer; }
+ const BinContainer& ref() const { return buffer_; }
private:
- BinContainer buffer;
+ BinContainer buffer_;
};
diff --git a/zen/shell_execute.h b/zen/shell_execute.h
index ee8203c3..2f73fc38 100644
--- a/zen/shell_execute.h
+++ b/zen/shell_execute.h
@@ -38,7 +38,7 @@ bool shellExecuteImpl(Function fillExecInfo, ExecutionType type)
SHELLEXECUTEINFO execInfo = {};
execInfo.cbSize = sizeof(execInfo);
execInfo.lpVerb = nullptr;
- execInfo.nShow = SW_SHOWNORMAL;
+ execInfo.nShow = SW_SHOW;
execInfo.fMask = type == EXEC_TYPE_SYNC ? (SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NOASYNC) : 0;
//don't use SEE_MASK_ASYNCOK -> different async mode than the default which returns successful despite errors!
execInfo.fMask |= SEE_MASK_FLAG_NO_UI; //::ShellExecuteEx() shows a non-blocking pop-up dialog on errors -> we want a blocking one
@@ -98,7 +98,7 @@ void shellExecute(const Zstring& command, ExecutionType type) //throw FileError
{
filePath = argv[0];
for (auto it = argv.begin() + 1; it != argv.end(); ++it)
- arguments += (it != argv.begin() ? L" " : L"") +
+ arguments += (it == argv.begin() + 1 ? L"" : L" ") +
(it->empty() || std::any_of(it->begin(), it->end(), &isWhiteSpace<wchar_t>) ? L"\"" + *it + L"\"" : *it);
}
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index 064d5b51..48f475f3 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -235,7 +235,7 @@ size_t hashBytesAppend(size_t hashVal, ByteIterator first, ByteIterator last)
#endif
static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, "");
- for (; first != last; ++first)
+ for (; first != last; ++first)
{
hashVal ^= static_cast<size_t>(*first);
hashVal *= prime;
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 9b8e7328..5292dfc6 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -24,6 +24,7 @@ namespace zen
{
template <class Char> bool isWhiteSpace(Char ch);
template <class Char> bool isDigit (Char ch); //not exactly the same as "std::isdigit" -> we consider '0'-'9' only!
+template <class Char> bool isHexDigit (Char ch);
template <class Char> bool isAlpha (Char ch);
template <class S, class T> bool startsWith(const S& str, const T& prefix); //
@@ -51,6 +52,9 @@ template <class S, class T, class U> S replaceCpy(const S& str, const T& oldT
template <class S, class Num> S numberTo(const Num& number);
template <class Num, class S > Num stringTo(const S& str);
+std::pair<char, char> hexify (unsigned char c, bool upperCase = true);
+char unhexify(char high, char low);
+
template <class S, class T, class Num> S printNumber(const T& format, const Num& number); //format a single number using std::snprintf()
//string to string conversion: converts string-like type into char-compatible target string class
@@ -101,6 +105,16 @@ bool isDigit(Char ch) //similar to implmenetation of std::::isdigit()!
}
+template <class Char> inline
+bool isHexDigit(Char c)
+{
+ static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, "");
+ return (static_cast<Char>('0') <= c && c <= static_cast<Char>('9')) ||
+ (static_cast<Char>('A') <= c && c <= static_cast<Char>('F')) ||
+ (static_cast<Char>('a') <= c && c <= static_cast<Char>('f'));
+}
+
+
template <> bool isAlpha(char ch) = delete; //probably not a good idea with UTF-8 anyway...
template <> inline bool isAlpha(wchar_t ch) { return std::iswalpha(ch) != 0; }
@@ -297,7 +311,7 @@ S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll)
return str;
const auto* const newBegin = strBegin(newTerm);
- const auto* const newEnd = newBegin + strLength(newTerm);
+ const auto* const newEnd = newBegin + strLength(newTerm);
S output;
for (;;)
@@ -658,6 +672,42 @@ Num stringTo(const S& str)
return impl::stringTo<Num>(str, TypeTag());
}
+
+
+inline //hexify beats "printNumber<std::string>("%02X", c)" by a nice factor of 3!
+std::pair<char, char> hexify(unsigned char c, bool upperCase)
+{
+ auto hexifyDigit = [upperCase](int num) -> char //input [0, 15], output 0-9, A-F
+ {
+ assert(0 <= num&& num <= 15); //guaranteed by design below!
+ if (num <= 9)
+ return static_cast<char>('0' + num); //no signed/unsigned char problem here!
+
+ if (upperCase)
+ return static_cast<char>('A' + (num - 10));
+ else
+ return static_cast<char>('a' + (num - 10));
+ };
+ return std::make_pair(hexifyDigit(c / 16), hexifyDigit(c % 16));
+}
+
+
+inline //unhexify beats "::sscanf(&it[3], "%02X", &tmp)" by a factor of 3000 for ~250000 calls!!!
+char unhexify(char high, char low)
+{
+ auto unhexifyDigit = [](char hex) -> int //input 0-9, a-f, A-F; output range: [0, 15]
+ {
+ if ('0' <= hex && hex <= '9') //no signed/unsigned char problem here!
+ return hex - '0';
+ else if ('A' <= hex && hex <= 'F')
+ return (hex - 'A') + 10;
+ else if ('a' <= hex && hex <= 'f')
+ return (hex - 'a') + 10;
+ assert(false);
+ return 0;
+ };
+ return static_cast<unsigned char>(16 * unhexifyDigit(high) + unhexifyDigit(low)); //[!] convert to unsigned char first, then to char (which may be signed)
+}
}
#endif //STRING_TOOLS_H_213458973046
diff --git a/zen/thread.h b/zen/thread.h
index ac94da6a..5bb02a0e 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -460,9 +460,9 @@ std::uint64_t getThreadId()
return ::GetCurrentThreadId(); //no-fail
#elif defined ZEN_LINUX
- //obviously "gettid()" is not available on Ubuntu/Debian/Suse => use the OpenSSL approach:
- static_assert(sizeof(std::uint64_t) >= sizeof(void*), "");
- return reinterpret_cast<std::uint64_t>(static_cast<void*>(&errno));
+ //obviously "gettid()" is not available on Ubuntu/Debian/Suse => use the OpenSSL approach:
+ static_assert(sizeof(std::uint64_t) >= sizeof(void*), "");
+ return reinterpret_cast<std::uint64_t>(static_cast<void*>(&errno));
#elif defined ZEN_MAC
uint64_t tid = 0;
diff --git a/zen/tick_count.h b/zen/tick_count.h
deleted file mode 100644
index 5ba4fd1b..00000000
--- a/zen/tick_count.h
+++ /dev/null
@@ -1,141 +0,0 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef TICK_COUNT_H_3807326223463457
-#define TICK_COUNT_H_3807326223463457
-
-#include <cstdint>
-#include "type_traits.h"
-#include "basic_math.h"
-
-#ifdef ZEN_WIN
- #include "win.h" //includes "windows.h"
-#elif defined ZEN_LINUX
- #include <time.h> //Posix ::clock_gettime()
-#elif defined ZEN_MAC
- #include <mach/mach_time.h>
-#endif
-
-
-namespace zen
-{
-//a portable "GetTickCount()" using "wall time equivalent" - e.g. no jumps due to ntp time corrections
-class TickVal;
-int64_t dist(const TickVal& lhs, const TickVal& rhs); //use absolute difference for paranoid security: even QueryPerformanceCounter "wraps-around" at *some* time
-
-int64_t ticksPerSec(); //return 0 on error
-TickVal getTicks(); //return invalid value on error: !TickVal::isValid()
-
-
-
-
-
-
-
-
-
-//############################ implementation ##############################
-class TickVal
-{
-public:
-#ifdef ZEN_WIN
- using NativeVal = LARGE_INTEGER;
-#elif defined ZEN_LINUX
- using NativeVal = timespec;
-#elif defined ZEN_MAC
- using NativeVal = uint64_t;
-#endif
-
- TickVal() {}
- explicit TickVal(const NativeVal& val) : val_(val) {}
-
- inline friend
- int64_t dist(const TickVal& lhs, const TickVal& rhs)
- {
-#ifdef ZEN_WIN
- return numeric::dist(lhs.val_.QuadPart, rhs.val_.QuadPart); //std::abs(a - b) can lead to overflow!
-#elif defined ZEN_LINUX
- //structure timespec documented with members:
- // time_t tv_sec seconds
- // long tv_nsec nanoseconds
- const int64_t deltaSec = lhs.val_.tv_sec - rhs.val_.tv_sec;
- const int64_t deltaNsec = lhs.val_.tv_nsec - rhs.val_.tv_nsec;
- return numeric::abs(deltaSec * 1000000000 + deltaNsec);
-#elif defined ZEN_MAC
- return numeric::dist(lhs.val_, rhs.val_);
-#endif
- }
-
- inline friend
- bool operator<(const TickVal& lhs, const TickVal& rhs)
- {
-#ifdef ZEN_WIN
- return lhs.val_.QuadPart < rhs.val_.QuadPart;
-#elif defined ZEN_LINUX
- if (lhs.val_.tv_sec != rhs.val_.tv_sec)
- return lhs.val_.tv_sec < rhs.val_.tv_sec;
- return lhs.val_.tv_nsec < rhs.val_.tv_nsec;
-#elif defined ZEN_MAC
- return lhs.val_ < rhs.val_;
-#endif
- }
-
- bool isValid() const { return dist(*this, TickVal()) != 0; }
-
-private:
- NativeVal val_ {};
-};
-
-
-inline
-int64_t ticksPerSec() //return 0 on error
-{
-#ifdef ZEN_WIN
- LARGE_INTEGER frequency = {};
- if (!::QueryPerformanceFrequency(&frequency)) //MSDN promises: "The frequency cannot change while the system is running."
- return 0; //MSDN: "This won't occur on any system that runs Windows XP or later."
- static_assert(sizeof(int64_t) >= sizeof(frequency.QuadPart), "");
- return frequency.QuadPart;
-
-#elif defined ZEN_LINUX
- return 1000000000; //precision: nanoseconds
-
-#elif defined ZEN_MAC
- mach_timebase_info_data_t tbi = {};
- if (::mach_timebase_info(&tbi) != KERN_SUCCESS)
- return 0;
- //structure mach_timebase_info_data_t documented with members:
- // uint32_t numer;
- // uint32_t denom;
- return static_cast<int64_t>(1000000000) * tbi.denom / tbi.numer;
-#endif
-}
-
-
-inline
-TickVal getTicks() //return !isValid() on error
-{
-#ifdef ZEN_WIN
- LARGE_INTEGER now = {};
- if (!::QueryPerformanceCounter(&now))
- return TickVal();
- //detailed info about QPC: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408
- //- MSDN: "No need to set the thread affinity"
-
-#elif defined ZEN_LINUX
- //gettimeofday() seems fine but is deprecated
- timespec now = {};
- if (::clock_gettime(CLOCK_MONOTONIC_RAW, &now) != 0) //CLOCK_MONOTONIC measures time reliably across processors!
- return TickVal();
-
-#elif defined ZEN_MAC
- uint64_t now = ::mach_absolute_time(); //can this call fail???
-#endif
- return TickVal(now);
-}
-}
-
-#endif //TICK_COUNT_H_3807326223463457
bgstack15