From 226ac347c51e21440d1740d85b5e9912d1ce08e5 Mon Sep 17 00:00:00 2001 From: "B. Stack" Date: Mon, 6 Dec 2021 07:57:52 -0500 Subject: add upstream 11.15 --- Changelog.txt | 11 + FreeFileSync/Build/Resources/Languages.zip | Bin 507711 -> 507742 bytes FreeFileSync/Build/Resources/bell.wav | Bin 143370 -> 102500 bytes FreeFileSync/Build/Resources/cacert.pem | 102 +++++-- FreeFileSync/Build/Resources/fail.wav | Bin 0 -> 35092 bytes FreeFileSync/Build/Resources/fail2.wav | Bin 0 -> 50454 bytes FreeFileSync/Build/Resources/remind.wav | Bin 0 -> 59894 bytes FreeFileSync/Source/Makefile | 1 + FreeFileSync/Source/RealTimeSync/Makefile | 1 + .../Source/RealTimeSync/folder_selector2.cpp | 1 + FreeFileSync/Source/RealTimeSync/main_dlg.cpp | 1 + FreeFileSync/Source/afs/abstract.h | 1 + FreeFileSync/Source/afs/ftp.cpp | 17 ++ FreeFileSync/Source/application.cpp | 4 +- FreeFileSync/Source/base/dir_lock.cpp | 6 +- FreeFileSync/Source/config.cpp | 63 +++-- FreeFileSync/Source/config.h | 1 + FreeFileSync/Source/ui/batch_status_handler.cpp | 12 +- FreeFileSync/Source/ui/batch_status_handler.h | 2 + FreeFileSync/Source/ui/cfg_grid.cpp | 2 +- FreeFileSync/Source/ui/file_grid.cpp | 2 +- FreeFileSync/Source/ui/gui_generated.cpp | 51 +++- FreeFileSync/Source/ui/gui_generated.h | 62 +++-- FreeFileSync/Source/ui/gui_status_handler.cpp | 24 +- FreeFileSync/Source/ui/gui_status_handler.h | 11 +- FreeFileSync/Source/ui/main_dlg.cpp | 32 ++- FreeFileSync/Source/ui/small_dlgs.cpp | 33 ++- FreeFileSync/Source/ui/tree_grid.cpp | 2 +- FreeFileSync/Source/version/version.h | 2 +- License.txt | 304 +++++++++++++-------- wx+/popup_dlg.cpp | 25 ++ wx+/popup_dlg.h | 3 + wx+/std_button_layout.h | 61 +++-- xBRZ/src/xbrz.cpp | 10 +- zen/file_access.cpp | 65 +---- zen/file_access.h | 9 - zen/file_path.cpp | 79 ++++++ zen/file_path.h | 27 ++ zen/http.cpp | 1 - zen/resolve_path.cpp | 3 +- zen/symlink_target.h | 15 +- zen/zstring.h | 4 +- 42 files changed, 698 insertions(+), 352 deletions(-) create mode 100644 FreeFileSync/Build/Resources/fail.wav create mode 100644 FreeFileSync/Build/Resources/fail2.wav create mode 100644 FreeFileSync/Build/Resources/remind.wav create mode 100644 zen/file_path.cpp create mode 100644 zen/file_path.h diff --git a/Changelog.txt b/Changelog.txt index b6984b1d..0e2924dc 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,14 @@ +FreeFileSync 11.15 [2021-12-03] +------------------------------- +Play sound reminder when waiting for user confirmation +Enhanced crash diagnostics with known triggers +Defer reporting third-party incompatibilities until after crashing +Support running FreeFileSync from Dokany-based encrypted volumes +Fixed Server 2019 not being detected for log file +Use native representation for modified config (macOS) +Improved WinMerge detection for external app integration + + FreeFileSync 11.14 [2021-09-20] ------------------------------- Authenticate (S)FTP connections using OpenSSL 3.0 diff --git a/FreeFileSync/Build/Resources/Languages.zip b/FreeFileSync/Build/Resources/Languages.zip index 156bfa90..9044f9bc 100644 Binary files a/FreeFileSync/Build/Resources/Languages.zip and b/FreeFileSync/Build/Resources/Languages.zip differ diff --git a/FreeFileSync/Build/Resources/bell.wav b/FreeFileSync/Build/Resources/bell.wav index 9d66acf9..5c3e245a 100644 Binary files a/FreeFileSync/Build/Resources/bell.wav and b/FreeFileSync/Build/Resources/bell.wav differ diff --git a/FreeFileSync/Build/Resources/cacert.pem b/FreeFileSync/Build/Resources/cacert.pem index fdb454dc..0bf312fe 100644 --- a/FreeFileSync/Build/Resources/cacert.pem +++ b/FreeFileSync/Build/Resources/cacert.pem @@ -1,7 +1,7 @@ ## ## Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Mon Jul 5 21:35:54 2021 GMT +## Certificate data from Mozilla as of: Tue Oct 26 03:12:05 2021 GMT ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates @@ -14,7 +14,7 @@ ## Just configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version 1.28. -## SHA256: c8f6733d1ff4e6a4769c182971a1234f95ae079247a9c439a13423fe8ba5c24f +## SHA256: bb36818a81feaa4cca61101e6d6276cd09e972efcb08112dfed846918ca41d7f ## @@ -381,26 +381,6 @@ mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K -----END CERTIFICATE----- -DST Root CA X3 -============== ------BEGIN CERTIFICATE----- -MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK -ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X -DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 -cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT -rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 -UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy -xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d -utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T -AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ -MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug -dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE -GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw -RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS -fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ ------END CERTIFICATE----- - SwissSign Gold CA - G2 ====================== -----BEGIN CERTIFICATE----- @@ -3172,3 +3152,81 @@ WWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7DP78v3DSk+yshzWePS/Tj OPQD8rv7gmsHINFSH5pkAnuYZttcTVoP0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZck bxJF0WddCajJFdr60qZfE2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb -----END CERTIFICATE----- + +TunTrust Root CA +================ +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQELBQAwYTELMAkG +A1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUgQ2VydGlmaWNhdGlvbiBFbGVj +dHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJvb3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQw +NDI2MDg1NzU2WjBhMQswCQYDVQQGEwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBD +ZXJ0aWZpY2F0aW9uIEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZn56eY+hz +2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd2JQDoOw05TDENX37Jk0b +bjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgFVwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7 +NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZGoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAd +gjH8KcwAWJeRTIAAHDOFli/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViW +VSHbhlnUr8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2eY8f +Tpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIbMlEsPvLfe/ZdeikZ +juXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISgjwBUFfyRbVinljvrS5YnzWuioYas +DXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwS +VXAkPcvCFDVDXSdOvsC9qnyW5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI +04Y+oXNZtPdEITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 +90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+zxiD2BkewhpMl +0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYuQEkHDVneixCwSQXi/5E/S7fd +Ao74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRY +YdZ2vyJ/0Adqp2RT8JeNnYA/u8EH22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJp +adbGNjHh/PqAulxPxOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65x +xBzndFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5Xc0yGYuP +jCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7bnV2UqL1g52KAdoGDDIzM +MEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQCvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9z +ZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZHu/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3r +AZ3r2OvEhJn7wAzMMujjd9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= +-----END CERTIFICATE----- + +HARICA TLS RSA Root CA 2021 +=========================== +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG +EwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u +cyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0EgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUz +OFoXDTQ1MDIxMzEwNTUzN1owbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRl +bWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNB +IFJvb3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569lmwVnlskN +JLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE4VGC/6zStGndLuwRo0Xu +a2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uva9of08WRiFukiZLRgeaMOVig1mlDqa2Y +Ulhu2wr7a89o+uOkXjpFc5gH6l8Cct4MpbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K +5FrZx40d/JiZ+yykgmvwKh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEv +dmn8kN3bLW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcYAuUR +0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqBAGMUuTNe3QvboEUH +GjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYqE613TBoYm5EPWNgGVMWX+Ko/IIqm +haZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHrW2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQ +CPxrvrNQKlr9qEgYRtaQQJKQCoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAUX15QvWiWkKQU +EapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3f5Z2EMVGpdAgS1D0NTsY9FVq +QRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxajaH6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxD +QpSbIPDRzbLrLFPCU3hKTwSUQZqPJzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcR +j88YxeMn/ibvBZ3PzzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5 +vZStjBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0/L5H9MG0 +qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pTBGIBnfHAT+7hOtSLIBD6 +Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79aPib8qXPMThcFarmlwDB31qlpzmq6YR/ +PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YWxw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnn +kf3/W9b3raYvAwtt41dU63ZTGI0RmLo= +-----END CERTIFICATE----- + +HARICA TLS ECC Root CA 2021 +=========================== +-----BEGIN CERTIFICATE----- +MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQswCQYDVQQGEwJH +UjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBD +QTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoX +DTQ1MDIxMzExMDEwOVowbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWlj +IGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJv +b3QgQ0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7KKrxcm1l +AEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9YSTHMmE5gEYd103KUkE+b +ECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW +0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAi +rcJRQO9gcS3ujwLEXQNwSaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/Qw +CZ61IygNnxS2PFOiTAZpffpskcYqSUXm7LcT4Tps +-----END CERTIFICATE----- diff --git a/FreeFileSync/Build/Resources/fail.wav b/FreeFileSync/Build/Resources/fail.wav new file mode 100644 index 00000000..50a8d08a Binary files /dev/null and b/FreeFileSync/Build/Resources/fail.wav differ diff --git a/FreeFileSync/Build/Resources/fail2.wav b/FreeFileSync/Build/Resources/fail2.wav new file mode 100644 index 00000000..1b6a8c38 Binary files /dev/null and b/FreeFileSync/Build/Resources/fail2.wav differ diff --git a/FreeFileSync/Build/Resources/remind.wav b/FreeFileSync/Build/Resources/remind.wav new file mode 100644 index 00000000..d5b47041 Binary files /dev/null and b/FreeFileSync/Build/Resources/remind.wav differ diff --git a/FreeFileSync/Source/Makefile b/FreeFileSync/Source/Makefile index 987362b6..1d422d02 100644 --- a/FreeFileSync/Source/Makefile +++ b/FreeFileSync/Source/Makefile @@ -80,6 +80,7 @@ cppFiles+=ui/version_check.cpp cppFiles+=../../libcurl/rest.cpp cppFiles+=../../zen/file_access.cpp cppFiles+=../../zen/file_io.cpp +cppFiles+=../../zen/file_path.cpp cppFiles+=../../zen/file_traverser.cpp cppFiles+=../../zen/http.cpp cppFiles+=../../zen/zstring.cpp diff --git a/FreeFileSync/Source/RealTimeSync/Makefile b/FreeFileSync/Source/RealTimeSync/Makefile index 716bbf1b..3a69ce60 100644 --- a/FreeFileSync/Source/RealTimeSync/Makefile +++ b/FreeFileSync/Source/RealTimeSync/Makefile @@ -34,6 +34,7 @@ cppFiles+=../../../xBRZ/src/xbrz.cpp cppFiles+=../../../zen/dir_watcher.cpp cppFiles+=../../../zen/file_access.cpp cppFiles+=../../../zen/file_io.cpp +cppFiles+=../../../zen/file_path.cpp cppFiles+=../../../zen/file_traverser.cpp cppFiles+=../../../zen/format_unit.cpp cppFiles+=../../../zen/legacy_compiler.cpp diff --git a/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp b/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp index f9897c61..d8478473 100644 --- a/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp +++ b/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp @@ -7,6 +7,7 @@ #include "folder_selector2.h" #include #include +#include #include #include #include diff --git a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp index 0e4c21df..dc7238bf 100644 --- a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp +++ b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include "config.h" diff --git a/FreeFileSync/Source/afs/abstract.h b/FreeFileSync/Source/afs/abstract.h index a507a152..cd5aa1a6 100644 --- a/FreeFileSync/Source/afs/abstract.h +++ b/FreeFileSync/Source/afs/abstract.h @@ -257,6 +257,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t //Note: it MAY happen that copyFileTransactional() leaves temp files behind, e.g. temporary network drop. // => clean them up at an appropriate time (automatically set sync directions to delete them). They have the following ending: static inline const Zchar* const TEMP_FILE_ENDING = Zstr(".ffs_tmp"); //don't use Zstring as global constant: avoid static initialization order problem in global namespace! + // caveat: ending is hard-coded by RealTimeSync struct FileCopyResult { diff --git a/FreeFileSync/Source/afs/ftp.cpp b/FreeFileSync/Source/afs/ftp.cpp index bedd811f..ce5aaa02 100644 --- a/FreeFileSync/Source/afs/ftp.cpp +++ b/FreeFileSync/Source/afs/ftp.cpp @@ -17,6 +17,10 @@ #include #include +warn_static("remove after test (including openssl-headers set for ftp.cpp") +//#include //SSL_OP_IGNORE_UNEXPECTED_EOF + + using namespace zen; using namespace fff; using AFS = AbstractFileSystem; @@ -454,6 +458,19 @@ public: { options.emplace_back(CURLOPT_USE_SSL, CURLUSESSL_ALL); //require SSL for both control and data options.emplace_back(CURLOPT_FTPSSLAUTH, CURLFTPAUTH_TLS); //try TLS first, then SSL (currently: CURLFTPAUTH_DEFAULT == CURLFTPAUTH_SSL) + + + warn_static("remove after test") +#if 0 + using SslContextCbType = CURLcode (*)(CURL* curl, SSL_CTX* ssl_ctx, void* userptr); //needed for cdecl function pointer cast + SslContextCbType onSslContextWrapper = [](CURL* curl, SSL_CTX* ssl_ctx, void* userptr) + { + assert((::SSL_CTX_get_options(ssl_ctx) & SSL_OP_IGNORE_UNEXPECTED_EOF) == 0); + ::SSL_CTX_set_options(ssl_ctx, SSL_OP_IGNORE_UNEXPECTED_EOF); //does not set, but *adds* options; no-fail + return CURLE_OK; + }; + options.emplace_back(CURLOPT_SSL_CTX_FUNCTION, onSslContextWrapper); +#endif } //let's not hold our breath until Curl adds a reasonable PASV handling => patch libcurl accordingly! diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp index 16f82c8a..00c5281b 100644 --- a/FreeFileSync/Source/application.cpp +++ b/FreeFileSync/Source/application.cpp @@ -7,6 +7,7 @@ #include "application.h" #include #include +#include #include #include #include @@ -39,8 +40,6 @@ IMPLEMENT_APP(Application) namespace { - - std::vector getCommandlineArgs(const wxApp& app) { std::vector args; @@ -593,6 +592,7 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat batchCfg.mainCfg.autoRetryCount, batchCfg.mainCfg.autoRetryDelay, globalCfg.soundFileSyncFinished, + globalCfg.soundFileAlertPending, globalCfg.dpiLayouts[getDpiScalePercent()].progressDlg.dlgSize, globalCfg.dpiLayouts[getDpiScalePercent()].progressDlg.isMaximized, batchCfg.batchExCfg.autoCloseSummary, diff --git a/FreeFileSync/Source/base/dir_lock.cpp b/FreeFileSync/Source/base/dir_lock.cpp index ba3b7974..b449f976 100644 --- a/FreeFileSync/Source/base/dir_lock.cpp +++ b/FreeFileSync/Source/base/dir_lock.cpp @@ -3,6 +3,7 @@ // * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * // * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * // ***************************************************************************** + #include "dir_lock.h" #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #include #include @@ -500,8 +502,8 @@ public: tidyUp(); //optimization: check if we already own a lock for this path - auto itGuid = guidByPath_.find(lockFilePath); - if (itGuid != guidByPath_.end()) + if (auto itGuid = guidByPath_.find(lockFilePath); + itGuid != guidByPath_.end()) if (const std::shared_ptr& activeLock = getActiveLock(itGuid->second)) //returns null-lock if not found return activeLock; //SharedDirLock is still active -> enlarge circle of shared ownership diff --git a/FreeFileSync/Source/config.cpp b/FreeFileSync/Source/config.cpp index 111ba57a..e5cb92c7 100644 --- a/FreeFileSync/Source/config.cpp +++ b/FreeFileSync/Source/config.cpp @@ -23,21 +23,21 @@ using namespace fff; //functionally needed for correct overload resolution!!! namespace { //------------------------------------------------------------------------------------------------------------------------------- -const int XML_FORMAT_GLOBAL_CFG = 22; //2021-07-31 +const int XML_FORMAT_GLOBAL_CFG = 23; //2021-12-02 const int XML_FORMAT_SYNC_CFG = 17; //2020-10-14 //------------------------------------------------------------------------------------------------------------------------------- } const ExternalApp fff::extCommandFileBrowse - //"xdg-open \"%parent_path%\"" -> not good enough: we need %local_path% for proper MTP/Google Drive handling - {L"Browse directory", "xdg-open \"$(dirname \"%local_path%\")\""}; - //mark for extraction: _("Browse directory") Linux doesn't use the term "folder" +//"xdg-open \"%parent_path%\"" -> not good enough: we need %local_path% for proper MTP/Google Drive handling +{L"Browse directory", "xdg-open \"$(dirname \"%local_path%\")\""}; +//mark for extraction: _("Browse directory") Linux doesn't use the term "folder" const ExternalApp fff::extCommandOpenDefault - //"xdg-open \"%parent_path%\"" -> not good enough: we need %local_path% for proper MTP/Google Drive handling - {L"Open with default application", "xdg-open \"%local_path%\""}; +//"xdg-open \"%parent_path%\"" -> not good enough: we need %local_path% for proper MTP/Google Drive handling +{L"Open with default application", "xdg-open \"%local_path%\""}; XmlType getXmlTypeNoThrow(const XmlDoc& doc) //throw() @@ -87,8 +87,11 @@ void setXmlType(XmlDoc& doc, XmlType type) //throw() } + + XmlGlobalSettings::XmlGlobalSettings() : - soundFileSyncFinished(getResourceDirPf() + Zstr("bell.wav")) + soundFileSyncFinished(getResourceDirPf() + Zstr("bell.wav")), + soundFileAlertPending(getResourceDirPf() + Zstr("remind.wav")) { } @@ -1502,8 +1505,6 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& cfg, int formatVer) in2["VerifyCopiedFiles" ].attribute("Enabled", cfg.verifyFileCopy); in2["LogFiles" ].attribute("MaxAge", cfg.logfilesMaxAgeDays); in2["LogFiles" ].attribute("Format", cfg.logFormat); - in2["NotificationSound" ].attribute("CompareFinished", cfg.soundFileCompareFinished); - in2["NotificationSound" ].attribute("SyncFinished", cfg.soundFileSyncFinished); //TODO: remove old parameter after migration! 2021-03-06 if (formatVer < 21) @@ -1515,18 +1516,6 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& cfg, int formatVer) in2["ProgressDialog"].attribute("AutoClose", cfg.progressDlgAutoClose); - //TODO: remove if parameter migration after some time! 2019-05-29 - if (formatVer < 13) - { - if (!cfg.soundFileCompareFinished.empty()) cfg.soundFileCompareFinished = getResourceDirPf() + cfg.soundFileCompareFinished; - if (!cfg.soundFileSyncFinished .empty()) cfg.soundFileSyncFinished = getResourceDirPf() + cfg.soundFileSyncFinished; - } - else - { - cfg.soundFileCompareFinished = resolveFfsResourceMacro(cfg.soundFileCompareFinished); - cfg.soundFileSyncFinished = resolveFfsResourceMacro(cfg.soundFileSyncFinished); - } - //TODO: remove if parameter migration after some time! 2018-08-13 if (formatVer < 14) if (cfg.logfilesMaxAgeDays == 14) //default value was too small @@ -1574,6 +1563,32 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& cfg, int formatVer) inOpt["WarnVersioningFolderPartOfSync"].attribute("Show", cfg.warnDlgs.warnVersioningFolderPartOfSync); } + //TODO: remove after migration! 2021-12-02 + if (formatVer < 23) + { + in2["NotificationSound"].attribute("CompareFinished", cfg.soundFileCompareFinished); + in2["NotificationSound"].attribute("SyncFinished", cfg.soundFileSyncFinished); + } + else + { + in2["Sounds"]["CompareFinished"].attribute("Path", cfg.soundFileCompareFinished); + in2["Sounds"]["SyncFinished" ].attribute("Path", cfg.soundFileSyncFinished); + in2["Sounds"]["AlertPending" ].attribute("Path", cfg.soundFileAlertPending); + } + + //TODO: remove if parameter migration after some time! 2019-05-29 + if (formatVer < 13) + { + if (!cfg.soundFileCompareFinished.empty()) cfg.soundFileCompareFinished = getResourceDirPf() + cfg.soundFileCompareFinished; + if (!cfg.soundFileSyncFinished .empty()) cfg.soundFileSyncFinished = getResourceDirPf() + cfg.soundFileSyncFinished; + } + else + { + cfg.soundFileCompareFinished = resolveFfsResourceMacro(cfg.soundFileCompareFinished); + cfg.soundFileSyncFinished = resolveFfsResourceMacro(cfg.soundFileSyncFinished); + cfg.soundFileAlertPending = resolveFfsResourceMacro(cfg.soundFileAlertPending); + } + XmlIn inMainWin = in["MainDialog"]; //TODO: remove old parameter after migration! 2020-12-03 @@ -2329,8 +2344,6 @@ void writeConfig(const XmlGlobalSettings& cfg, XmlOut& out) out["VerifyCopiedFiles" ].attribute("Enabled", cfg.verifyFileCopy); out["LogFiles" ].attribute("MaxAge", cfg.logfilesMaxAgeDays); out["LogFiles" ].attribute("Format", cfg.logFormat); - out["NotificationSound" ].attribute("CompareFinished", substituteFfsResourcePath(cfg.soundFileCompareFinished)); - out["NotificationSound" ].attribute("SyncFinished", substituteFfsResourcePath(cfg.soundFileSyncFinished)); out["ProgressDialog"].attribute("AutoClose", cfg.progressDlgAutoClose); @@ -2352,6 +2365,10 @@ void writeConfig(const XmlGlobalSettings& cfg, XmlOut& out) outOpt["WarnDirectoryLockFailed" ].attribute("Show", cfg.warnDlgs.warnDirectoryLockFailed); outOpt["WarnVersioningFolderPartOfSync"].attribute("Show", cfg.warnDlgs.warnVersioningFolderPartOfSync); + out["Sounds"]["CompareFinished"].attribute("Path", substituteFfsResourcePath(cfg.soundFileCompareFinished)); + out["Sounds"]["SyncFinished" ].attribute("Path", substituteFfsResourcePath(cfg.soundFileSyncFinished)); + out["Sounds"]["AlertPending" ].attribute("Path", substituteFfsResourcePath(cfg.soundFileAlertPending)); + //gui specific global settings (optional) XmlOut outMainWin = out["MainDialog"]; diff --git a/FreeFileSync/Source/config.h b/FreeFileSync/Source/config.h index 4278ebf1..9e366cb9 100644 --- a/FreeFileSync/Source/config.h +++ b/FreeFileSync/Source/config.h @@ -166,6 +166,7 @@ struct XmlGlobalSettings Zstring soundFileCompareFinished; Zstring soundFileSyncFinished; + Zstring soundFileAlertPending; ConfirmationDialogs confirmDlgs; WarningDialogs warnDlgs; diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp index 7950f76d..3e5ee9a5 100644 --- a/FreeFileSync/Source/ui/batch_status_handler.cpp +++ b/FreeFileSync/Source/ui/batch_status_handler.cpp @@ -25,6 +25,7 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress, size_t autoRetryCount, std::chrono::seconds autoRetryDelay, const Zstring& soundFileSyncComplete, + const Zstring& soundFileAlertPending, wxSize progressDlgSize, bool dlgMaximize, bool autoCloseDialog, PostSyncAction postSyncAction, @@ -34,6 +35,7 @@ BatchStatusHandler::BatchStatusHandler(bool showProgress, autoRetryCount_(autoRetryCount), autoRetryDelay_(autoRetryDelay), soundFileSyncComplete_(soundFileSyncComplete), + soundFileAlertPending_(soundFileAlertPending), progressDlg_(SyncProgressDialog::create(progressDlgSize, dlgMaximize, [this] { userRequestAbort(); }, *this, nullptr /*parentWindow*/, showProgress, autoCloseDialog, {jobName}, startTime, ignoreErrors, autoRetryCount, [&] { @@ -195,7 +197,7 @@ BatchStatusHandler::Result BatchStatusHandler::reportResults(const Zstring& post if (!autoClose) //only play when showing results dialog if (!soundFileSyncComplete_.empty()) { - //wxWidgets shows modal error dialog by default => NO! + //wxWidgets shows modal error dialog by default => "no, wxWidgets, NO!" wxLog* oldLogTarget = wxLog::SetActiveTarget(new wxLogStderr); //transfer and receive ownership! ZEN_ON_SCOPE_EXIT(delete wxLog::SetActiveTarget(oldLogTarget)); @@ -285,6 +287,7 @@ void BatchStatusHandler::reportWarning(const std::wstring& msg, bool& warningAct bool dontWarnAgain = false; switch (showQuestionDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::warning, PopupDialogCfg().setDetailInstructions(msg + L"\n\n" + _("You can switch to FreeFileSync's main window to resolve this issue.")). + remindWhenPending(soundFileAlertPending_). setCheckBox(dontWarnAgain, _("&Don't show this warning again"), static_cast(QuestionButton2::no)), _("&Ignore"), _("&Switch"))) { @@ -339,7 +342,8 @@ ProcessCallback::Response BatchStatusHandler::reportError(const ErrorInfo& error forceUiUpdateNoThrow(); //noexcept! => don't throw here when error occurs during clean up! switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::error, - PopupDialogCfg().setDetailInstructions(errorInfo.msg), + PopupDialogCfg().setDetailInstructions(errorInfo.msg). + remindWhenPending(soundFileAlertPending_), _("&Ignore"), _("Ignore &all"), _("&Retry"))) { case ConfirmationButton3::accept: //ignore @@ -388,8 +392,8 @@ void BatchStatusHandler::reportFatalError(const std::wstring& msg) forceUiUpdateNoThrow(); //noexcept! => don't throw here when error occurs during clean up! switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::error, - PopupDialogCfg().setTitle(_("Error")). - setDetailInstructions(msg), + PopupDialogCfg().setDetailInstructions(msg). + remindWhenPending(soundFileAlertPending_), _("&Ignore"), _("Ignore &all"))) { case ConfirmationButton2::accept: //ignore diff --git a/FreeFileSync/Source/ui/batch_status_handler.h b/FreeFileSync/Source/ui/batch_status_handler.h index 91c1d78d..80a5baf4 100644 --- a/FreeFileSync/Source/ui/batch_status_handler.h +++ b/FreeFileSync/Source/ui/batch_status_handler.h @@ -27,6 +27,7 @@ public: size_t autoRetryCount, std::chrono::seconds autoRetryDelay, const Zstring& soundFileSyncComplete, + const Zstring& soundFileAlertPending, wxSize progressDlgSize, bool dlgMaximize, bool autoCloseDialog, PostSyncAction postSyncAction, @@ -67,6 +68,7 @@ private: const size_t autoRetryCount_; const std::chrono::seconds autoRetryDelay_; const Zstring soundFileSyncComplete_; + const Zstring soundFileAlertPending_; SyncProgressDialog* progressDlg_; //managed to have the same lifetime as this handler! zen::ErrorLog errorLog_; //list of non-resolved errors and warnings diff --git a/FreeFileSync/Source/ui/cfg_grid.cpp b/FreeFileSync/Source/ui/cfg_grid.cpp index 2f1f1776..b9f0a2f2 100644 --- a/FreeFileSync/Source/ui/cfg_grid.cpp +++ b/FreeFileSync/Source/ui/cfg_grid.cpp @@ -572,7 +572,7 @@ private: if (!item->isLastRunCfg && !AFS::isNullPath(item->cfgItem.logFilePath)) - return getSyncResultLabel(item->cfgItem.logResult) + SPACED_DASH + AFS::getDisplayPath(item->cfgItem.logFilePath); + return getSyncResultLabel(item->cfgItem.logResult) + L"\n" + AFS::getDisplayPath(item->cfgItem.logFilePath); break; } return std::wstring(); diff --git a/FreeFileSync/Source/ui/file_grid.cpp b/FreeFileSync/Source/ui/file_grid.cpp index 38d2518d..01a30d48 100644 --- a/FreeFileSync/Source/ui/file_grid.cpp +++ b/FreeFileSync/Source/ui/file_grid.cpp @@ -483,7 +483,7 @@ private: break; case ColumnTypeRim::size: - visitFSObject(*fsObj, [&](const FolderPair& folder) { value = L'<' + _("Folder") + L'>'; }, + visitFSObject(*fsObj, [&](const FolderPair& folder) { /*value = L'<' + _("Folder") + L'>'; -> redundant!? */ }, [&](const FilePair& file) { value = formatNumber(file.getFileSize()); }, //[&](const FilePair& file) { value = numberTo(file.getFilePrint()); }, // -> test file id [&](const SymlinkPair& symlink) { value = L'<' + _("Symlink") + L'>'; }); diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp index 19160a24..9827af12 100644 --- a/FreeFileSync/Source/ui/gui_generated.cpp +++ b/FreeFileSync/Source/ui/gui_generated.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Oct 26 2018) +// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -3879,12 +3879,12 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - m_checkBoxAutoClose = new wxCheckBox( this, wxID_ANY, _("Auto-close"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizerStdButtons->Add( m_checkBoxAutoClose, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - bSizerStdButtons->Add( 0, 0, 1, wxEXPAND, 5 ); + m_checkBoxAutoClose = new wxCheckBox( this, wxID_ANY, _("Auto-close"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerStdButtons->Add( m_checkBoxAutoClose, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); + m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonClose->SetDefault(); @@ -4672,6 +4672,32 @@ OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const ffgSizer11->Add( bSizer2901, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + m_bitmapAlertPending = new wxStaticBitmap( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + ffgSizer11->Add( m_bitmapAlertPending, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText17111 = new wxStaticText( m_panel39, wxID_ANY, _("Alert pending:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText17111->Wrap( -1 ); + m_staticText17111->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) ); + + ffgSizer11->Add( m_staticText17111, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + wxBoxSizer* bSizer29011; + bSizer29011 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonPlayAlertPending = new wxBitmapButton( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 ); + bSizer29011->Add( m_bpButtonPlayAlertPending, 0, wxEXPAND, 5 ); + + m_textCtrlSoundPathAlertPending = new wxTextCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer29011->Add( m_textCtrlSoundPathAlertPending, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_buttonSelectSoundAlertPending = new wxButton( m_panel39, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonSelectSoundAlertPending->SetToolTip( _("Select a folder") ); + + bSizer29011->Add( m_buttonSelectSoundAlertPending, 0, wxEXPAND, 5 ); + + + ffgSizer11->Add( bSizer29011, 1, wxEXPAND, 5 ); + bSizer301->Add( ffgSizer11, 1, wxALL, 5 ); @@ -4819,9 +4845,9 @@ OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const // Columns m_gridCustomCommand->EnableDragColMove( false ); m_gridCustomCommand->EnableDragColSize( true ); - m_gridCustomCommand->SetColLabelSize( -1 ); m_gridCustomCommand->SetColLabelValue( 0, _("Description") ); m_gridCustomCommand->SetColLabelValue( 1, _("Command line") ); + m_gridCustomCommand->SetColLabelSize( -1 ); m_gridCustomCommand->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); // Rows @@ -4886,6 +4912,9 @@ OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const m_bpButtonPlaySyncDone->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::onPlaySyncDone ), NULL, this ); m_textCtrlSoundPathSyncDone->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( OptionsDlgGenerated::onChangeSoundFilePath ), NULL, this ); m_buttonSelectSoundSyncDone->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::onSelectSoundSyncDone ), NULL, this ); + m_bpButtonPlayAlertPending->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::onPlayAlertPending ), NULL, this ); + m_textCtrlSoundPathAlertPending->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( OptionsDlgGenerated::onChangeSoundFilePath ), NULL, this ); + m_buttonSelectSoundAlertPending->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::onSelectSoundAlertPending ), NULL, this ); m_bpButtonAddRow->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::onAddRow ), NULL, this ); m_bpButtonRemoveRow->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::onRemoveRow ), NULL, this ); m_buttonDefault->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( OptionsDlgGenerated::onDefault ), NULL, this ); @@ -5635,24 +5664,18 @@ WarnAccessRightsMissingDlgGenerated::WarnAccessRightsMissingDlgGenerated( wxWind m_staticline36 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bSizer95->Add( m_staticline36, 0, wxEXPAND, 5 ); - wxBoxSizer* bSizer25; - bSizer25 = new wxBoxSizer( wxVERTICAL ); - m_checkBoxDontShowAgain = new wxCheckBox( this, wxID_ANY, _("&Don't show this dialog again"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer25->Add( m_checkBoxDontShowAgain, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); + bSizer95->Add( m_checkBoxDontShowAgain, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonClose->SetDefault(); - bSizerStdButtons->Add( m_buttonClose, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizer25->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + bSizerStdButtons->Add( m_buttonClose, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - bSizer95->Add( bSizer25, 0, wxEXPAND, 5 ); + bSizer95->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); bSizer330->Add( bSizer95, 1, wxEXPAND, 5 ); diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h index e06c4489..35a19b5c 100644 --- a/FreeFileSync/Source/ui/gui_generated.h +++ b/FreeFileSync/Source/ui/gui_generated.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Oct 26 2018) +// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -10,6 +10,8 @@ #include #include #include +namespace zen { class BitmapTextButton; } + #include "wx+/bitmap_button.h" #include "folder_history_box.h" #include "wx+/grid.h" @@ -210,7 +212,7 @@ protected: wxStaticBitmap* m_bitmapDeleteRight; wxStaticText* m_staticTextDeleteRight; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override them in your derived class virtual void onClose( wxCloseEvent& event ) { event.Skip(); } virtual void onConfigNew( wxCommandEvent& event ) { event.Skip(); } virtual void onConfigLoad( wxCommandEvent& event ) { event.Skip(); } @@ -312,6 +314,7 @@ public: wxBitmapButton* m_bpButtonSelectAltFolderRight; FolderPairPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 698, 67 ), long style = 0, const wxString& name = wxEmptyString ); + ~FolderPairPanelGenerated(); }; @@ -488,7 +491,7 @@ protected: wxButton* m_buttonOkay; wxButton* m_buttonCancel; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override them in your derived class virtual void onClose( wxCloseEvent& event ) { event.Skip(); } virtual void onListBoxKeyEvent( wxKeyEvent& event ) { event.Skip(); } virtual void onSelectFolderPair( wxCommandEvent& event ) { event.Skip(); } @@ -546,6 +549,7 @@ public: wxChoice* m_choicePostSyncCondition; ConfigDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Synchronization Settings"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); + ~ConfigDlgGenerated(); }; @@ -638,7 +642,7 @@ protected: wxButton* m_buttonOkay; wxButton* m_buttonCancel; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override them in your derived class virtual void onClose( wxCloseEvent& event ) { event.Skip(); } virtual void onConnectionGdrive( wxCommandEvent& event ) { event.Skip(); } virtual void onConnectionSftp( wxCommandEvent& event ) { event.Skip(); } @@ -660,6 +664,7 @@ protected: public: CloudSetupDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Access Online Storage"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~CloudSetupDlgGenerated(); }; @@ -680,7 +685,7 @@ protected: wxButton* m_buttonOkay; wxButton* m_buttonCancel; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override them in your derived class virtual void onClose( wxCloseEvent& event ) { event.Skip(); } virtual void onExpandNode( wxTreeEvent& event ) { event.Skip(); } virtual void onOkay( wxCommandEvent& event ) { event.Skip(); } @@ -690,6 +695,7 @@ protected: public: AbstractFolderPickerGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Select a folder"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); + ~AbstractFolderPickerGenerated(); }; @@ -733,7 +739,7 @@ protected: wxButton* m_buttonStartSync; wxButton* m_buttonCancel; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override them in your derived class virtual void onClose( wxCloseEvent& event ) { event.Skip(); } virtual void onStartSync( wxCommandEvent& event ) { event.Skip(); } virtual void onCancel( wxCommandEvent& event ) { event.Skip(); } @@ -742,6 +748,7 @@ protected: public: SyncConfirmationDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~SyncConfirmationDlgGenerated(); }; @@ -784,6 +791,7 @@ public: wxStaticBitmap* m_bitmapIgnoreErrors; CompareProgressDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxBORDER_RAISED, const wxString& name = wxEmptyString ); + ~CompareProgressDlgGenerated(); }; @@ -846,6 +854,7 @@ public: wxButton* m_buttonStop; SyncProgressPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString ); + ~SyncProgressPanelGenerated(); }; @@ -863,7 +872,7 @@ protected: zen::ToggleButton* m_bpButtonInfo; wxStaticLine* m_staticline13; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override them in your derived class virtual void onErrors( wxCommandEvent& event ) { event.Skip(); } virtual void onWarnings( wxCommandEvent& event ) { event.Skip(); } virtual void onInfo( wxCommandEvent& event ) { event.Skip(); } @@ -873,6 +882,7 @@ public: zen::Grid* m_gridMessages; LogPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString ); + ~LogPanelGenerated(); }; @@ -908,7 +918,7 @@ protected: wxButton* m_buttonSaveAs; wxButton* m_buttonCancel; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override them in your derived class virtual void onClose( wxCloseEvent& event ) { event.Skip(); } virtual void onToggleRunMinimized( wxCommandEvent& event ) { event.Skip(); } virtual void onToggleIgnoreErrors( wxCommandEvent& event ) { event.Skip(); } @@ -921,6 +931,7 @@ public: wxChoice* m_choicePostSyncAction; BatchDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Save as a Batch Job"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~BatchDlgGenerated(); }; @@ -945,7 +956,7 @@ protected: wxButton* m_buttonOK; wxButton* m_buttonCancel; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override them in your derived class virtual void onClose( wxCloseEvent& event ) { event.Skip(); } virtual void onUseRecycler( wxCommandEvent& event ) { event.Skip(); } virtual void onOkay( wxCommandEvent& event ) { event.Skip(); } @@ -955,6 +966,7 @@ protected: public: DeleteDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Delete Items"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); + ~DeleteDlgGenerated(); }; @@ -981,7 +993,7 @@ protected: wxButton* m_buttonOK; wxButton* m_buttonCancel; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override them in your derived class virtual void onClose( wxCloseEvent& event ) { event.Skip(); } virtual void onOkay( wxCommandEvent& event ) { event.Skip(); } virtual void onCancel( wxCommandEvent& event ) { event.Skip(); } @@ -992,6 +1004,7 @@ public: wxBitmapButton* m_bpButtonSelectAltTargetFolder; CopyToDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Copy Items"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); + ~CopyToDlgGenerated(); }; @@ -1052,6 +1065,11 @@ protected: wxBitmapButton* m_bpButtonPlaySyncDone; wxTextCtrl* m_textCtrlSoundPathSyncDone; wxButton* m_buttonSelectSoundSyncDone; + wxStaticBitmap* m_bitmapAlertPending; + wxStaticText* m_staticText17111; + wxBitmapButton* m_bpButtonPlayAlertPending; + wxTextCtrl* m_textCtrlSoundPathAlertPending; + wxButton* m_buttonSelectSoundAlertPending; wxStaticLine* m_staticline3611; wxStaticBitmap* m_bitmapConsole; wxStaticText* m_staticText85; @@ -1073,7 +1091,7 @@ protected: wxButton* m_buttonOkay; wxButton* m_buttonCancel; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override them in your derived class virtual void onClose( wxCloseEvent& event ) { event.Skip(); } virtual void onRestoreDialogs( wxCommandEvent& event ) { event.Skip(); } virtual void onShowLogFolder( wxHyperlinkEvent& event ) { event.Skip(); } @@ -1083,6 +1101,8 @@ protected: virtual void onSelectSoundCompareDone( wxCommandEvent& event ) { event.Skip(); } virtual void onPlaySyncDone( wxCommandEvent& event ) { event.Skip(); } virtual void onSelectSoundSyncDone( wxCommandEvent& event ) { event.Skip(); } + virtual void onPlayAlertPending( wxCommandEvent& event ) { event.Skip(); } + virtual void onSelectSoundAlertPending( wxCommandEvent& event ) { event.Skip(); } virtual void onAddRow( wxCommandEvent& event ) { event.Skip(); } virtual void onRemoveRow( wxCommandEvent& event ) { event.Skip(); } virtual void onDefault( wxCommandEvent& event ) { event.Skip(); } @@ -1093,6 +1113,7 @@ protected: public: OptionsDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Options"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~OptionsDlgGenerated(); }; @@ -1111,6 +1132,7 @@ public: wxStaticText* m_staticTextMain; 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(); }; @@ -1131,7 +1153,7 @@ protected: wxButton* m_buttonOkay; wxButton* m_buttonCancel; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override them in your derived class virtual void onClose( wxCloseEvent& event ) { event.Skip(); } virtual void onChangeSelectionFrom( wxCalendarEvent& event ) { event.Skip(); } virtual void onChangeSelectionTo( wxCalendarEvent& event ) { event.Skip(); } @@ -1142,6 +1164,7 @@ protected: public: SelectTimespanDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Select Time Span"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~SelectTimespanDlgGenerated(); }; @@ -1190,7 +1213,7 @@ protected: wxBoxSizer* bSizerStdButtons; wxButton* m_buttonClose; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override them in your derived class virtual void onClose( wxCloseEvent& event ) { event.Skip(); } virtual void onDonate( wxCommandEvent& event ) { event.Skip(); } virtual void onShowDonationDetails( wxCommandEvent& event ) { event.Skip(); } @@ -1204,6 +1227,7 @@ protected: public: AboutDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("About"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~AboutDlgGenerated(); }; @@ -1224,13 +1248,14 @@ protected: wxBoxSizer* bSizerStdButtons; wxButton* m_buttonCancel; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override 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(); }; @@ -1268,7 +1293,7 @@ protected: wxBoxSizer* bSizerStdButtons; wxButton* m_buttonCancel; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override 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(); } @@ -1280,6 +1305,7 @@ protected: public: ActivationDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~ActivationDlgGenerated(); }; @@ -1300,7 +1326,7 @@ protected: wxButton* m_buttonOkay; wxButton* m_buttonCancel; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override them in your derived class virtual void onClose( wxCloseEvent& event ) { event.Skip(); } virtual void onOkay( wxCommandEvent& event ) { event.Skip(); } virtual void onCancel( wxCommandEvent& event ) { event.Skip(); } @@ -1309,6 +1335,7 @@ protected: public: CfgHighlightDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Highlight Configurations"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~CfgHighlightDlgGenerated(); }; @@ -1339,7 +1366,7 @@ protected: wxBoxSizer* bSizerStdButtons; wxButton* m_buttonClose; - // Virtual event handlers, overide them in your derived class + // Virtual event handlers, override them in your derived class virtual void onClose( wxCloseEvent& event ) { event.Skip(); } virtual void onShowAppBundle( wxCommandEvent& event ) { event.Skip(); } virtual void onOpenSecuritySettings( wxCommandEvent& event ) { event.Skip(); } @@ -1350,6 +1377,7 @@ protected: public: WarnAccessRightsMissingDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Grant Full Disk Access"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE ); + ~WarnAccessRightsMissingDlgGenerated(); }; diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp index 861ea810..71f201f1 100644 --- a/FreeFileSync/Source/ui/gui_status_handler.cpp +++ b/FreeFileSync/Source/ui/gui_status_handler.cpp @@ -29,11 +29,13 @@ StatusHandlerTemporaryPanel::StatusHandlerTemporaryPanel(MainDialog& dlg, const std::chrono::system_clock::time_point& startTime, bool ignoreErrors, size_t autoRetryCount, - std::chrono::seconds autoRetryDelay) : + std::chrono::seconds autoRetryDelay, + const Zstring& soundFileAlertPending) : mainDlg_(dlg), ignoreErrors_(ignoreErrors), autoRetryCount_(autoRetryCount), autoRetryDelay_(autoRetryDelay), + soundFileAlertPending_(soundFileAlertPending), startTime_(startTime) { mainDlg_.compareStatus_->init(*this, ignoreErrors_, autoRetryCount_); //clear old values before showing panel @@ -210,6 +212,7 @@ void StatusHandlerTemporaryPanel::reportWarning(const std::wstring& msg, bool& w bool dontWarnAgain = false; switch (showConfirmationDialog(&mainDlg_, DialogInfoType::warning, PopupDialogCfg().setDetailInstructions(msg). + remindWhenPending(soundFileAlertPending_). setCheckBox(dontWarnAgain, _("&Don't show this warning again")), _("&Ignore"))) { @@ -249,7 +252,8 @@ ProcessCallback::Response StatusHandlerTemporaryPanel::reportError(const ErrorIn forceUiUpdateNoThrow(); //noexcept! => don't throw here when error occurs during clean up! switch (showConfirmationDialog(&mainDlg_, DialogInfoType::error, - PopupDialogCfg().setDetailInstructions(errorInfo.msg), + PopupDialogCfg().setDetailInstructions(errorInfo.msg). + remindWhenPending(soundFileAlertPending_), _("&Ignore"), _("Ignore &all"), _("&Retry"))) { case ConfirmationButton3::accept: //ignore @@ -289,8 +293,8 @@ void StatusHandlerTemporaryPanel::reportFatalError(const std::wstring& msg) forceUiUpdateNoThrow(); //noexcept! => don't throw here when error occurs during clean up! switch (showConfirmationDialog(&mainDlg_, DialogInfoType::error, - PopupDialogCfg().setTitle(_("Error")). - setDetailInstructions(msg), + PopupDialogCfg().setDetailInstructions(msg). + remindWhenPending(soundFileAlertPending_), _("&Ignore"), _("Ignore &all"))) { case ConfirmationButton2::accept: //ignore @@ -345,6 +349,7 @@ StatusHandlerFloatingDialog::StatusHandlerFloatingDialog(wxFrame* parentDlg, size_t autoRetryCount, std::chrono::seconds autoRetryDelay, const Zstring& soundFileSyncComplete, + const Zstring& soundFileAlertPending, const wxSize& progressDlgSize, bool dlgMaximize, bool autoCloseDialog) : jobNames_(jobNames), @@ -352,6 +357,7 @@ StatusHandlerFloatingDialog::StatusHandlerFloatingDialog(wxFrame* parentDlg, autoRetryCount_(autoRetryCount), autoRetryDelay_(autoRetryDelay), soundFileSyncComplete_(soundFileSyncComplete), + soundFileAlertPending_(soundFileAlertPending), progressDlg_(SyncProgressDialog::create(progressDlgSize, dlgMaximize, [this] { userRequestAbort(); }, *this, parentDlg, true /*showProgress*/, autoCloseDialog, jobNames, startTime, ignoreErrors, autoRetryCount, PostSyncAction2::none)) {} @@ -497,7 +503,7 @@ StatusHandlerFloatingDialog::Result StatusHandlerFloatingDialog::reportResults(c if (!autoClose) //only play when showing results dialog if (!soundFileSyncComplete_.empty()) { - //wxWidgets shows modal error dialog by default => NO! + //wxWidgets shows modal error dialog by default => "no, wxWidgets, NO!" wxLog* oldLogTarget = wxLog::SetActiveTarget(new wxLogStderr); //transfer and receive ownership! ZEN_ON_SCOPE_EXIT(delete wxLog::SetActiveTarget(oldLogTarget)); @@ -570,6 +576,7 @@ void StatusHandlerFloatingDialog::reportWarning(const std::wstring& msg, bool& w bool dontWarnAgain = false; switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::warning, PopupDialogCfg().setDetailInstructions(msg). + remindWhenPending(soundFileAlertPending_). setCheckBox(dontWarnAgain, _("&Don't show this warning again")), _("&Ignore"))) { @@ -609,7 +616,8 @@ ProcessCallback::Response StatusHandlerFloatingDialog::reportError(const ErrorIn forceUiUpdateNoThrow(); //noexcept! => don't throw here when error occurs during clean up! switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::error, - PopupDialogCfg().setDetailInstructions(errorInfo.msg), + PopupDialogCfg().setDetailInstructions(errorInfo.msg). + remindWhenPending(soundFileAlertPending_), _("&Ignore"), _("Ignore &all"), _("&Retry"))) { case ConfirmationButton3::accept: //ignore @@ -649,8 +657,8 @@ void StatusHandlerFloatingDialog::reportFatalError(const std::wstring& msg) forceUiUpdateNoThrow(); //noexcept! => don't throw here when error occurs during clean up! switch (showConfirmationDialog(progressDlg_->getWindowIfVisible(), DialogInfoType::error, - PopupDialogCfg().setTitle(_("Error")). - setDetailInstructions(msg), + PopupDialogCfg().setDetailInstructions(msg). + remindWhenPending(soundFileAlertPending_), _("&Ignore"), _("Ignore &all"))) { case ConfirmationButton2::accept: //ignore diff --git a/FreeFileSync/Source/ui/gui_status_handler.h b/FreeFileSync/Source/ui/gui_status_handler.h index 8f74e77d..3fe20e97 100644 --- a/FreeFileSync/Source/ui/gui_status_handler.h +++ b/FreeFileSync/Source/ui/gui_status_handler.h @@ -22,7 +22,12 @@ namespace fff class StatusHandlerTemporaryPanel : private wxEvtHandler, public StatusHandler { public: - StatusHandlerTemporaryPanel(MainDialog& dlg, const std::chrono::system_clock::time_point& startTime, bool ignoreErrors, size_t autoRetryCount, std::chrono::seconds autoRetryDelay); + StatusHandlerTemporaryPanel(MainDialog& dlg, + const std::chrono::system_clock::time_point& startTime, + bool ignoreErrors, + size_t autoRetryCount, + std::chrono::seconds autoRetryDelay, + const Zstring& soundFileAlertPending); ~StatusHandlerTemporaryPanel(); void initNewPhase (int itemsTotal, int64_t bytesTotal, ProcessPhase phaseID) override; // @@ -50,6 +55,8 @@ private: const bool ignoreErrors_; const size_t autoRetryCount_; const std::chrono::seconds autoRetryDelay_; + const Zstring soundFileAlertPending_; + const std::chrono::system_clock::time_point startTime_; const std::chrono::steady_clock::time_point startTimeSteady_ = std::chrono::steady_clock::now(); }; @@ -66,6 +73,7 @@ public: size_t autoRetryCount, std::chrono::seconds autoRetryDelay, const Zstring& soundFileSyncComplete, + const Zstring& soundFileAlertPending, const wxSize& progressDlgSize, bool dlgMaximize, bool autoCloseDialog); //noexcept! ~StatusHandlerFloatingDialog(); @@ -105,6 +113,7 @@ private: const size_t autoRetryCount_; const std::chrono::seconds autoRetryDelay_; const Zstring soundFileSyncComplete_; + const Zstring soundFileAlertPending_; SyncProgressDialog* progressDlg_; //managed to have the same lifetime as this handler! zen::ErrorLog errorLog_; diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp index 550a8557..ffbb35c5 100644 --- a/FreeFileSync/Source/ui/main_dlg.cpp +++ b/FreeFileSync/Source/ui/main_dlg.cpp @@ -7,6 +7,7 @@ #include "main_dlg.h" #include #include +#include #include #include #include @@ -1422,7 +1423,8 @@ void MainDialog::copyToAlternateFolder(const std::vector& sel StatusHandlerTemporaryPanel statusHandler(*this, std::chrono::system_clock::now() /*startTime*/, false /*ignoreErrors*/, guiCfg.mainCfg.autoRetryCount, - guiCfg.mainCfg.autoRetryDelay); //handle status display and error messages + guiCfg.mainCfg.autoRetryDelay, + globalCfg_.soundFileAlertPending); try { fff::copyToAlternateFolder(selectionL, selectionR, @@ -1468,7 +1470,8 @@ void MainDialog::deleteSelectedFiles(const std::vector& selec StatusHandlerTemporaryPanel statusHandler(*this, std::chrono::system_clock::now() /*startTime*/, false /*ignoreErrors*/, guiCfg.mainCfg.autoRetryCount, - guiCfg.mainCfg.autoRetryDelay); //handle status display and error messages + guiCfg.mainCfg.autoRetryDelay, + globalCfg_.soundFileAlertPending); try { deleteFromGridAndHD(selectionL, selectionR, @@ -1710,7 +1713,8 @@ void MainDialog::openExternalApplication(const Zstring& commandLinePhrase, bool StatusHandlerTemporaryPanel statusHandler(*this, std::chrono::system_clock::now() /*startTime*/, false /*ignoreErrors*/, guiCfg.mainCfg.autoRetryCount, - guiCfg.mainCfg.autoRetryDelay); //handle status display and error messages + guiCfg.mainCfg.autoRetryDelay, + globalCfg_.soundFileAlertPending); try { tempFileBuf_.createTempFiles(nonNativeFiles, statusHandler); //throw AbortProcess @@ -2996,6 +3000,10 @@ void MainDialog::updateUnsavedCfgStatus() } SetTitle(title); + + //macOS-only: + OSXSetModified(haveUnsavedCfg); + SetRepresentedFilename(utfTo(activeCfgFilePath)); } @@ -4065,7 +4073,8 @@ void MainDialog::onCompare(wxCommandEvent& event) StatusHandlerTemporaryPanel statusHandler(*this, startTime, guiCfg.mainCfg.ignoreErrors, guiCfg.mainCfg.autoRetryCount, - guiCfg.mainCfg.autoRetryDelay); + guiCfg.mainCfg.autoRetryDelay, + globalCfg_.soundFileAlertPending); try { //GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization @@ -4105,7 +4114,7 @@ void MainDialog::onCompare(wxCommandEvent& event) //play (optional) sound notification if (!globalCfg_.soundFileCompareFinished.empty()) { - //wxWidgets shows modal error dialog by default => NO! + //wxWidgets shows modal error dialog by default => "no, wxWidgets, NO!" wxLog* oldLogTarget = wxLog::SetActiveTarget(new wxLogStderr); //transfer and receive ownership! ZEN_ON_SCOPE_EXIT(delete wxLog::SetActiveTarget(oldLogTarget)); @@ -4113,7 +4122,7 @@ void MainDialog::onCompare(wxCommandEvent& event) } if (!IsActive()) - RequestUserAttention(); + RequestUserAttention(); //this == toplevel win, so we also get the taskbar flash! //remember folder history (except when cancelled by user) for (const FolderPairCfg& fpCfg : fpCfgList) @@ -4306,6 +4315,7 @@ void MainDialog::onStartSync(wxCommandEvent& event) guiCfg.mainCfg.autoRetryCount, guiCfg.mainCfg.autoRetryDelay, globalCfg_.soundFileSyncFinished, + globalCfg_.soundFileAlertPending, globalCfg_.dpiLayouts[getDpiScalePercent()].progressDlg.dlgSize, globalCfg_.dpiLayouts[getDpiScalePercent()].progressDlg.isMaximized, globalCfg_.progressDlgAutoClose); @@ -4518,7 +4528,8 @@ void MainDialog::startSyncForSelecction(const std::vector& se StatusHandlerTemporaryPanel statusHandler(*this, syncStartTime, guiCfg.mainCfg.ignoreErrors, guiCfg.mainCfg.autoRetryCount, - guiCfg.mainCfg.autoRetryDelay); //handle status display and error messages + guiCfg.mainCfg.autoRetryDelay, + globalCfg_.soundFileAlertPending); try { //let's report here rather than before comparison (user might have changed global settings in the meantime!) @@ -4732,6 +4743,7 @@ void MainDialog::onGridLabelLeftClickC(GridLabelClickEvent& event) } } + void MainDialog::onSwapSides(wxCommandEvent& event) { if (globalCfg_.confirmDlgs.confirmSwapSides) @@ -4800,7 +4812,8 @@ void MainDialog::onSwapSides(wxCommandEvent& event) StatusHandlerTemporaryPanel statusHandler(*this, std::chrono::system_clock::now() /*startTime*/, false /*ignoreErrors*/, guiCfg.mainCfg.autoRetryCount, - guiCfg.mainCfg.autoRetryDelay); //handle status display and error messages + guiCfg.mainCfg.autoRetryDelay, + Zstr("") /*soundFileAlertPending*/); try { statusHandler.initNewPhase(-1, -1, ProcessPhase::none); @@ -5048,7 +5061,8 @@ void MainDialog::applySyncDirections() StatusHandlerTemporaryPanel statusHandler(*this, std::chrono::system_clock::now() /*startTime*/, false /*ignoreErrors*/, guiCfg.mainCfg.autoRetryCount, - guiCfg.mainCfg.autoRetryDelay); //handle status display and error messages + guiCfg.mainCfg.autoRetryDelay, + Zstr("") /*soundFileAlertPending*/); try { statusHandler.initNewPhase(-1, -1, ProcessPhase::none); diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp index 8ce5a325..52ab1c0f 100644 --- a/FreeFileSync/Source/ui/small_dlgs.cpp +++ b/FreeFileSync/Source/ui/small_dlgs.cpp @@ -7,6 +7,7 @@ #include "small_dlgs.h" #include #include +#include #include #include #include @@ -1137,14 +1138,16 @@ private: void onShowLogFolder (wxHyperlinkEvent& event) override; void onToggleLogfilesLimit(wxCommandEvent& event) override { updateGui(); } - void onSelectSoundCompareDone(wxCommandEvent& event) override { selectSound(*m_textCtrlSoundPathCompareDone); } - void onSelectSoundSyncDone (wxCommandEvent& event) override { selectSound(*m_textCtrlSoundPathSyncDone); } + void onSelectSoundCompareDone (wxCommandEvent& event) override { selectSound(*m_textCtrlSoundPathCompareDone); } + void onSelectSoundSyncDone (wxCommandEvent& event) override { selectSound(*m_textCtrlSoundPathSyncDone); } + void onSelectSoundAlertPending(wxCommandEvent& event) override { selectSound(*m_textCtrlSoundPathAlertPending); } void selectSound(wxTextCtrl& txtCtrl); void onChangeSoundFilePath(wxCommandEvent& event) override { updateGui(); } - void onPlayCompareDone(wxCommandEvent& event) override { playSoundWithDiagnostics(trimCpy(m_textCtrlSoundPathCompareDone->GetValue())); } - void onPlaySyncDone (wxCommandEvent& event) override { playSoundWithDiagnostics(trimCpy(m_textCtrlSoundPathSyncDone ->GetValue())); } + void onPlayCompareDone (wxCommandEvent& event) override { playSoundWithDiagnostics(trimCpy(m_textCtrlSoundPathCompareDone ->GetValue())); } + void onPlaySyncDone (wxCommandEvent& event) override { playSoundWithDiagnostics(trimCpy(m_textCtrlSoundPathSyncDone ->GetValue())); } + void onPlayAlertPending(wxCommandEvent& event) override { playSoundWithDiagnostics(trimCpy(m_textCtrlSoundPathAlertPending->GetValue())); } void playSoundWithDiagnostics(const wxString& filePath); void onResize(wxSizeEvent& event); @@ -1193,8 +1196,10 @@ OptionsDlg::OptionsDlg(wxWindow* parent, XmlGlobalSettings& globalSettings) : m_bitmapConsole ->SetBitmap (loadImage("command_line", fastFromDIP(20))); m_bitmapCompareDone ->SetBitmap (loadImage("compare_sicon")); m_bitmapSyncDone ->SetBitmap (loadImage("start_sync_sicon")); + m_bitmapAlertPending ->SetBitmap (loadImage("msg_error", loadImage("compare_sicon").GetSize().x)); m_bpButtonPlayCompareDone ->SetBitmapLabel(loadImage("play_sound")); m_bpButtonPlaySyncDone ->SetBitmapLabel(loadImage("play_sound")); + m_bpButtonPlayAlertPending->SetBitmapLabel(loadImage("play_sound")); m_bpButtonAddRow ->SetBitmapLabel(loadImage("item_add")); m_bpButtonRemoveRow ->SetBitmapLabel(loadImage("item_remove")); @@ -1222,8 +1227,9 @@ OptionsDlg::OptionsDlg(wxWindow* parent, XmlGlobalSettings& globalSettings) : break; } - m_textCtrlSoundPathCompareDone->ChangeValue(utfTo(globalSettings.soundFileCompareFinished)); - m_textCtrlSoundPathSyncDone ->ChangeValue(utfTo(globalSettings.soundFileSyncFinished)); + m_textCtrlSoundPathCompareDone ->ChangeValue(utfTo(globalSettings.soundFileCompareFinished)); + m_textCtrlSoundPathSyncDone ->ChangeValue(utfTo(globalSettings.soundFileSyncFinished)); + m_textCtrlSoundPathAlertPending->ChangeValue(utfTo(globalSettings.soundFileAlertPending)); //-------------------------------------------------------------------------------- bSizerLockedFiles->Show(false); @@ -1284,8 +1290,9 @@ void OptionsDlg::updateGui() m_spinCtrlLogFilesMaxAge->Enable(m_checkBoxLogFilesMaxAge->GetValue()); - m_bpButtonPlayCompareDone->Enable(!trimCpy(m_textCtrlSoundPathCompareDone->GetValue()).empty()); - m_bpButtonPlaySyncDone ->Enable(!trimCpy(m_textCtrlSoundPathSyncDone ->GetValue()).empty()); + m_bpButtonPlayCompareDone ->Enable(!trimCpy(m_textCtrlSoundPathCompareDone ->GetValue()).empty()); + m_bpButtonPlaySyncDone ->Enable(!trimCpy(m_textCtrlSoundPathSyncDone ->GetValue()).empty()); + m_bpButtonPlayAlertPending->Enable(!trimCpy(m_textCtrlSoundPathAlertPending->GetValue()).empty()); } @@ -1349,8 +1356,9 @@ void OptionsDlg::onDefault(wxCommandEvent& event) break; } - m_textCtrlSoundPathCompareDone->ChangeValue(utfTo(defaultCfg_.soundFileCompareFinished)); - m_textCtrlSoundPathSyncDone ->ChangeValue(utfTo(defaultCfg_.soundFileSyncFinished)); + m_textCtrlSoundPathCompareDone ->ChangeValue(utfTo(defaultCfg_.soundFileCompareFinished)); + m_textCtrlSoundPathSyncDone ->ChangeValue(utfTo(defaultCfg_.soundFileSyncFinished)); + m_textCtrlSoundPathAlertPending->ChangeValue(utfTo(defaultCfg_.soundFileAlertPending)); setExtApp(defaultCfg_.externalApps); @@ -1368,8 +1376,9 @@ void OptionsDlg::onOkay(wxCommandEvent& event) globalCfgOut_.logfilesMaxAgeDays = m_checkBoxLogFilesMaxAge->GetValue() ? m_spinCtrlLogFilesMaxAge->GetValue() : -1; globalCfgOut_.logFormat = m_radioBtnLogHtml->GetValue() ? LogFileFormat::html : LogFileFormat::text; - globalCfgOut_.soundFileCompareFinished = utfTo(trimCpy(m_textCtrlSoundPathCompareDone->GetValue())); - globalCfgOut_.soundFileSyncFinished = utfTo(trimCpy(m_textCtrlSoundPathSyncDone ->GetValue())); + globalCfgOut_.soundFileCompareFinished = utfTo(trimCpy(m_textCtrlSoundPathCompareDone ->GetValue())); + globalCfgOut_.soundFileSyncFinished = utfTo(trimCpy(m_textCtrlSoundPathSyncDone ->GetValue())); + globalCfgOut_.soundFileAlertPending = utfTo(trimCpy(m_textCtrlSoundPathAlertPending->GetValue())); globalCfgOut_.externalApps = getExtApp(); diff --git a/FreeFileSync/Source/ui/tree_grid.cpp b/FreeFileSync/Source/ui/tree_grid.cpp index 76d4c95b..13a96295 100644 --- a/FreeFileSync/Source/ui/tree_grid.cpp +++ b/FreeFileSync/Source/ui/tree_grid.cpp @@ -730,7 +730,7 @@ private: return dirRight; else if (dirRight.empty()) return dirLeft; - return dirLeft + L' ' + EN_DASH + L'\n' + dirRight; + return dirLeft + /*L' ' + EM_DASH + */ L'\n' + dirRight; } break; diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h index 6c2e91e9..4a4de811 100644 --- a/FreeFileSync/Source/version/version.h +++ b/FreeFileSync/Source/version/version.h @@ -3,7 +3,7 @@ namespace fff { -const char ffsVersion[] = "11.14"; //internal linkage! +const char ffsVersion[] = "11.15"; //internal linkage! const char FFS_VERSION_SEPARATOR = '.'; } diff --git a/License.txt b/License.txt index db3fc4ce..7c78f826 100644 --- a/License.txt +++ b/License.txt @@ -1,8 +1,10 @@ A. GNU GENERAL PUBLIC LICENSE -B. OpenSSL and SSLeay License -C. cURL License +B. OpenSSL License +C. curl License D. libssh2 License +==================================================================== + A. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 @@ -623,127 +625,192 @@ an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. - -B. OpenSSL and SSLeay License - -OpenSSL License - ==================================================================== -Copyright (c) 1998-2018 The OpenSSL Project. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - -3. All advertising materials mentioning features or use of this - software must display the following acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - -4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - endorse or promote products derived from this software without - prior written permission. For written permission, please contact - openssl-core@openssl.org. - -5. Products derived from this software may not be called "OpenSSL" - nor may "OpenSSL" appear in their names without prior written - permission of the OpenSSL Project. - -6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes software developed by the OpenSSL Project - for use in the OpenSSL Toolkit (http://www.openssl.org/)" - -THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY -EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR -ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================== - -This product includes cryptographic software written by Eric Young -(eay@cryptsoft.com). This product includes software written by Tim -Hudson (tjh@cryptsoft.com). -Original SSLeay License +B. OpenSSL License + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. -Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) -All rights reserved. +==================================================================== -This package is an SSL implementation written -by Eric Young (eay@cryptsoft.com). -The implementation was written so as to conform with Netscapes SSL. - -This library is free for commercial and non-commercial use as long as -the following conditions are aheared to. The following conditions -apply to all code found in this distribution, be it the RC4, RSA, -lhash, DES, etc., code; not just the SSL code. The SSL documentation -included with this distribution is covered by the same copyright terms -except that the holder is Tim Hudson (tjh@cryptsoft.com). - -Copyright remains Eric Young's, and as such any Copyright notices in -the code are not to be removed. -If this package is used in a product, Eric Young should be given attribution -as the author of the parts of the library used. -This can be in the form of a textual message at program startup or -in documentation (online or textual) provided with the package. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. All advertising materials mentioning features or use of this software - must display the following acknowledgement: - "This product includes cryptographic software written by - Eric Young (eay@cryptsoft.com)" - The word 'cryptographic' can be left out if the rouines from the library - being used are not cryptographic related :-). -4. If you include any Windows specific code (or a derivative thereof) from - the apps directory (application code) you must include an acknowledgement: - "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - -THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -SUCH DAMAGE. - -The licence and distribution terms for any publically available version or -derivative of this code cannot be changed. i.e. this code cannot simply be -copied and put under another distribution licence -[including the GNU Public Licence.] - - -C. cURL License +C. curl License COPYRIGHT AND PERMISSION NOTICE -Copyright (c) 1996 - 2018, Daniel Stenberg, , and many +Copyright (c) 1996 - 2021, Daniel Stenberg, daniel@haxx.se, and many contributors, see the THANKS file. All rights reserved. @@ -764,6 +831,7 @@ Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. +==================================================================== D. libssh2 License @@ -771,8 +839,10 @@ Copyright (c) 2004-2007 Sara Golemon Copyright (c) 2005,2006 Mikhail Gusarov Copyright (c) 2006-2007 The Written Word, Inc. Copyright (c) 2007 Eli Fant -Copyright (c) 2009-2014 Daniel Stenberg +Copyright (c) 2009-2021 Daniel Stenberg Copyright (C) 2008, 2009 Simon Josefsson +Copyright (c) 2000 Markus Friedl +Copyright (c) 2015 Microsoft Corp. All rights reserved. Redistribution and use in source and binary forms, diff --git a/wx+/popup_dlg.cpp b/wx+/popup_dlg.cpp index 88ed590b..3d4077c3 100644 --- a/wx+/popup_dlg.cpp +++ b/wx+/popup_dlg.cpp @@ -6,14 +6,17 @@ #include "popup_dlg.h" #include +#include #include #include +#include #include "no_flicker.h" #include "font_size.h" #include "image_resources.h" #include "popup_dlg_generated.h" #include "std_button_layout.h" #include "taskbar.h" + #include "window_tools.h" using namespace zen; @@ -194,6 +197,27 @@ public: Bind(wxEVT_CHAR_HOOK, [this](wxKeyEvent& event) { onLocalKeyEvent(event); }); //dialog-specific local key events + //play sound reminder when waiting for user confirmation + if (!cfg.soundFileAlertPending.empty()) + { + timer_.Bind(wxEVT_TIMER, [this, parent, alertSoundPath = cfg.soundFileAlertPending](wxTimerEvent& event) + { + //wxWidgets shows modal error dialog by default => "no, wxWidgets, NO!" + wxLog* oldLogTarget = wxLog::SetActiveTarget(new wxLogStderr); //transfer and receive ownership! + ZEN_ON_SCOPE_EXIT(delete wxLog::SetActiveTarget(oldLogTarget)); + + wxSound::Play(utfTo(alertSoundPath), wxSOUND_ASYNC); + + RequestUserAttention(wxUSER_ATTENTION_INFO); + /* wxUSER_ATTENTION_INFO: flashes window 3 times, unconditionally + wxUSER_ATTENTION_ERROR: flashes without limit, but *only* if not in foreground (FLASHW_TIMERNOFG) :( */ + if (parent) + if (auto tlw = dynamic_cast(&getRootWindow(*parent))) + tlw->RequestUserAttention(wxUSER_ATTENTION_INFO); //top-level window needed for the taskbar flash! + }); + timer_.Start(60'000 /*unit: [ms]*/); + } + //------------------------------------------------------------------------------ StdButtons stdBtns; stdBtns.setAffirmative(m_buttonAccept); @@ -312,6 +336,7 @@ private: bool* checkBoxValue_; const ConfirmationButton3 buttonToDisableWhenChecked_; std::unique_ptr taskbar_; + wxTimer timer_; }; //######################################################################################## diff --git a/wx+/popup_dlg.h b/wx+/popup_dlg.h index bb7ba51b..11e96e3b 100644 --- a/wx+/popup_dlg.h +++ b/wx+/popup_dlg.h @@ -8,6 +8,7 @@ #define POPUP_DLG_H_820780154723456 #include +#include #include #include #include @@ -68,6 +69,7 @@ struct PopupDialogCfg PopupDialogCfg& setMainInstructions (const wxString& label) { textMain = label; return *this; } //set at least one of these! PopupDialogCfg& setDetailInstructions(const wxString& label) { textDetail = label; return *this; } // PopupDialogCfg& disableButton(ConfirmationButton3 button) { disabledButtons.insert(button); return *this; } + PopupDialogCfg& remindWhenPending(const Zstring& soundFilePath) { soundFileAlertPending = soundFilePath; return *this; } PopupDialogCfg& setCheckBox(bool& value, const wxString& label, ConfirmationButton3 disableWhenChecked = ConfirmationButton3::cancel) { checkBoxValue = &value; @@ -84,6 +86,7 @@ private: wxString textMain; wxString textDetail; std::set disabledButtons; + Zstring soundFileAlertPending; bool* checkBoxValue = nullptr; //in/out wxString checkBoxLabel; ConfirmationButton3 buttonToDisableWhenChecked = ConfirmationButton3::cancel; diff --git a/wx+/std_button_layout.h b/wx+/std_button_layout.h index 25745132..e84b0c78 100644 --- a/wx+/std_button_layout.h +++ b/wx+/std_button_layout.h @@ -47,6 +47,11 @@ void setStandardButtonLayout(wxBoxSizer& sizer, const StdButtons& buttons) { assert(sizer.GetOrientation() == wxHORIZONTAL); + //GNOME Human Interface Guidelines: https://developer.gnome.org/hig-book/3.2/hig-book.html#alert-spacing + const int spaceH = fastFromDIP( 6); //OK + const int spaceRimH = fastFromDIP(12); //OK + const int spaceRimV = fastFromDIP(12); //OK + StdButtons buttonsTmp = buttons; auto detach = [&](wxButton*& btn) @@ -54,15 +59,11 @@ void setStandardButtonLayout(wxBoxSizer& sizer, const StdButtons& buttons) if (btn) { assert(btn->GetContainingSizer() == &sizer); - if (btn->IsShown()) - { - bool rv = sizer.Detach(btn); - assert(rv); - if (!rv) - btn = nullptr; - } - else - btn = nullptr; + if (btn->IsShown() && sizer.Detach(btn)) + return; + + assert(false); //why is it hidden!? + btn = nullptr; } }; @@ -71,10 +72,30 @@ void setStandardButtonLayout(wxBoxSizer& sizer, const StdButtons& buttons) detach(buttonsTmp.btnNo); detach(buttonsTmp.btnCancel); - //GNOME Human Interface Guidelines: https://developer.gnome.org/hig-book/3.2/hig-book.html#alert-spacing - const int spaceH = fastFromDIP( 6); //OK - const int spaceRimH = fastFromDIP(12); //OK - const int spaceRimV = fastFromDIP(12); //OK + + //"All your fixed-size spacers are belong to us!" => have a clean slate: consider repeated setStandardButtonLayout() calls + for (size_t pos = sizer.GetItemCount(); pos-- > 0;) + if (wxSizerItem& item = *sizer.GetItem(pos); + item.IsSpacer() && item.GetProportion() == 0 && item.GetSize().y == 0) + { + [[maybe_unused]] bool rv = sizer.Detach(pos); + assert(rv); + } + + //set border on left considering existing items + if (!sizer.IsEmpty()) //for yet another retarded reason wxWidgets will have wxSizer::GetItem(0) cause an assert rather than just return nullptr as documented + if (wxSizerItem& item = *sizer.GetItem(static_cast(0)); + item.IsShown()) + { + assert(item.GetBorder() <= spaceRimV); //pragmatic check: other controls in the sizer should not have a larger border + + if (const int flag = item.GetFlag(); + flag & wxLEFT) + item.SetFlag(flag & ~wxLEFT); + + sizer.Prepend(spaceRimH, 0); + } + bool settingFirstButton = true; auto attach = [&](wxButton* btn) @@ -93,20 +114,6 @@ void setStandardButtonLayout(wxBoxSizer& sizer, const StdButtons& buttons) } }; - //set border on left considering existing items - if (sizer.GetChildren().GetCount() > 0) //for yet another retarded reason wxWidgets will have wxSizer::GetItem(0) cause an assert rather than just return nullptr as documented - if (wxSizerItem* item = sizer.GetItem(static_cast(0))) - { - assert(item->GetBorder() <= spaceRimV); //pragmatic check: other controls in the sizer should not have a larger border - int flag = item->GetFlag(); - if (flag & wxLEFT) - { - flag &= ~wxLEFT; - item->SetFlag(flag); - } - sizer.Insert(static_cast(0), spaceRimH, 0); - } - sizer.Add(spaceRimH, 0); attach(buttonsTmp.btnNo); attach(buttonsTmp.btnCancel); diff --git a/xBRZ/src/xbrz.cpp b/xBRZ/src/xbrz.cpp index 659614fd..9cf4b8e9 100644 --- a/xBRZ/src/xbrz.cpp +++ b/xBRZ/src/xbrz.cpp @@ -475,10 +475,7 @@ public: void readPonm(Kernel_4x4& ker, int x) const //(x, y) is at kernel position E { -#if __has_cpp_attribute(likely) - [[likely]] -#endif - if (const int x_p2 = x + 2; 0 <= x_p2 && x_p2 < srcWidth_) + [[likely]] if (const int x_p2 = x + 2; 0 <= x_p2 && x_p2 < srcWidth_) { ker.p = s_m1 ? s_m1[x_p2] : 0; ker.o = s_0 ? s_0 [x_p2] : 0; @@ -691,10 +688,7 @@ void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, addTopR(blend_xy1, res.blend_h); //set 2nd known corner for (x, y + 1) preProcBuf[x] = blend_xy1; //store on current buffer position for use on next row -#if __has_cpp_attribute(likely) - [[likely]] -#endif - if (x + 1 < srcWidth) + [[likely]] if (x + 1 < srcWidth) { //blend_xy1 -> blend_x1y1 clearAddTopL(blend_xy1, res.blend_i); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column diff --git a/zen/file_access.cpp b/zen/file_access.cpp index db4f2505..6940b22f 100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -29,71 +29,8 @@ using namespace zen; -std::optional zen::parsePathComponents(const Zstring& itemPath) -{ - auto doParse = [&](int sepCountVolumeRoot, bool rootWithSep) -> std::optional - { - const Zstring itemPathFmt = appendSeparator(itemPath); //simplify analysis of root without separator, e.g. \\server-name\share - int sepCount = 0; - for (auto it = itemPathFmt.begin(); it != itemPathFmt.end(); ++it) - if (*it == FILE_NAME_SEPARATOR) - if (++sepCount == sepCountVolumeRoot) - { - Zstring rootPath(itemPathFmt.begin(), rootWithSep ? it + 1 : it); - - Zstring relPath(it + 1, itemPathFmt.end()); - trim(relPath, true, true, [](Zchar c) { return c == FILE_NAME_SEPARATOR; }); - - return PathComponents({rootPath, relPath}); - } - return {}; - }; - - std::optional pc; //"/media/zenju/" and "/Volumes/" should not fail to parse - - if (!pc && startsWith(itemPath, "/mnt/")) //e.g. /mnt/DEVICE_NAME - pc = doParse(3 /*sepCountVolumeRoot*/, false /*rootWithSep*/); - - if (!pc && startsWith(itemPath, "/media/")) //Ubuntu: e.g. /media/zenju/DEVICE_NAME - if (const char* username = ::getenv("USER")) - if (startsWith(itemPath, std::string("/media/") + username + "/")) - pc = doParse(4 /*sepCountVolumeRoot*/, false /*rootWithSep*/); - - if (!pc && startsWith(itemPath, "/run/media/")) //CentOS, Suse: e.g. /run/media/zenju/DEVICE_NAME - if (const char* username = ::getenv("USER")) - if (startsWith(itemPath, std::string("/run/media/") + username + "/")) - pc = doParse(5 /*sepCountVolumeRoot*/, false /*rootWithSep*/); - - if (!pc && startsWith(itemPath, "/run/user/")) //Ubuntu, e.g.: /run/user/1000/gvfs/smb-share:server=192.168.62.145,share=folder - { - Zstring tmp(itemPath.begin() + strLength("/run/user/"), itemPath.end()); - tmp = beforeFirst(tmp, "/gvfs/", IfNotFoundReturn::none); - if (!tmp.empty() && std::all_of(tmp.begin(), tmp.end(), [](char c) { return isDigit(c); })) - /**/pc = doParse(6 /*sepCountVolumeRoot*/, false /*rootWithSep*/); - } - - - if (!pc && startsWith(itemPath, "/")) - pc = doParse(1 /*sepCountVolumeRoot*/, true /*rootWithSep*/); - - return pc; -} - - -std::optional zen::getParentFolderPath(const Zstring& itemPath) +namespace { - if (const std::optional comp = parsePathComponents(itemPath)) - { - if (comp->relPath.empty()) - return std::nullopt; - - const Zstring parentRelPath = beforeLast(comp->relPath, FILE_NAME_SEPARATOR, IfNotFoundReturn::none); - if (parentRelPath.empty()) - return comp->rootPath; - return appendSeparator(comp->rootPath) + parentRelPath; - } - assert(false); - return std::nullopt; } diff --git a/zen/file_access.h b/zen/file_access.h index f3ea6c00..691a8df9 100644 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -17,15 +17,6 @@ namespace zen { //note: certain functions require COM initialization! (vista_file_op.h) -struct PathComponents -{ - Zstring rootPath; //itemPath = rootPath + (FILE_NAME_SEPARATOR?) + relPath - Zstring relPath; // -}; -std::optional parsePathComponents(const Zstring& itemPath); //no value on failure - -std::optional getParentFolderPath(const Zstring& itemPath); - //POSITIVE existence checks; if false: 1. item not existing 2. different type 3.device access error or similar bool fileAvailable(const Zstring& filePath); //noexcept bool dirAvailable (const Zstring& dirPath ); // diff --git a/zen/file_path.cpp b/zen/file_path.cpp new file mode 100644 index 00000000..d846e804 --- /dev/null +++ b/zen/file_path.cpp @@ -0,0 +1,79 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#include "file_path.h" + +using namespace zen; + + +std::optional zen::parsePathComponents(const Zstring& itemPath) +{ + auto doParse = [&](int sepCountVolumeRoot, bool rootWithSep) -> std::optional + { + const Zstring itemPathFmt = appendSeparator(itemPath); //simplify analysis of root without separator, e.g. \\server-name\share + int sepCount = 0; + for (auto it = itemPathFmt.begin(); it != itemPathFmt.end(); ++it) + if (*it == FILE_NAME_SEPARATOR) + if (++sepCount == sepCountVolumeRoot) + { + Zstring rootPath(itemPathFmt.begin(), rootWithSep ? it + 1 : it); + + Zstring relPath(it + 1, itemPathFmt.end()); + trim(relPath, true, true, [](Zchar c) { return c == FILE_NAME_SEPARATOR; }); + + return PathComponents({rootPath, relPath}); + } + return {}; + }; + + std::optional pc; //"/media/zenju/" and "/Volumes/" should not fail to parse + + if (!pc && startsWith(itemPath, "/mnt/")) //e.g. /mnt/DEVICE_NAME + pc = doParse(3 /*sepCountVolumeRoot*/, false /*rootWithSep*/); + + if (!pc && startsWith(itemPath, "/media/")) //Ubuntu: e.g. /media/zenju/DEVICE_NAME + if (const char* username = ::getenv("USER")) + if (startsWith(itemPath, std::string("/media/") + username + "/")) + pc = doParse(4 /*sepCountVolumeRoot*/, false /*rootWithSep*/); + + if (!pc && startsWith(itemPath, "/run/media/")) //CentOS, Suse: e.g. /run/media/zenju/DEVICE_NAME + if (const char* username = ::getenv("USER")) + if (startsWith(itemPath, std::string("/run/media/") + username + "/")) + pc = doParse(5 /*sepCountVolumeRoot*/, false /*rootWithSep*/); + + if (!pc && startsWith(itemPath, "/run/user/")) //Ubuntu, e.g.: /run/user/1000/gvfs/smb-share:server=192.168.62.145,share=folder + { + Zstring tmp(itemPath.begin() + strLength("/run/user/"), itemPath.end()); + tmp = beforeFirst(tmp, "/gvfs/", IfNotFoundReturn::none); + if (!tmp.empty() && std::all_of(tmp.begin(), tmp.end(), [](char c) { return isDigit(c); })) + /**/pc = doParse(6 /*sepCountVolumeRoot*/, false /*rootWithSep*/); + } + + + if (!pc && startsWith(itemPath, "/")) + pc = doParse(1 /*sepCountVolumeRoot*/, true /*rootWithSep*/); + + return pc; +} + + +std::optional zen::getParentFolderPath(const Zstring& itemPath) +{ + if (const std::optional comp = parsePathComponents(itemPath)) + { + if (comp->relPath.empty()) + return std::nullopt; + + const Zstring parentRelPath = beforeLast(comp->relPath, FILE_NAME_SEPARATOR, IfNotFoundReturn::none); + if (parentRelPath.empty()) + return comp->rootPath; + return appendSeparator(comp->rootPath) + parentRelPath; + } + assert(false); + return std::nullopt; +} + + diff --git a/zen/file_path.h b/zen/file_path.h new file mode 100644 index 00000000..e328fa8e --- /dev/null +++ b/zen/file_path.h @@ -0,0 +1,27 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef FILE_PATH_H_3984678473567247567 +#define FILE_PATH_H_3984678473567247567 + +#include "zstring.h" + + +namespace zen +{ +struct PathComponents +{ + Zstring rootPath; //itemPath = rootPath + (FILE_NAME_SEPARATOR?) + relPath + Zstring relPath; // +}; +std::optional parsePathComponents(const Zstring& itemPath); //no value on failure + +std::optional getParentFolderPath(const Zstring& itemPath); + + +} + +#endif //FILE_PATH_H_3984678473567247567 diff --git a/zen/http.cpp b/zen/http.cpp index f8fb24a3..05ed81d1 100644 --- a/zen/http.cpp +++ b/zen/http.cpp @@ -5,7 +5,6 @@ // ***************************************************************************** #include "http.h" - #include "socket.h" #include "open_ssl.h" diff --git a/zen/resolve_path.cpp b/zen/resolve_path.cpp index 4eab76ee..0a45646e 100644 --- a/zen/resolve_path.cpp +++ b/zen/resolve_path.cpp @@ -8,6 +8,7 @@ #include "time.h" #include "thread.h" #include "file_access.h" +#include "file_path.h" #include //getenv() #include //getcwd() @@ -44,7 +45,7 @@ Zstring resolveRelativePath(const Zstring& relativePath) assert(runningOnMainThread()); /* MSDN: "Multithreaded applications and shared library code should not use the GetFullPathName function and should avoid using relative path names. The current directory state written by the - SetCurrentDirectory function is stored as a global variable in each process, + SetCurrentDirectory function is stored as a global variable in each process, therefore multithreaded applications cannot reliably use this value without possible data corruption from other threads, [...]" => Just plain wrong, there is no data corruption. What MSDN really means: GetFullPathName() is *perfectly* thread-safe, but depends diff --git a/zen/symlink_target.h b/zen/symlink_target.h index 32b1211d..60353292 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -9,6 +9,7 @@ #include "scope_guard.h" #include "file_error.h" +#include "file_path.h" #include #include //realpath @@ -58,11 +59,15 @@ zen::SymlinkRawContent getSymlinkRawContent_impl(const Zstring& linkPath) //thro Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError { using namespace zen; - char* targetPath = ::realpath(linkPath.c_str(), nullptr); - if (!targetPath) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), "realpath"); - ZEN_ON_SCOPE_EXIT(::free(targetPath)); - return targetPath; + try + { + char* targetPath = ::realpath(linkPath.c_str(), nullptr); + if (!targetPath) + THROW_LAST_SYS_ERROR("realpath"); + ZEN_ON_SCOPE_EXIT(::free(targetPath)); + return targetPath; + } + catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), e.toString()); } } } diff --git a/zen/zstring.h b/zen/zstring.h index de90c324..1afebe58 100644 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -136,9 +136,9 @@ Zstring getFileExtension(const Zstring& filePath) //common unicode characters -const wchar_t EM_DASH = L'\u2014'; const wchar_t EN_DASH = L'\u2013'; -const wchar_t* const SPACED_DASH = L" \u2013 "; //using 'EN DASH' +const wchar_t EM_DASH = L'\u2014'; +const wchar_t* const SPACED_DASH = L" \u2014 "; //using 'EM DASH' const wchar_t LTR_MARK = L'\u200E'; //UTF-8: E2 80 8E const wchar_t* const ELLIPSIS = L"\u2026"; //"..." const wchar_t MULT_SIGN = L'\u00D7'; //fancy "x" -- cgit