summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FreeFileSync/Build/Changelog.txt21
-rw-r--r--FreeFileSync/Build/Help/FreeFileSync.hhc4
-rw-r--r--FreeFileSync/Build/Help/FreeFileSync.hhp1
-rw-r--r--FreeFileSync/Build/Help/html/Versioning.html5
-rw-r--r--FreeFileSync/Build/Help/html/expert-settings.html4
-rw-r--r--FreeFileSync/Build/Help/html/synchronization-settings.html41
-rw-r--r--FreeFileSync/Build/Help/images/synchronization-settings.pngbin0 -> 29637 bytes
-rw-r--r--FreeFileSync/Build/Help/images/versioning.pngbin0 -> 8349 bytes
-rw-r--r--FreeFileSync/Build/Languages/arabic.lng2
-rw-r--r--FreeFileSync/Build/Languages/bulgarian.lng2
-rw-r--r--FreeFileSync/Build/Languages/chinese_simple.lng2
-rw-r--r--FreeFileSync/Build/Languages/chinese_traditional.lng2
-rw-r--r--FreeFileSync/Build/Languages/croatian.lng2
-rw-r--r--FreeFileSync/Build/Languages/czech.lng2
-rw-r--r--FreeFileSync/Build/Languages/danish.lng2
-rw-r--r--FreeFileSync/Build/Languages/dutch.lng2
-rw-r--r--FreeFileSync/Build/Languages/english_uk.lng2
-rw-r--r--FreeFileSync/Build/Languages/finnish.lng2
-rw-r--r--FreeFileSync/Build/Languages/french.lng2
-rw-r--r--FreeFileSync/Build/Languages/german.lng100
-rw-r--r--FreeFileSync/Build/Languages/greek.lng2
-rw-r--r--FreeFileSync/Build/Languages/hebrew.lng2
-rw-r--r--FreeFileSync/Build/Languages/hindi.lng2
-rw-r--r--FreeFileSync/Build/Languages/hungarian.lng2
-rw-r--r--FreeFileSync/Build/Languages/italian.lng2
-rw-r--r--FreeFileSync/Build/Languages/japanese.lng2
-rw-r--r--FreeFileSync/Build/Languages/korean.lng2
-rw-r--r--FreeFileSync/Build/Languages/lithuanian.lng2
-rw-r--r--FreeFileSync/Build/Languages/outdated/norwegian.lng2
-rw-r--r--FreeFileSync/Build/Languages/outdated/scottish_gaelic.lng2
-rw-r--r--FreeFileSync/Build/Languages/polish.lng2
-rw-r--r--FreeFileSync/Build/Languages/portuguese.lng2
-rw-r--r--FreeFileSync/Build/Languages/portuguese_br.lng2
-rw-r--r--FreeFileSync/Build/Languages/romanian.lng2
-rw-r--r--FreeFileSync/Build/Languages/russian.lng2
-rw-r--r--FreeFileSync/Build/Languages/serbian.lng2
-rw-r--r--FreeFileSync/Build/Languages/slovak.lng2
-rw-r--r--FreeFileSync/Build/Languages/slovenian.lng2
-rw-r--r--FreeFileSync/Build/Languages/spanish.lng2
-rw-r--r--FreeFileSync/Build/Languages/swedish.lng2
-rw-r--r--FreeFileSync/Build/Languages/turkish.lng2
-rw-r--r--FreeFileSync/Build/Languages/ukrainian.lng2
-rw-r--r--FreeFileSync/Source/Makefile6
-rw-r--r--FreeFileSync/Source/RealtimeSync/monitor.cpp14
-rw-r--r--FreeFileSync/Source/application.cpp11
-rw-r--r--FreeFileSync/Source/comparison.cpp66
-rw-r--r--FreeFileSync/Source/comparison.h9
-rw-r--r--FreeFileSync/Source/fs/abstract.cpp2
-rw-r--r--FreeFileSync/Source/fs/abstract.h2
-rw-r--r--FreeFileSync/Source/fs/native.cpp10
-rw-r--r--FreeFileSync/Source/fs/native_traverser_impl.h4
-rw-r--r--FreeFileSync/Source/lib/db_file.cpp4
-rw-r--r--FreeFileSync/Source/lib/dir_exist_async.h2
-rw-r--r--FreeFileSync/Source/lib/dir_lock.cpp5
-rw-r--r--FreeFileSync/Source/lib/ffs_paths.cpp6
-rw-r--r--FreeFileSync/Source/lib/localization.cpp21
-rw-r--r--FreeFileSync/Source/lib/process_xml.cpp10
-rw-r--r--FreeFileSync/Source/lib/process_xml.h5
-rw-r--r--FreeFileSync/Source/lib/resolve_path.cpp249
-rw-r--r--FreeFileSync/Source/lib/resolve_path.h2
-rw-r--r--FreeFileSync/Source/lib/status_handler.h2
-rw-r--r--FreeFileSync/Source/synchronization.cpp150
-rw-r--r--FreeFileSync/Source/synchronization.h2
-rw-r--r--FreeFileSync/Source/ui/custom_grid.cpp48
-rw-r--r--FreeFileSync/Source/ui/folder_selector.cpp10
-rw-r--r--FreeFileSync/Source/ui/gui_generated.cpp251
-rw-r--r--FreeFileSync/Source/ui/gui_generated.h57
-rw-r--r--FreeFileSync/Source/ui/gui_status_handler.cpp143
-rw-r--r--FreeFileSync/Source/ui/gui_status_handler.h14
-rw-r--r--FreeFileSync/Source/ui/main_dlg.cpp37
-rw-r--r--FreeFileSync/Source/ui/main_dlg.h3
-rw-r--r--FreeFileSync/Source/ui/progress_indicator.cpp11
-rw-r--r--FreeFileSync/Source/ui/small_dlgs.cpp8
-rw-r--r--FreeFileSync/Source/ui/sync_cfg.cpp25
-rw-r--r--FreeFileSync/Source/ui/tree_view.cpp21
-rw-r--r--FreeFileSync/Source/ui/version_check.cpp380
-rw-r--r--FreeFileSync/Source/ui/version_check.h18
-rw-r--r--FreeFileSync/Source/version/version.h4
-rw-r--r--wx+/file_drop.h6
-rw-r--r--wx+/grid.cpp124
-rw-r--r--wx+/grid.h8
-rw-r--r--wx+/http.cpp302
-rw-r--r--wx+/http.h25
-rw-r--r--wx+/image_resources.cpp2
-rw-r--r--wx+/rtl.h90
-rw-r--r--zen/build_info.h2
-rw-r--r--zen/dir_watcher.cpp9
-rw-r--r--zen/file_access.cpp96
-rw-r--r--zen/file_access.h3
-rw-r--r--zen/file_error.h1
-rw-r--r--zen/file_id_def.h36
-rw-r--r--zen/file_io.cpp10
-rw-r--r--zen/file_traverser.cpp18
-rw-r--r--zen/format_unit.cpp2
-rw-r--r--zen/guid.h2
-rw-r--r--zen/scope_guard.h64
-rw-r--r--zen/shell_execute.h12
-rw-r--r--zen/stl_tools.h21
-rw-r--r--zen/string_tools.h8
-rw-r--r--zen/symlink_target.h17
-rw-r--r--zen/utf.h8
101 files changed, 1629 insertions, 1091 deletions
diff --git a/FreeFileSync/Build/Changelog.txt b/FreeFileSync/Build/Changelog.txt
index 510a754e..e2967c96 100644
--- a/FreeFileSync/Build/Changelog.txt
+++ b/FreeFileSync/Build/Changelog.txt
@@ -1,5 +1,20 @@
-FreeFileSync 8.0
-----------------
+FreeFileSync 8.1 [2016-04-21]
+-----------------------------
+Follow shell links during drag and drop on main dialog (Windows)
+Significantly improved main grid rendering performance
+Log info about non-default global settings
+Establish new network connections only when needed (Windows)
+Show only a single login dialog per network share
+Show login dialogs for the same network address one after another
+Fixed endless recursion for paths containing certain unicode characters (OS X)
+Support using portable version without direct installation
+Fixed access denied error when verifying read-only target file (Windows)
+New global option for sound cue after comparison
+Updated help file
+
+
+FreeFileSync 8.0 [2016-03-15]
+-----------------------------
Fine-tuned buffer sizes for 70% improved SFTP stream I/O speed
Support incomplete read/write operations while maximizing buffer saturation
Automatically check consistency of FreeFileSync installation
@@ -892,7 +907,7 @@ Updated translation files
FreeFileSync 5.2 [2012-04-01]
-----------------------------
-Fixed runtime error "Error comparing strings! (LCMapString)" (Windows 2000, XP only)
+Fixed runtime error "Error comparing strings! (LCMapString)" (Windows 2000, XP)
FreeFileSync 5.1 [2012-03-31]
diff --git a/FreeFileSync/Build/Help/FreeFileSync.hhc b/FreeFileSync/Build/Help/FreeFileSync.hhc
index 79864cd9..194d8951 100644
--- a/FreeFileSync/Build/Help/FreeFileSync.hhc
+++ b/FreeFileSync/Build/Help/FreeFileSync.hhc
@@ -52,6 +52,10 @@
<param name="Local" value="html\schedule-a-batch-job.html">
</OBJECT>
<LI> <OBJECT type="text/sitemap">
+ <param name="Name" value="Synchronization Settings">
+ <param name="Local" value="html\synchronization-settings.html">
+ </OBJECT>
+ <LI> <OBJECT type="text/sitemap">
<param name="Name" value="Synchronize with SFTP">
<param name="Local" value="html\synchronize-with-sftp.html">
</OBJECT>
diff --git a/FreeFileSync/Build/Help/FreeFileSync.hhp b/FreeFileSync/Build/Help/FreeFileSync.hhp
index a165eb37..92006f89 100644
--- a/FreeFileSync/Build/Help/FreeFileSync.hhp
+++ b/FreeFileSync/Build/Help/FreeFileSync.hhp
@@ -19,6 +19,7 @@ html\external-applications.html
html\freefilesync.html
html\macros.html
html\schedule-a-batch-job.html
+html\synchronization-settings.html
html\synchronize-with-sftp.html
html\tips-and-tricks.html
html\variable-drive-letters.html
diff --git a/FreeFileSync/Build/Help/html/Versioning.html b/FreeFileSync/Build/Help/html/Versioning.html
index 11e6f82b..e3ca9bbd 100644
--- a/FreeFileSync/Build/Help/html/Versioning.html
+++ b/FreeFileSync/Build/Help/html/Versioning.html
@@ -26,8 +26,9 @@
without any decoration and will replace already existing older
versions.
</p>
- <br>
-
+ <img src="../images/versioning.png" alt="Versioning"><br>
+ &nbsp;
+
<h2>2. Keep all versions of old files</h2>
<p>
diff --git a/FreeFileSync/Build/Help/html/expert-settings.html b/FreeFileSync/Build/Help/html/expert-settings.html
index d430be4e..e6f34531 100644
--- a/FreeFileSync/Build/Help/html/expert-settings.html
+++ b/FreeFileSync/Build/Help/html/expert-settings.html
@@ -31,7 +31,7 @@
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;<b>LockDirectoriesDuringSync</b> Enabled=&quot;true&quot;/&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;<b>VerifyCopiedFiles</b> Enabled=&quot;false&quot;/&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;<b>LastSyncsLogSizeMax</b> Bytes=&quot;100000&quot;/&gt;<br>
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;<b>NotificationSound</b> SyncComplete=&quot;harp.wav&quot;/&gt;
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;<b>NotificationSound</b> CompareFinished=&quot;ding.wav&quot; SyncFinished=&quot;harp.wav&quot;/&gt;
</div>
</div></div></div>
<br>
@@ -89,7 +89,7 @@
<p>
<b>NotificationSound:</b><br>
- Select a sound file from the FreeFileSync installation directory to be played after synchronization. Set an empty name if no sound should be played.
+ Select sound files from the FreeFileSync installation directory to be played after comparison or synchronization. Set empty names if no sound should be played.
</p>
</body>
</html> \ No newline at end of file
diff --git a/FreeFileSync/Build/Help/html/synchronization-settings.html b/FreeFileSync/Build/Help/html/synchronization-settings.html
new file mode 100644
index 00000000..ff3c79e1
--- /dev/null
+++ b/FreeFileSync/Build/Help/html/synchronization-settings.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <link rel="stylesheet" type="text/css" href="base.css" >
+ <title>Synchronization Settings</title>
+</head>
+
+<body>
+ <h1>Synchronization Settings</h1>
+
+ <p>
+ <img src="../images/synchronization-settings.png" alt="Synchronization settings dialog"><br>
+ &nbsp;
+ </p>
+
+ <h2>Detect Moved Files</h2>
+
+ <p>
+ FreeFileSync is able to detect moved files on one side and can quickly apply the same move on the target side during synchronization instead of a slow copy and delete. To make this work FreeFileSync requires database files (sync.ffs_db) to compare the current file system state against the time of the last synchronization.
+ </p>
+ <p>
+ The <i>Two-Way</i> variant already creates database files, therefore detection of moved files is always active.<br>
+ The <i>Mirror</i> variant however does not need a database file to find synchronization directions, so detection of moved files
+ is not available by default. If you don't mind the creation of the database files you can enable this feature by
+ selecting the <b>Detect moved files</b> checkbox.
+ </p>
+
+ <div class="box-outer"><div class="bluebox"><div class="box-inner">
+ <b>Note</b>
+ <ul style="margin: 0">
+ <li>Detection of moved files is not available when synchronizing a folder pair for the first time. Only beginning with the second sync
+ the database files are available to determine moved files.
+ <li>Detection is not supported by all file systems. Most notably, certain file moves on the FAT file system cannot be detected.
+ Also virtualized file systems, e.g. a mounted WebDAV drive, might not support move detection. In these cases FreeFileSync will automatically fall back to copy and delete.
+ </ul>
+ </div></div></div>
+ <br>
+
+</body>
+</html> \ No newline at end of file
diff --git a/FreeFileSync/Build/Help/images/synchronization-settings.png b/FreeFileSync/Build/Help/images/synchronization-settings.png
new file mode 100644
index 00000000..cd778caf
--- /dev/null
+++ b/FreeFileSync/Build/Help/images/synchronization-settings.png
Binary files differ
diff --git a/FreeFileSync/Build/Help/images/versioning.png b/FreeFileSync/Build/Help/images/versioning.png
new file mode 100644
index 00000000..1bc4643c
--- /dev/null
+++ b/FreeFileSync/Build/Help/images/versioning.png
Binary files differ
diff --git a/FreeFileSync/Build/Languages/arabic.lng b/FreeFileSync/Build/Languages/arabic.lng
index 8f522c4c..d8614bfb 100644
--- a/FreeFileSync/Build/Languages/arabic.lng
+++ b/FreeFileSync/Build/Languages/arabic.lng
@@ -337,7 +337,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>البحث عن المجلد %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>انقضت المهلة أثناء البحث عن مجلد %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/bulgarian.lng b/FreeFileSync/Build/Languages/bulgarian.lng
index 96716838..bf36cdb9 100644
--- a/FreeFileSync/Build/Languages/bulgarian.lng
+++ b/FreeFileSync/Build/Languages/bulgarian.lng
@@ -333,7 +333,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>ТърÑене на папка %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Изтекло време за Ñ‚ÑŠÑ€Ñене на папка %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/chinese_simple.lng b/FreeFileSync/Build/Languages/chinese_simple.lng
index 21f03125..0a8049b9 100644
--- a/FreeFileSync/Build/Languages/chinese_simple.lng
+++ b/FreeFileSync/Build/Languages/chinese_simple.lng
@@ -332,7 +332,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>正在æœç´¢æ–‡ä»¶å¤¹ %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>æœç´¢æ–‡ä»¶å¤¹ %x 超时.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/chinese_traditional.lng b/FreeFileSync/Build/Languages/chinese_traditional.lng
index f383f381..5fc90821 100644
--- a/FreeFileSync/Build/Languages/chinese_traditional.lng
+++ b/FreeFileSync/Build/Languages/chinese_traditional.lng
@@ -332,7 +332,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>正在æœå°‹è³‡æ–™å¤¾ %x…</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>æœå°‹è³‡æ–™å¤¾ %x 時超時。</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/croatian.lng b/FreeFileSync/Build/Languages/croatian.lng
index 92cb8282..3f8f4318 100644
--- a/FreeFileSync/Build/Languages/croatian.lng
+++ b/FreeFileSync/Build/Languages/croatian.lng
@@ -334,7 +334,7 @@ Stvarno: %y bajta
<source>Searching for folder %x...</source>
<target>Tražim mapu %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Isteklo vrijeme traženja mape %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/czech.lng b/FreeFileSync/Build/Languages/czech.lng
index ff525fef..3fca8e3a 100644
--- a/FreeFileSync/Build/Languages/czech.lng
+++ b/FreeFileSync/Build/Languages/czech.lng
@@ -334,7 +334,7 @@ Aktuálně: %y b
<source>Searching for folder %x...</source>
<target>Otevírání složky %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>VyprÅ¡el Äasový limit pro nalezení adresáře %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/danish.lng b/FreeFileSync/Build/Languages/danish.lng
index 1808257c..de7625b4 100644
--- a/FreeFileSync/Build/Languages/danish.lng
+++ b/FreeFileSync/Build/Languages/danish.lng
@@ -333,7 +333,7 @@ Aktuel: %y byte
<source>Searching for folder %x...</source>
<target>Søger efter mappen %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Timeout ved søgning efter mappen %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/dutch.lng b/FreeFileSync/Build/Languages/dutch.lng
index d4f4412c..8f76028d 100644
--- a/FreeFileSync/Build/Languages/dutch.lng
+++ b/FreeFileSync/Build/Languages/dutch.lng
@@ -333,7 +333,7 @@ Werkelijk: %y bytes
<source>Searching for folder %x...</source>
<target>Zoek naar map %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Time-out tijdens het zoeken naar de map %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/english_uk.lng b/FreeFileSync/Build/Languages/english_uk.lng
index 025252cd..4f5a67f4 100644
--- a/FreeFileSync/Build/Languages/english_uk.lng
+++ b/FreeFileSync/Build/Languages/english_uk.lng
@@ -333,7 +333,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>Searching for folder %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Time out while searching for folder %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/finnish.lng b/FreeFileSync/Build/Languages/finnish.lng
index 3dcd8cea..b78a0b4d 100644
--- a/FreeFileSync/Build/Languages/finnish.lng
+++ b/FreeFileSync/Build/Languages/finnish.lng
@@ -333,7 +333,7 @@ Todellinen: %y tavua
<source>Searching for folder %x...</source>
<target>Etsitään hakemistoa %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Aikaviive hakiessa hakemistoa %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/french.lng b/FreeFileSync/Build/Languages/french.lng
index 075b04f2..b3fd3e5c 100644
--- a/FreeFileSync/Build/Languages/french.lng
+++ b/FreeFileSync/Build/Languages/french.lng
@@ -333,7 +333,7 @@ Trouvé : %y octets
<source>Searching for folder %x...</source>
<target>Recherche du dossier %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Timeout lors de la recherche du dossier %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng
index de0e6ca3..5c5ec50c 100644
--- a/FreeFileSync/Build/Languages/german.lng
+++ b/FreeFileSync/Build/Languages/german.lng
@@ -7,12 +7,6 @@
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
-<source>Installation files are corrupt. Please reinstall FreeFileSync.</source>
-<target>Die Installationsdateien sind beschädigt. Bitte installieren Sie FreeFileSync neu.</target>
-
-<source>Consistency check failed for %x.</source>
-<target>Die Konsistenzprüfung für %x ist fehlgeschlagen.</target>
-
<source>Both sides have changed since last synchronization.</source>
<target>Beide Seiten wurden seit der letzten Synchronisation verändert.</target>
@@ -157,6 +151,39 @@
<source>Generating file list...</source>
<target>Erzeuge Dateiliste...</target>
+<source>Fail-safe file copy</source>
+<target>Dateien ausfallsicher kopieren</target>
+
+<source>Enabled</source>
+<target>Aktiviert</target>
+
+<source>Disabled</source>
+<target>Deaktiviert</target>
+
+<source>Copy locked files</source>
+<target>Gesperrte Dateien kopieren</target>
+
+<source>Copy file access permissions</source>
+<target>Dateizugriffsberechtigungen kopieren</target>
+
+<source>File time tolerance</source>
+<target>Dateizeittoleranz</target>
+
+<source>Folder access timeout</source>
+<target>Zeitlimit für Ordnerzugriff</target>
+
+<source>Run with background priority</source>
+<target>Mit Hintergrundpriorität ausführen</target>
+
+<source>Lock directories during sync</source>
+<target>Verzeichnisse während Synchronisation sperren</target>
+
+<source>Verify copied files</source>
+<target>Kopierte Dateien verifizieren</target>
+
+<source>Using non-default global settings:</source>
+<target>Verwende nicht dem Standard entsprechende globale Einstellungen:</target>
+
<source>Starting comparison</source>
<target>Starte Vergleich</target>
@@ -297,8 +324,8 @@ Tatsächlich: %y bytes
<source>Error Code %x:</source>
<target>Fehlercode %x:</target>
-<source>Failed to connect to SFTP server %x.</source>
-<target>Die Verbindung zum SFTP-Server %x ist fehlgeschlagen.</target>
+<source>Unable to connect to %x.</source>
+<target>Es kann keine Verbindung zu %x aufgebaut werden.</target>
<source>
<pluralform>1 byte</pluralform>
@@ -339,7 +366,7 @@ Tatsächlich: %y bytes
<source>Searching for folder %x...</source>
<target>Suche Ordner %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Das Zeitlimit für die Suche nach Ordner %x wurde überschritten.</target>
<source>Cannot get process information.</source>
@@ -587,6 +614,9 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>Updating attributes of %x</source>
<target>Aktualisiere Attribute von %x</target>
+<source>Cannot write file attributes of %x.</source>
+<target>Die Dateiattribute von %x können nicht geschrieben werden.</target>
+
<source>%x and %y have different content.</source>
<target>%x und %y haben unterschiedlichen Inhalt.</target>
@@ -612,10 +642,10 @@ Die Befehlszeile wird ausgelöst, wenn:
<target>Die folgenden Elemente haben ungelöste Konflikte und werden nicht synchronisiert werden:</target>
<source>The following folders are significantly different. Make sure you have selected the correct folders for synchronization.</source>
-<target>Die folgenden Ordner unterscheiden sich erheblich. Stellen Sie sicher, dass die richtigen Ordner für die Synchronisation ausgewählt sind.</target>
+<target>Die folgenden Ordner unterscheiden sich erheblich. Überprüfen Sie, ob die richtigen Ordner für die Synchronisation ausgewählt wurden.</target>
<source>Not enough free disk space available in:</source>
-<target>Nicht genügend freier Speicher verfügbar unter:</target>
+<target>Nicht genügend freier Speicher verfügbar für:</target>
<source>Required:</source>
<target>Benötigt:</target>
@@ -884,6 +914,9 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>Folder pair:</source>
<target>Ordnerpaar:</target>
+<source>Main settings:</source>
+<target>Haupteinstellungen:</target>
+
<source>Use local settings:</source>
<target>Verwende lokale Einstellungen:</target>
@@ -944,6 +977,9 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>C&lear</source>
<target>&Löschen</target>
+<source>Detect synchronization directions with the help of database files</source>
+<target>Ermittle die Synchronisationsrichtungen mit Hilfe von Datenbankdateien</target>
+
<source>Detect moved files</source>
<target>Verschobene Dateien erkennen</target>
@@ -958,24 +994,21 @@ Die Befehlszeile wird ausgelöst, wenn:
- Erkennung bei erster Synchronisation nicht verfügbar.
</target>
-<source>Detect synchronization directions with the help of database files</source>
-<target>Ermittle die Synchronisationsrichtungen mit Hilfe von Datenbankdateien</target>
-
<source>Delete files:</source>
<target>Dateien löschen:</target>
-<source>&Permanent</source>
-<target>&Permanent</target>
-
-<source>Delete or overwrite files permanently</source>
-<target>Dateien endgültig löschen oder überschreiben</target>
-
<source>&Recycle bin</source>
<target>Papier&korb</target>
<source>Back up deleted and overwritten files in the recycle bin</source>
<target>Gelöschte und überschriebene Dateien im Papierkorb sichern</target>
+<source>&Permanent</source>
+<target>&Permanent</target>
+
+<source>Delete or overwrite files permanently</source>
+<target>Dateien endgültig löschen oder überschreiben</target>
+
<source>&Versioning</source>
<target>&Versionierung</target>
@@ -988,15 +1021,15 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>Handle errors:</source>
<target>Fehlerbehandlung:</target>
-<source>Hide all error and warning messages</source>
-<target>Alle Fehler- und Warnmeldungen unterdrücken</target>
-
<source>&Pop-up</source>
<target>&Nachfragen</target>
<source>Show pop-up on errors or warnings</source>
<target>Ein Auswahlfenster bei Fehlern oder Warnungen anzeigen</target>
+<source>Hide all error and warning messages</source>
+<target>Alle Fehler- und Warnmeldungen unterdrücken</target>
+
<source>On completion:</source>
<target>Nach Abschluss:</target>
@@ -1102,9 +1135,6 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>The following settings are used for all synchronization jobs.</source>
<target>Die folgenden Einstellungen werden für alle Synchronisationsaufgaben verwendet.</target>
-<source>Fail-safe file copy</source>
-<target>Dateien ausfallsicher kopieren</target>
-
<source>
Copy to a temporary file (*.ffs_tmp) before overwriting target.
This guarantees a consistent state even in case of a serious error.
@@ -1117,18 +1147,12 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>(recommended)</source>
<target>(Empfohlen)</target>
-<source>Copy locked files</source>
-<target>Gesperrte Dateien kopieren</target>
-
<source>Copy shared or locked files using the Volume Shadow Copy Service.</source>
<target>Kopiere gesperrte Dateien mit Hilfe des Volumenschattenkopie-Dienstes.</target>
<source>(requires administrator rights)</source>
<target>(Benötigt Administratorrechte)</target>
-<source>Copy file access permissions</source>
-<target>Dateizugriffsberechtigungen kopieren</target>
-
<source>Transfer file and folder permissions.</source>
<target>Ãœbertrage Datei- und Ordnerberechtigungen.</target>
@@ -1618,21 +1642,24 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>FreeFileSync is up to date.</source>
<target>FreeFileSync ist auf dem neuesten Stand.</target>
-<source>Unable to connect to www.freefilesync.org.</source>
-<target>Es kann keine Verbindung zu www.freefilesync.org aufgebaut werden.</target>
-
<source>Cannot find current FreeFileSync version number online. A newer version is likely available. Check manually now?</source>
<target>Die aktuelle FreeFileSync Versionsnummer wurde online nicht gefunden. Wahrscheinlich ist eine neuere Version verfügbar. Jetzt manuell prüfen?</target>
<source>&Check</source>
<target>&Prüfen</target>
+<source>Consistency check failed for %x.</source>
+<target>Die Konsistenzprüfung für %x ist fehlgeschlagen.</target>
+
<source>Cannot find system function %x.</source>
<target>Die Systemfunktion %x wurde nicht gefunden.</target>
<source>Unable to register to receive system messages.</source>
<target>Die Registrierung zum Empfang von Systemmeldungen ist fehlgeschlagen.</target>
+<source>Installation files are corrupt. Please reinstall FreeFileSync.</source>
+<target>Die Installationsdateien sind beschädigt. Bitte installieren Sie FreeFileSync neu.</target>
+
<source>Unable to register device notifications for %x.</source>
<target>Die Registrierung für Gerätemeldungen ist für %x fehlgeschlagen.</target>
@@ -1642,9 +1669,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>The file is locked by another process:</source>
<target>Die Datei wird von einem anderen Prozess gesperrt:</target>
-<source>Cannot write file attributes of %x.</source>
-<target>Die Dateiattribute von %x können nicht geschrieben werden.</target>
-
<source>Cannot read security context of %x.</source>
<target>Der Sicherheitskontext von %x kann nicht gelesen werden.</target>
diff --git a/FreeFileSync/Build/Languages/greek.lng b/FreeFileSync/Build/Languages/greek.lng
index 11dd984b..a7a14841 100644
--- a/FreeFileSync/Build/Languages/greek.lng
+++ b/FreeFileSync/Build/Languages/greek.lng
@@ -333,7 +333,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>Αναζήτηση του υποκαταλόγου %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Λήξη χÏÎ¿Î½Î¹ÎºÎ¿Ï Î¿Ïίου αναζήτησης του υποκαταλόγου %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/hebrew.lng b/FreeFileSync/Build/Languages/hebrew.lng
index 945710d1..9c3e3ca7 100644
--- a/FreeFileSync/Build/Languages/hebrew.lng
+++ b/FreeFileSync/Build/Languages/hebrew.lng
@@ -333,7 +333,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>מחפש ×ת תיקייה %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>פקע הזמן עבור חיפוש של תיקייה %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/hindi.lng b/FreeFileSync/Build/Languages/hindi.lng
index 058b6b19..b546be2c 100644
--- a/FreeFileSync/Build/Languages/hindi.lng
+++ b/FreeFileSync/Build/Languages/hindi.lng
@@ -333,7 +333,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>फ़ोलà¥à¤¡à¤° %x की खोज हो रही है...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>फ़ोलà¥à¤¡à¤° %x की खोज करते हà¥à¤ समय बाहà¥à¤¯ हआ।</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/hungarian.lng b/FreeFileSync/Build/Languages/hungarian.lng
index e4e9794d..59a5f5b7 100644
--- a/FreeFileSync/Build/Languages/hungarian.lng
+++ b/FreeFileSync/Build/Languages/hungarian.lng
@@ -333,7 +333,7 @@ Jelenlegi: %y bájt
<source>Searching for folder %x...</source>
<target>%x könyvtár keresése...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Időtúllépés a %x könyvtár keresése folyamán.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/italian.lng b/FreeFileSync/Build/Languages/italian.lng
index 77638f94..bea508f7 100644
--- a/FreeFileSync/Build/Languages/italian.lng
+++ b/FreeFileSync/Build/Languages/italian.lng
@@ -333,7 +333,7 @@ Attuale: %y byte
<source>Searching for folder %x...</source>
<target>Ricerca della cartella %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Pausa durante la ricerca per la cartella %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/japanese.lng b/FreeFileSync/Build/Languages/japanese.lng
index 3c3f6842..b75ca319 100644
--- a/FreeFileSync/Build/Languages/japanese.lng
+++ b/FreeFileSync/Build/Languages/japanese.lng
@@ -332,7 +332,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>フォルダ %x を検索中...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>フォルダ %x 検索中ã«ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆ.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/korean.lng b/FreeFileSync/Build/Languages/korean.lng
index dc39bfdb..06dbe5f9 100644
--- a/FreeFileSync/Build/Languages/korean.lng
+++ b/FreeFileSync/Build/Languages/korean.lng
@@ -332,7 +332,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>í´ë” %x 검색 중...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>í´ë” %xì„(를) 검색하는 ë™ì•ˆ 타임아웃 ë¨.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/lithuanian.lng b/FreeFileSync/Build/Languages/lithuanian.lng
index 72eb4cd2..6b88f722 100644
--- a/FreeFileSync/Build/Languages/lithuanian.lng
+++ b/FreeFileSync/Build/Languages/lithuanian.lng
@@ -334,7 +334,7 @@ Esamas: %y baitai
<source>Searching for folder %x...</source>
<target>Ieškoma aplanko %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Laikas baigėsi ieškant %x aplanko.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/outdated/norwegian.lng b/FreeFileSync/Build/Languages/outdated/norwegian.lng
index 879580ba..f0558e6d 100644
--- a/FreeFileSync/Build/Languages/outdated/norwegian.lng
+++ b/FreeFileSync/Build/Languages/outdated/norwegian.lng
@@ -317,7 +317,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>Søker etter mappen %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target></target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/outdated/scottish_gaelic.lng b/FreeFileSync/Build/Languages/outdated/scottish_gaelic.lng
index 928b14f8..cd52c07c 100644
--- a/FreeFileSync/Build/Languages/outdated/scottish_gaelic.lng
+++ b/FreeFileSync/Build/Languages/outdated/scottish_gaelic.lng
@@ -212,7 +212,7 @@
<source>Saving file %x...</source>
<target></target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target></target>
<source>Failed to connect to SFTP server %x.</source>
diff --git a/FreeFileSync/Build/Languages/polish.lng b/FreeFileSync/Build/Languages/polish.lng
index 8a808194..841cc6d5 100644
--- a/FreeFileSync/Build/Languages/polish.lng
+++ b/FreeFileSync/Build/Languages/polish.lng
@@ -334,7 +334,7 @@ Przesłany: %y bajtów
<source>Searching for folder %x...</source>
<target>Wyszukiwanie katalogu %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Przekroczono czas oczekiwania podczas szukania katalogu %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/portuguese.lng b/FreeFileSync/Build/Languages/portuguese.lng
index 73f7f010..a374f87d 100644
--- a/FreeFileSync/Build/Languages/portuguese.lng
+++ b/FreeFileSync/Build/Languages/portuguese.lng
@@ -333,7 +333,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>À procura da pasta %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Tempo limite na procura da pasta %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/portuguese_br.lng b/FreeFileSync/Build/Languages/portuguese_br.lng
index 82faeea6..485f32e3 100644
--- a/FreeFileSync/Build/Languages/portuguese_br.lng
+++ b/FreeFileSync/Build/Languages/portuguese_br.lng
@@ -333,7 +333,7 @@ Atual: %y bytes
<source>Searching for folder %x...</source>
<target>Buscando pela pasta %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Tempo esgotado na procura pela pasta %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/romanian.lng b/FreeFileSync/Build/Languages/romanian.lng
index 5dc84db8..68448fa7 100644
--- a/FreeFileSync/Build/Languages/romanian.lng
+++ b/FreeFileSync/Build/Languages/romanian.lng
@@ -334,7 +334,7 @@ Actuală: %y baiți
<source>Searching for folder %x...</source>
<target>Caut dosarul %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Timp expirat la căutarea dosarului %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/russian.lng b/FreeFileSync/Build/Languages/russian.lng
index 74fd26f7..a988dc6f 100644
--- a/FreeFileSync/Build/Languages/russian.lng
+++ b/FreeFileSync/Build/Languages/russian.lng
@@ -343,7 +343,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>ПоиÑк папки %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Тайм-аут при поиÑке папки %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/serbian.lng b/FreeFileSync/Build/Languages/serbian.lng
index 55f61c4b..a741dd76 100644
--- a/FreeFileSync/Build/Languages/serbian.lng
+++ b/FreeFileSync/Build/Languages/serbian.lng
@@ -334,7 +334,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>Тражим фолдер %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Дошло је до тајмаута при претрази фолдера %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/slovak.lng b/FreeFileSync/Build/Languages/slovak.lng
index c7d78a3e..74dd1f68 100644
--- a/FreeFileSync/Build/Languages/slovak.lng
+++ b/FreeFileSync/Build/Languages/slovak.lng
@@ -334,7 +334,7 @@ Aktuálne: %y b
<source>Searching for folder %x...</source>
<target>Vyhľadávanie prieÄinka %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>VyprÅ¡al Äasový limit pre nájdenie prieÄinka %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/slovenian.lng b/FreeFileSync/Build/Languages/slovenian.lng
index 9fda61ff..1b6d116d 100644
--- a/FreeFileSync/Build/Languages/slovenian.lng
+++ b/FreeFileSync/Build/Languages/slovenian.lng
@@ -335,7 +335,7 @@ Dejansko: %y bajtov
<source>Searching for folder %x...</source>
<target>Iskanje mape %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>ÄŒas je potekel med iskanjem datoteke %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/spanish.lng b/FreeFileSync/Build/Languages/spanish.lng
index fccdf5ad..f51aac89 100644
--- a/FreeFileSync/Build/Languages/spanish.lng
+++ b/FreeFileSync/Build/Languages/spanish.lng
@@ -333,7 +333,7 @@ Reales: %y bytes
<source>Searching for folder %x...</source>
<target>Buscando carpeta %x…</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Tiempo agotado buscando la carpeta %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/swedish.lng b/FreeFileSync/Build/Languages/swedish.lng
index 3a8e4f91..12c9b91f 100644
--- a/FreeFileSync/Build/Languages/swedish.lng
+++ b/FreeFileSync/Build/Languages/swedish.lng
@@ -333,7 +333,7 @@ Aktuell: %y byte
<source>Searching for folder %x...</source>
<target>Söker efter mappen %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>Timeout vid sökning efter mappen %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/turkish.lng b/FreeFileSync/Build/Languages/turkish.lng
index 0419a7f7..0d0b620a 100644
--- a/FreeFileSync/Build/Languages/turkish.lng
+++ b/FreeFileSync/Build/Languages/turkish.lng
@@ -333,7 +333,7 @@ Gerçekleşen: %y bayt
<source>Searching for folder %x...</source>
<target>%x klasörü aranıyor...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>%x klasöründeki arama işlemi zaman aşımına uğradı.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Build/Languages/ukrainian.lng b/FreeFileSync/Build/Languages/ukrainian.lng
index f821aa56..72f21600 100644
--- a/FreeFileSync/Build/Languages/ukrainian.lng
+++ b/FreeFileSync/Build/Languages/ukrainian.lng
@@ -334,7 +334,7 @@ Actual: %y bytes
<source>Searching for folder %x...</source>
<target>Пошук папки %x...</target>
-<source>Time out while searching for folder %x.</source>
+<source>Timeout while searching for folder %x.</source>
<target>ВичерпавÑÑ Ñ‡Ð°Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ папки %x.</target>
<source>Cannot get process information.</source>
diff --git a/FreeFileSync/Source/Makefile b/FreeFileSync/Source/Makefile
index e7d21650..45f6f91a 100644
--- a/FreeFileSync/Source/Makefile
+++ b/FreeFileSync/Source/Makefile
@@ -83,6 +83,7 @@ CPP_LIST+=../../wx+/grid.cpp
CPP_LIST+=../../wx+/image_tools.cpp
CPP_LIST+=../../wx+/graph.cpp
CPP_LIST+=../../wx+/tooltip.cpp
+CPP_LIST+=../../wx+/http.cpp
CPP_LIST+=../../wx+/image_resources.cpp
CPP_LIST+=../../wx+/popup_dlg.cpp
CPP_LIST+=../../wx+/popup_dlg_generated.cpp
@@ -113,9 +114,10 @@ install:
mkdir -p $(APPSHAREDIR)
cp -R ../Build/Languages/ \
../Build/Help/ \
- ../Build/Sync_Complete.wav \
+ ../Build/ding.wav \
+ ../Build/gong.wav \
+ ../Build/harp.wav \
../Build/Resources.zip \
- ../Build/styles.gtk_rc \
$(APPSHAREDIR)
mkdir -p $(DOCSHAREDIR)
diff --git a/FreeFileSync/Source/RealtimeSync/monitor.cpp b/FreeFileSync/Source/RealtimeSync/monitor.cpp
index cce7ddf5..188c6c1a 100644
--- a/FreeFileSync/Source/RealtimeSync/monitor.cpp
+++ b/FreeFileSync/Source/RealtimeSync/monitor.cpp
@@ -95,7 +95,7 @@ WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw
const std::int64_t TICKS_DIR_CHECK_INTERVAL = CHECK_FOLDER_INTERVAL * ticksPerSec(); //0 on error
TickVal lastCheck = getTicks(); //0 on error
- while (true)
+ for (;;)
{
const bool checkDirExistNow = [&]() -> bool //checking once per sec should suffice
{
@@ -166,7 +166,11 @@ void waitForMissingDirs(const std::vector<Zstring>& folderPathPhrases, //throw F
{
#ifdef ZEN_WIN
//1. login to network share, if necessary -> we probably do NOT want multiple concurrent runs: GUI!?
- loginNetworkShare(folderPathFmt, false); //login networks shares, no PW prompt -> is this really RTS's job?
+ try{
+ connectNetworkShare(folderPathFmt, false /*allowUserInteraction*/); //throw FileError
+ //is this really RTS's job?
+ }
+ catch (FileError&) {}
#endif
//2. check dir existence
return zen::dirExists(folderPathFmt);
@@ -230,12 +234,12 @@ void rts::monitorDirectories(const std::vector<Zstring>& folderPathPhrases, unsi
//schedule initial execution (*after* all directories have arrived, which could take some time which we don't want to include)
time_t nextExecDate = std::time(nullptr) + delay;
- while (true) //loop over command invocations
+ for (;;) //loop over command invocations
{
DirWatcher::Entry lastChangeDetected;
try
{
- while (true) //loop over detected changes
+ for (;;) //loop over detected changes
{
//wait for changes (and for all directories to become available)
WaitResult res = waitForChanges(folderPathPhrases, [&](bool readyForSync) //throw FileError, ExecCommandNowException
@@ -271,7 +275,7 @@ void rts::monitorDirectories(const std::vector<Zstring>& folderPathPhrases, unsi
}
};
- while (true)
+ for (;;)
try
{
execMonitoring(); //throw FileError
diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp
index 71a59bfb..7c1a802e 100644
--- a/FreeFileSync/Source/application.cpp
+++ b/FreeFileSync/Source/application.cpp
@@ -80,7 +80,7 @@ std::vector<Zstring> getCommandlineArgs(const wxApp& app)
{
std::vector<Zstring> args;
#ifdef ZEN_WIN
- //"Parsing C++ Command-Line Arguments": https://msdn.microsoft.com/en-us/library/17w5ykft
+ //"Parsing C++ Command-Line Arguments": https://msdn.microsoft.com/en-us/library/17w5ykft
//we do the job ourselves! both wxWidgets and ::CommandLineToArgvW() parse "C:\" "D:\" as single line C:\" D:\"
//-> "solution": we just don't support protected quotation mark!
Zstring cmdLine = ::GetCommandLine(); //only way to get a unicode commandline
@@ -615,7 +615,7 @@ void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCf
//class handling status updates and error messages
BatchStatusHandler statusHandler(!batchCfg.runMinimized, //throw BatchAbortProcess
extractJobName(referenceFile),
- globalCfg.soundFileSyncComplete,
+ globalCfg.soundFileSyncFinished,
timeStamp,
batchCfg.logFolderPathPhrase,
batchCfg.logfilesCountLimit,
@@ -628,7 +628,9 @@ void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCf
batchCfg.mainCfg.onCompletion,
globalCfg.gui.onCompletionHistory);
- const std::vector<FolderPairCfg> cmpConfig = extractCompareCfg(batchCfg.mainCfg, globalCfg.fileTimeTolerance);
+ logNonDefaultSettings(globalCfg, statusHandler); //inform about (important) non-default global settings
+
+ const std::vector<FolderPairCfg> cmpConfig = extractCompareCfg(batchCfg.mainCfg);
bool allowPwPrompt = false;
switch (batchCfg.handleError)
@@ -646,6 +648,7 @@ void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCf
//COMPARE DIRECTORIES
FolderComparison cmpResult = compare(globalCfg.optDialogs,
+ globalCfg.fileTimeTolerance,
allowPwPrompt, //allowUserInteraction
globalCfg.runWithBackgroundPriority,
globalCfg.folderAccessTimeout,
@@ -664,7 +667,7 @@ void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCf
globalCfg.verifyFileCopy,
globalCfg.copyLockedFiles,
globalCfg.copyFilePermissions,
- globalCfg.failsafeFileCopy,
+ globalCfg.failSafeFileCopy,
globalCfg.runWithBackgroundPriority,
globalCfg.folderAccessTimeout,
syncProcessCfg,
diff --git a/FreeFileSync/Source/comparison.cpp b/FreeFileSync/Source/comparison.cpp
index 99e9b8d6..c96fb05a 100644
--- a/FreeFileSync/Source/comparison.cpp
+++ b/FreeFileSync/Source/comparison.cpp
@@ -18,7 +18,7 @@
using namespace zen;
-std::vector<FolderPairCfg> zen::extractCompareCfg(const MainConfiguration& mainCfg, int fileTimeTolerance)
+std::vector<FolderPairCfg> zen::extractCompareCfg(const MainConfiguration& mainCfg)
{
//merge first and additional pairs
std::vector<FolderPairEnh> allPairs = { mainCfg.firstPair };
@@ -31,7 +31,6 @@ std::vector<FolderPairCfg> zen::extractCompareCfg(const MainConfiguration& mainC
return FolderPairCfg(enhPair.folderPathPhraseLeft_, enhPair.folderPathPhraseRight_,
enhPair.altCmpConfig.get() ? enhPair.altCmpConfig->compareVar : mainCfg.cmpConfig.compareVar,
enhPair.altCmpConfig.get() ? enhPair.altCmpConfig->handleSymlinks : mainCfg.cmpConfig.handleSymlinks,
- fileTimeTolerance,
enhPair.altCmpConfig.get() ? enhPair.altCmpConfig->ignoreTimeShiftMinutes : mainCfg.cmpConfig.ignoreTimeShiftMinutes,
normalizeFilters(mainCfg.globalFilter, enhPair.localFilter),
@@ -164,7 +163,7 @@ void checkFolderDependency(const std::vector<ResolvedFolderPair>& folderPairs, b
class ComparisonBuffer
{
public:
- ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, ProcessCallback& callback);
+ ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, int fileTimeTolerance, ProcessCallback& callback);
//create comparison result table and fill category except for files existing on both sides: undefinedFiles and undefinedSymlinks are appended!
std::shared_ptr<BaseFolderPair> compareByTimeSize(const ResolvedFolderPair& fp, const FolderPairCfg& fpConfig) const;
@@ -181,11 +180,13 @@ private:
std::vector<SymlinkPair*>& undefinedSymlinks) const;
std::map<DirectoryKey, DirectoryValue> directoryBuffer; //contains only *existing* directories
+ const int fileTimeTolerance_;
ProcessCallback& callback_;
};
-ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, ProcessCallback& callback) : callback_(callback)
+ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, int fileTimeTolerance, ProcessCallback& callback) :
+ fileTimeTolerance_(fileTimeTolerance), callback_(callback)
{
class CbImpl : public FillBufferCallback
{
@@ -328,7 +329,7 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::compareByTimeSize(const Resolv
for (FilePair* file : uncategorizedFiles)
{
switch (compareFileTime(file->getLastWriteTime<LEFT_SIDE>(),
- file->getLastWriteTime<RIGHT_SIDE>(), fpConfig.fileTimeTolerance, fpConfig.ignoreTimeShiftMinutes))
+ file->getLastWriteTime<RIGHT_SIDE>(), fileTimeTolerance_, fpConfig.ignoreTimeShiftMinutes))
{
case TimeResult::EQUAL:
//Caveat:
@@ -806,7 +807,7 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::performComparison(const Resolv
bufValueRight != nullptr,
fpCfg.filter.nameFilter->copyFilterAddingExclusion(excludefilterFailedRead),
fpCfg.compareVar,
- fpCfg.fileTimeTolerance,
+ fileTimeTolerance_,
fpCfg.ignoreTimeShiftMinutes);
//PERF_START;
@@ -831,7 +832,42 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::performComparison(const Resolv
}
+void zen::logNonDefaultSettings(const xmlAccess::XmlGlobalSettings& activeSettings, ProcessCallback& callback)
+{
+ const xmlAccess::XmlGlobalSettings defaultSettings;
+ std::wstring changedSettingsMsg;
+
+ if (activeSettings.failSafeFileCopy != defaultSettings.failSafeFileCopy)
+ changedSettingsMsg += L"\n " + _("Fail-safe file copy") + L" - " + (activeSettings.failSafeFileCopy ? _("Enabled") : _("Disabled"));
+
+ if (activeSettings.copyLockedFiles != defaultSettings.copyLockedFiles)
+ changedSettingsMsg += L"\n " + _("Copy locked files") + L" - " + (activeSettings.copyLockedFiles ? _("Enabled") : _("Disabled"));
+
+ if (activeSettings.copyFilePermissions != defaultSettings.copyFilePermissions)
+ changedSettingsMsg += L"\n " + _("Copy file access permissions") + L" - " + (activeSettings.copyFilePermissions ? _("Enabled") : _("Disabled"));
+
+ if (activeSettings.fileTimeTolerance != defaultSettings.fileTimeTolerance)
+ changedSettingsMsg += L"\n " + _("File time tolerance") + L" - " + numberTo<std::wstring>(activeSettings.fileTimeTolerance);
+
+ if (activeSettings.folderAccessTimeout != defaultSettings.folderAccessTimeout)
+ changedSettingsMsg += L"\n " + _("Folder access timeout") + L" - " + numberTo<std::wstring>(activeSettings.folderAccessTimeout);
+
+ if (activeSettings.runWithBackgroundPriority != defaultSettings.runWithBackgroundPriority)
+ changedSettingsMsg += L"\n " + _("Run with background priority") + L" - " + (activeSettings.runWithBackgroundPriority ? _("Enabled") : _("Disabled"));
+
+ if (activeSettings.createLockFile != defaultSettings.createLockFile)
+ changedSettingsMsg += L"\n " + _("Lock directories during sync") + L" - " + (activeSettings.createLockFile ? _("Enabled") : _("Disabled"));
+
+ if (activeSettings.verifyFileCopy != defaultSettings.verifyFileCopy)
+ changedSettingsMsg += L"\n " + _("Verify copied files") + L" - " + (activeSettings.verifyFileCopy ? _("Enabled") : _("Disabled"));
+
+ if (!changedSettingsMsg.empty())
+ callback.reportInfo(_("Using non-default global settings:") + changedSettingsMsg);
+}
+
+
FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings,
+ int fileTimeTolerance,
bool allowUserInteraction,
bool runWithBackgroundPriority,
int folderAccessTimeout,
@@ -840,6 +876,15 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings,
const std::vector<FolderPairCfg>& cfgList,
ProcessCallback& callback)
{
+ //PERF_START;
+
+ //indicator at the very beginning of the log to make sense of "total time"
+ //init process: keep at beginning so that all gui elements are initialized properly
+ callback.initNewPhase(-1, 0, ProcessCallback::PHASE_SCANNING); //may throw; it's not known how many files will be scanned => -1 objects
+ //callback.reportInfo(_("Starting comparison")); -> still useful?
+
+ //-------------------------------------------------------------------------------
+
//specify process and resource handling priorities
std::unique_ptr<ScheduleForBackgroundProcessing> backgroundPrio;
if (runWithBackgroundPriority)
@@ -863,13 +908,6 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings,
callback.reportInfo(e.toString()); //may throw!
}
- //PERF_START;
-
- callback.reportInfo(_("Starting comparison")); //indicator at the very beginning of the log to make sense of "total time"
-
- //init process: keep at beginning so that all gui elements are initialized properly
- callback.initNewPhase(-1, 0, ProcessCallback::PHASE_SCANNING); //may throw; it's not known how many files will be scanned => -1 objects
-
//-------------------some basic checks:------------------------------------------
const ResolvedBaseFolders& resInfo = initializeBaseFolders(cfgList, folderAccessTimeout, allowUserInteraction, callback);
@@ -921,7 +959,7 @@ FolderComparison zen::compare(xmlAccess::OptionalDialogs& warnings,
{
//------------ traverse/read folders -----------------------------------------------------
//PERF_START;
- ComparisonBuffer cmpBuff(dirsToRead, callback);
+ ComparisonBuffer cmpBuff(dirsToRead, fileTimeTolerance, callback);
//PERF_STOP;
//process binary comparison as one junk
diff --git a/FreeFileSync/Source/comparison.h b/FreeFileSync/Source/comparison.h
index 41617d4f..ea258696 100644
--- a/FreeFileSync/Source/comparison.h
+++ b/FreeFileSync/Source/comparison.h
@@ -22,7 +22,6 @@ struct FolderPairCfg
const Zstring& folderPathPhraseRight,
CompareVariant cmpVar,
SymLinkHandling handleSymlinksIn,
- int fileTimeToleranceIn,
const std::vector<unsigned int>& ignoreTimeShiftMinutesIn,
const NormalizedFilter& filterIn,
const DirectionConfig& directCfg) :
@@ -30,7 +29,6 @@ struct FolderPairCfg
folderPathPhraseRight_(folderPathPhraseRight),
compareVar(cmpVar),
handleSymlinks(handleSymlinksIn),
- fileTimeTolerance(fileTimeToleranceIn),
ignoreTimeShiftMinutes(ignoreTimeShiftMinutesIn),
filter(filterIn),
directionCfg(directCfg) {}
@@ -40,7 +38,6 @@ struct FolderPairCfg
CompareVariant compareVar;
SymLinkHandling handleSymlinks;
- int fileTimeTolerance;
std::vector<unsigned int> ignoreTimeShiftMinutes;
NormalizedFilter filter;
@@ -48,10 +45,14 @@ struct FolderPairCfg
DirectionConfig directionCfg;
};
-std::vector<FolderPairCfg> extractCompareCfg(const MainConfiguration& mainCfg, int fileTimeTolerance); //fill FolderPairCfg and resolve folder pairs
+std::vector<FolderPairCfg> extractCompareCfg(const MainConfiguration& mainCfg); //fill FolderPairCfg and resolve folder pairs
+
+//inform about (important) non-default global settings related to comparison and synchronization
+void logNonDefaultSettings(const xmlAccess::XmlGlobalSettings& currentSettings, ProcessCallback& callback);
//FFS core routine:
FolderComparison compare(xmlAccess::OptionalDialogs& warnings,
+ int fileTimeTolerance,
bool allowUserInteraction,
bool runWithBackgroundPriority,
int folderAccessTimeout,
diff --git a/FreeFileSync/Source/fs/abstract.cpp b/FreeFileSync/Source/fs/abstract.cpp
index b8c5bec3..0f7c9ae6 100644
--- a/FreeFileSync/Source/fs/abstract.cpp
+++ b/FreeFileSync/Source/fs/abstract.cpp
@@ -93,7 +93,7 @@ AFS::FileAttribAfterCopy AFS::copyFileTransactional(const AbstractPath& apSource
CAVEAT on FAT/FAT32: the sequence of deleting the target file and renaming "file.txt.ffs_tmp" to "file.txt" does
NOT PRESERVE the creation time of the .ffs_tmp file, but SILENTLY "reuses" whatever creation time the old "file.txt" had!
This "feature" is called "File System Tunneling":
- http://blogs.msdn.com/b/oldnewthing/archive/2005/07/15/439261.aspx
+ https://blogs.msdn.microsoft.com/oldnewthing/20050715-14/?p=34923
http://support.microsoft.com/kb/172190/en-us
*/
return attr;
diff --git a/FreeFileSync/Source/fs/abstract.h b/FreeFileSync/Source/fs/abstract.h
index e424506c..17ece0b8 100644
--- a/FreeFileSync/Source/fs/abstract.h
+++ b/FreeFileSync/Source/fs/abstract.h
@@ -135,7 +135,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
~OutputStream();
size_t getBlockSize() const { return outStream_->getBlockSize(); } //non-zero block size is AFS contract!
size_t tryWrite(const void* data, size_t len); //throw FileError; may return short! CONTRACT: bytesToWrite > 0
- FileId finalize (const std::function<void()>& onUpdateStatus); //throw FileError
+ FileId finalize(const std::function<void()>& onUpdateStatus); //throw FileError
private:
std::unique_ptr<OutputStreamImpl> outStream_; //bound!
diff --git a/FreeFileSync/Source/fs/native.cpp b/FreeFileSync/Source/fs/native.cpp
index de0b3d27..572881f5 100644
--- a/FreeFileSync/Source/fs/native.cpp
+++ b/FreeFileSync/Source/fs/native.cpp
@@ -23,6 +23,8 @@
#include <fcntl.h> //fallocate, fcntl
#endif
+using namespace zen;
+
namespace
{
@@ -453,13 +455,13 @@ private:
void connectNetworkFolder(const Zstring& itemPathImpl, bool allowUserInteraction) const override //throw FileError
{
- warn_static("clean-up/remove/re-think the getAsyncConnectFolder() function")
+ warn_static("clean-up/remove/re-think connectNetworkFolder()")
#ifdef ZEN_WIN
initComForThread(); //throw FileError
//login to network share, if necessary
- loginNetworkShare(itemPathImpl, allowUserInteraction);
+ connectNetworkShare(itemPathImpl, allowUserInteraction); //throw FileError
#endif
}
@@ -571,8 +573,8 @@ bool RecycleSessionNative::recycleItem(const AbstractPath& itemPath, const Zstri
if (pos == Zstring::npos)
return false;
- const size_t pos2 = itemPathImpl.find(L'\\', pos + 1);
- return endsWith(StringRef<const Zchar>(itemPathImpl.begin(), pos2 == Zstring::npos ? itemPathImpl.end() : itemPathImpl.begin() + pos2), AFS::TEMP_FILE_ENDING);
+ auto itEnd = std::find(itemPathImpl.begin() + pos + 1, itemPathImpl.end(), L'\\');
+ return endsWith(StringRef<const Zchar>(itemPathImpl.begin(), itEnd), AFS::TEMP_FILE_ENDING);
}();
//do not create RecycleBin.ffs_tmp directories recursively if recycling a particular item fails forever!
diff --git a/FreeFileSync/Source/fs/native_traverser_impl.h b/FreeFileSync/Source/fs/native_traverser_impl.h
index 3c3efe2f..330df263 100644
--- a/FreeFileSync/Source/fs/native_traverser_impl.h
+++ b/FreeFileSync/Source/fs/native_traverser_impl.h
@@ -25,8 +25,8 @@ AFS::FileId convertToAbstractFileId(const zen::FileId& fid)
if (fid == zen::FileId())
return AFS::FileId();
- AFS::FileId out(reinterpret_cast<const char*>(&fid.first), sizeof(fid.first));
- out.append(reinterpret_cast<const char*>(&fid.second), sizeof(fid.second));
+ AFS::FileId out(reinterpret_cast<const char*>(&fid.volumeId), sizeof(fid.volumeId));
+ out.append(reinterpret_cast<const char*>(&fid.fileIndex), sizeof(fid.fileIndex));
return out;
}
diff --git a/FreeFileSync/Source/lib/db_file.cpp b/FreeFileSync/Source/lib/db_file.cpp
index 0bcc3e40..47d963a3 100644
--- a/FreeFileSync/Source/lib/db_file.cpp
+++ b/FreeFileSync/Source/lib/db_file.cpp
@@ -406,9 +406,9 @@ private:
warn_static("remove after migration! 2015-05-02")
if (streamVersion_ == 1)
{
- auto devId = static_cast<DeviceId >(readNumber<std::uint64_t>(input)); //
+ auto devId = static_cast<VolumeId >(readNumber<std::uint64_t>(input)); //
auto fileIdx = static_cast<FileIndex>(readNumber<std::uint64_t>(input)); //silence "loss of precision" compiler warnings
- if (devId != 0 || fileIdx != 0)
+ if (devId != 0 && fileIdx != 0)
{
fileId.append(reinterpret_cast<const char*>(&devId), sizeof(devId));
fileId.append(reinterpret_cast<const char*>(&fileIdx), sizeof(fileIdx));
diff --git a/FreeFileSync/Source/lib/dir_exist_async.h b/FreeFileSync/Source/lib/dir_exist_async.h
index 56a14cdc..3bb01f32 100644
--- a/FreeFileSync/Source/lib/dir_exist_async.h
+++ b/FreeFileSync/Source/lib/dir_exist_async.h
@@ -74,7 +74,7 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath, AFS::LessAb
catch (const FileError& e) { output.failedChecks.emplace(fi.first, e); }
}
else
- output.failedChecks.emplace(fi.first, FileError(replaceCpy(_("Time out while searching for folder %x."), L"%x", displayPathFmt)));
+ output.failedChecks.emplace(fi.first, FileError(replaceCpy(_("Timeout while searching for folder %x."), L"%x", displayPathFmt)));
}
return output;
diff --git a/FreeFileSync/Source/lib/dir_lock.cpp b/FreeFileSync/Source/lib/dir_lock.cpp
index fc98f308..4cc9b9f1 100644
--- a/FreeFileSync/Source/lib/dir_lock.cpp
+++ b/FreeFileSync/Source/lib/dir_lock.cpp
@@ -515,9 +515,8 @@ bool tryLock(const Zstring& lockFilePath) //throw FileError
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
CREATE_NEW, //_In_ DWORD dwCreationDisposition,
- FILE_ATTRIBUTE_TEMPORARY | //
- FILE_ATTRIBUTE_NORMAL |
- FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes,
+ FILE_ATTRIBUTE_TEMPORARY | //https://blogs.msdn.microsoft.com/larryosterman/2004/04/19/its-only-temporary/
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes,
nullptr); //_In_opt_ HANDLE hTemplateFile
if (fileHandle == INVALID_HANDLE_VALUE)
{
diff --git a/FreeFileSync/Source/lib/ffs_paths.cpp b/FreeFileSync/Source/lib/ffs_paths.cpp
index 31694214..ee843156 100644
--- a/FreeFileSync/Source/lib/ffs_paths.cpp
+++ b/FreeFileSync/Source/lib/ffs_paths.cpp
@@ -141,7 +141,11 @@ Zstring zen::getFreeFileSyncLauncherPath()
if (CFStringRef path = ::CFURLCopyFileSystemPath(absUrl, kCFURLPOSIXPathStyle))
{
ZEN_ON_SCOPE_EXIT(::CFRelease(path));
- return appendSeparator(osx::cfStringToZstring(path)) + "Contents/MacOS/FreeFileSync";
+ try
+ {
+ return appendSeparator(osx::cfStringToZstring(path)) + "Contents/MacOS/FreeFileSync"; //throw SysError
+ }
+ catch (SysError&) {}
}
}
return Zstr("./FreeFileSync"); //fallback: at least give some hint...
diff --git a/FreeFileSync/Source/lib/localization.cpp b/FreeFileSync/Source/lib/localization.cpp
index 2f93c7ae..098f42d7 100644
--- a/FreeFileSync/Source/lib/localization.cpp
+++ b/FreeFileSync/Source/lib/localization.cpp
@@ -25,7 +25,7 @@
#elif defined ZEN_MAC
#include <zen/osx_string.h>
- #include <CoreServices/CoreServices.h>
+ // #include <CoreServices/CoreServices.h>
#endif
using namespace zen;
@@ -134,14 +134,21 @@ struct LessTranslation
return ::wcscasecmp(lhs.languageName.c_str(), rhs.languageName.c_str()) < 0; //ignores case; locale-dependent!
#elif defined ZEN_MAC
- CFStringRef langL = osx::createCFString(utfCvrtTo<std::string>(lhs.languageName).c_str());
- ZEN_ON_SCOPE_EXIT(::CFRelease(langL));
+ try
+ {
+ CFStringRef langL = osx::createCFString(utfCvrtTo<std::string>(lhs.languageName).c_str()); //throw SysError
+ ZEN_ON_SCOPE_EXIT(::CFRelease(langL));
- CFStringRef langR = osx::createCFString(utfCvrtTo<std::string>(rhs.languageName).c_str());
- ZEN_ON_SCOPE_EXIT(::CFRelease(langR));
+ CFStringRef langR = osx::createCFString(utfCvrtTo<std::string>(rhs.languageName).c_str()); //throw SysError
+ ZEN_ON_SCOPE_EXIT(::CFRelease(langR));
- //correctly position "czech" unlike wcscasecmp():
- return::CFStringCompare(langL, langR, kCFCompareLocalized | kCFCompareCaseInsensitive) == kCFCompareLessThan; //no-fail
+ //correctly position "czech" unlike wcscasecmp():
+ return::CFStringCompare(langL, langR, kCFCompareLocalized | kCFCompareCaseInsensitive) == kCFCompareLessThan; //no-fail
+ }
+ catch (const SysError& e)
+ {
+ throw std::runtime_error(utfCvrtTo<std::string>(e.toString()) + " " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+ }
#endif
}
};
diff --git a/FreeFileSync/Source/lib/process_xml.cpp b/FreeFileSync/Source/lib/process_xml.cpp
index 5bd93e67..04b55ea5 100644
--- a/FreeFileSync/Source/lib/process_xml.cpp
+++ b/FreeFileSync/Source/lib/process_xml.cpp
@@ -981,7 +981,7 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config)
else
inGeneral["Language"].attribute("Name", config.programLanguage);
- inGeneral["FailSafeFileCopy" ].attribute("Enabled", config.failsafeFileCopy);
+ inGeneral["FailSafeFileCopy" ].attribute("Enabled", config.failSafeFileCopy);
inGeneral["CopyLockedFiles" ].attribute("Enabled", config.copyLockedFiles);
inGeneral["CopyFilePermissions" ].attribute("Enabled", config.copyFilePermissions);
inGeneral["AutomaticRetry" ].attribute("Count" , config.automaticRetryCount);
@@ -992,7 +992,8 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config)
inGeneral["LockDirectoriesDuringSync"].attribute("Enabled", config.createLockFile);
inGeneral["VerifyCopiedFiles" ].attribute("Enabled", config.verifyFileCopy);
inGeneral["LastSyncsLogSizeMax" ].attribute("Bytes" , config.lastSyncsLogFileSizeMax);
- inGeneral["NotificationSound" ].attribute("SyncComplete", config.soundFileSyncComplete);
+ inGeneral["NotificationSound" ].attribute("CompareFinished", config.soundFileCompareFinished);
+ inGeneral["NotificationSound" ].attribute("SyncFinished" , config.soundFileSyncFinished);
XmlIn inOpt = inGeneral["OptionalDialogs"];
inOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warningUnresolvedConflicts);
@@ -1368,7 +1369,7 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out)
outGeneral["Language"].attribute("Name", config.programLanguage);
- outGeneral["FailSafeFileCopy" ].attribute("Enabled", config.failsafeFileCopy);
+ outGeneral["FailSafeFileCopy" ].attribute("Enabled", config.failSafeFileCopy);
outGeneral["CopyLockedFiles" ].attribute("Enabled", config.copyLockedFiles);
outGeneral["CopyFilePermissions" ].attribute("Enabled", config.copyFilePermissions);
outGeneral["AutomaticRetry" ].attribute("Count" , config.automaticRetryCount);
@@ -1379,7 +1380,8 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out)
outGeneral["LockDirectoriesDuringSync"].attribute("Enabled", config.createLockFile);
outGeneral["VerifyCopiedFiles" ].attribute("Enabled", config.verifyFileCopy);
outGeneral["LastSyncsLogSizeMax" ].attribute("Bytes" , config.lastSyncsLogFileSizeMax);
- outGeneral["NotificationSound" ].attribute("SyncComplete", config.soundFileSyncComplete);
+ outGeneral["NotificationSound" ].attribute("CompareFinished", config.soundFileCompareFinished);
+ outGeneral["NotificationSound" ].attribute("SyncFinished" , config.soundFileSyncFinished);
XmlOut outOpt = outGeneral["OptionalDialogs"];
outOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warningUnresolvedConflicts);
diff --git a/FreeFileSync/Source/lib/process_xml.h b/FreeFileSync/Source/lib/process_xml.h
index 8e72f71c..6e06e795 100644
--- a/FreeFileSync/Source/lib/process_xml.h
+++ b/FreeFileSync/Source/lib/process_xml.h
@@ -140,7 +140,7 @@ struct XmlGlobalSettings
//---------------------------------------------------------------------
//Shared (GUI/BATCH) settings
wxLanguage programLanguage = zen::getSystemLanguage();
- bool failsafeFileCopy = true;
+ bool failSafeFileCopy = true;
bool copyLockedFiles = false; //safer default: avoid copies of partially written files
bool copyFilePermissions = false;
size_t automaticRetryCount = 0;
@@ -152,7 +152,8 @@ struct XmlGlobalSettings
bool createLockFile = true;
bool verifyFileCopy = false;
size_t lastSyncsLogFileSizeMax = 100000; //maximum size for LastSyncs.log: use a human-readable number
- Zstring soundFileSyncComplete = Zstr("gong.wav");
+ Zstring soundFileCompareFinished;
+ Zstring soundFileSyncFinished= Zstr("gong.wav");
OptionalDialogs optDialogs;
diff --git a/FreeFileSync/Source/lib/resolve_path.cpp b/FreeFileSync/Source/lib/resolve_path.cpp
index 371d3ee3..f51435c0 100644
--- a/FreeFileSync/Source/lib/resolve_path.cpp
+++ b/FreeFileSync/Source/lib/resolve_path.cpp
@@ -511,7 +511,7 @@ void getDirectoryAliasesRecursive(const Zstring& dirpath, std::set<Zstring, Less
addEnvVar(L"WinDir"); // C:\Windows
addEnvVar(L"Temp"); // C:\Windows\Temp
- //add CSIDL values: http://msdn.microsoft.com/en-us/library/bb762494(v=vs.85).aspx
+ //add CSIDL values: https://msdn.microsoft.com/en-us/library/bb762494
const auto& csidlMap = CsidlConstants::get();
envToDir.insert(csidlMap.begin(), csidlMap.end());
@@ -601,25 +601,158 @@ Zstring zen::getResolvedFilePath(const Zstring& pathPhrase) //noexcept
#ifdef ZEN_WIN
-void zen::loginNetworkShare(const Zstring& dirpathOrig, bool allowUserInteraction) //throw() - user interaction: show OS password prompt
+namespace
+{
+//1. let's not keep state whether connections are established or not
+//2. show only a single login dialog per network share
+//3. show login dialogs for the same network address but different share one after another: usually passing the first login will auto-login the rest!
+class NetworkConnector
+{
+public:
+ static NetworkConnector& getInstance()
+ {
+ static NetworkConnector inst;
+ return inst; //meyers singleton: avoid static initialization order problem in global namespace!
+ }
+
+ struct Credentials
+ {
+ Zstring localName; //optional
+ Zstring remoteName;
+ bool allowUserInteraction = false;
+ };
+
+ void establishConnection(const Credentials& cred) //throw FileError
+ {
+ std::unique_lock<std::mutex> dummy(lockConnections);
+
+ auto it = activeLogins.find(cred);
+ if (it != activeLogins.end())
+ {
+ auto fut = it->second;
+ dummy.unlock(); //wait *outside* the lock!
+ return fut.get(); //throw FileError
+ }
+ else
+ {
+ std::promise<void> connectShare;
+ activeLogins.emplace(cred, connectShare.get_future());
+
+ //get or create mutex associated with the network address:
+ const Zstring networkName = beforeLast(cred.remoteName, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
+ NetworkLock& netLock = networkLocks[networkName];
+ ++netLock.shareCount;
+ std::mutex& netMutex = netLock.m;
+ dummy.unlock(); //do work *outside* the lock!
+
+ ZEN_ON_SCOPE_EXIT
+ (
+ dummy.lock();
+ activeLogins.erase(cred);
+ if (--netLock.shareCount == 0)
+ networkLocks.erase(networkName);
+ dummy.unlock();
+ );
+
+ try
+ {
+ std::lock_guard<std::mutex> dummy2(netMutex); //show one login dialog per network address at a time; ideally we should only serialize connection
+ //attempts that show the login dialog: fulfilled for deviceless connections in this context, but not necessarily for mapped network shares
+ connect(cred); //throw FileError
+ connectShare.set_value();
+ }
+ catch (FileError&)
+ {
+ connectShare.set_exception(std::current_exception());
+ throw;
+ }
+ }
+ }
+
+private:
+ NetworkConnector() {}
+ NetworkConnector (const NetworkConnector&) = delete;
+ NetworkConnector& operator=(const NetworkConnector&) = delete;
+
+ static void connect(const Credentials& cred); //throw FileError
+
+ std::mutex lockConnections;
+ std::map<Credentials, std::shared_future<void>> activeLogins;
+
+ struct NetworkLock
+ {
+ std::mutex m;
+ int shareCount = 0;
+ };
+ std::map<Zstring, NetworkLock, LessFilePath> networkLocks;
+};
+
+
+bool operator<(const NetworkConnector::Credentials& lhs, const NetworkConnector::Credentials& rhs)
+{
+ int rv = cmpFilePath(lhs.localName.c_str(), lhs.localName.size(),
+ rhs.localName.c_str(), rhs.localName.size());
+ if (rv != 0) return rv < 0;
+
+ rv = cmpFilePath(lhs.remoteName.c_str(), lhs.remoteName.size(),
+ rhs.remoteName.c_str(), rhs.remoteName.size());
+ if (rv != 0) return rv < 0;
+
+ return static_cast<int>(lhs.allowUserInteraction) < static_cast<int>(rhs.allowUserInteraction);
+}
+
+
+//blocks heavily if network is not reachable!!!
+void NetworkConnector::connect(const Credentials& cred) //throw FileError
+{
+ NETRESOURCE trgRes = {};
+ trgRes.dwType = RESOURCETYPE_DISK;
+ assert(!cred.remoteName.empty());
+ trgRes.lpRemoteName = const_cast<LPWSTR>(cred.remoteName.c_str()); //
+ if (!cred.localName.empty())
+ trgRes.lpLocalName = const_cast<LPWSTR>(cred.localName.c_str()); //lpNetResource is marked "__in", seems WNetAddConnection2 is not const correct!
+
+ const DWORD rv = ::WNetAddConnection2(&trgRes, //__in LPNETRESOURCE lpNetResource,
+ nullptr, //__in LPCTSTR lpPassword,
+ nullptr, //__in LPCTSTR lpUsername,
+ cred.allowUserInteraction ? CONNECT_INTERACTIVE : 0); //__in DWORD dwFlags
+ if (rv != NO_ERROR)
+ throw FileError(replaceCpy(_("Unable to connect to %x."), L"%x", fmtPath(trgRes.lpRemoteName)), formatSystemError(L"WNetAddConnection2", rv));
+ // 53 ERROR_BAD_NETPATH
+ // 67 ERROR_BAD_NET_NAME
+ // 86 ERROR_INVALID_PASSWORD
+ //1219 ERROR_SESSION_CREDENTIAL_CONFLICT Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again.
+ //1326 ERROR_LOGON_FAILURE Logon failure: unknown user name or bad password.
+ //1236 ERROR_CONNECTION_ABORTED
+}
+}
+
+
+void zen::connectNetworkShare(const Zstring& dirpathOrig, bool allowUserInteraction) //throw FileError
{
/*
- ATTENTION: it is not safe to retrieve UNC path via ::WNetGetConnection() for every type of network share:
-
- network type |::WNetGetConnection rv | lpRemoteName | existing UNC path
- -----------------------------|-------------------------|---------------------------------|----------------
- inactive local network share | ERROR_CONNECTION_UNAVAIL| \\192.168.1.27\new2 | YES
- WebDrive | NO_ERROR | \\Webdrive-ZenJu\GNU | NO
- Box.net (WebDav) | NO_ERROR | \\www.box.net\DavWWWRoot\dav | YES
- NetDrive | ERROR_NOT_CONNECTED | <empty> | NO
+ ----------------------
+ |Mapped Network Share|
+ ----------------------
+ test if unconnected: WNetGetConnection + ERROR_CONNECTION_UNAVAIL (can't use GetFileAttributes: returns ERROR_PATH_NOT_FOUND)
+ Password-proteced?
+ Windows remembers credentials?
+
+ -----------------------
+ |Deviceless Connection|
+ -----------------------
+ test if unconnected: GetFileAttributes + ERROR_LOGON_FAILURE => looks like GetFileAttributes() internally tries to establish the connection!
+ Password-proteced? yes, otherwise GetFileAttributes would have succeeded
+ Windows remembers credentials? no, otherwise GetFileAttributes would have succeeded
____________________________________________________________________________________________________________
Windows Login Prompt Naming Conventions:
- network share: \\<server>\<share> e.g. \\WIN-XP\folder or \\192.168.1.50\folder
- user account: <Domain>\<user> e.g. WIN-XP\Zenju or 192.168.1.50\Zenju
+ network share: \\<server>\<share> e.g. \\WIN7-VM\folder or \\192.168.1.50\folder
+ user account: <Domain>\<user> e.g. WIN7-VM\Zenju or 192.168.1.50\Zenju
+ Note: the remote login user must be different from the active login user of the network share (at least for Win7-hosted share)!
Windows Command Line:
- - list *all* active network connections, including deviceless ones which are hidden in Explorer:
+ - list all active network connections, including deviceless ones which are hidden in Explorer:
net use
- delete active connection:
net use /delete \\server\share
@@ -630,51 +763,20 @@ void zen::loginNetworkShare(const Zstring& dirpathOrig, bool allowUserInteractio
Problems:
I. WNetAddConnection2() allows (at least certain) invalid credentials (e.g. username: a/password: a) and establishes an *unusable* connection
II. WNetAddConnection2() refuses to overwrite an existing (unusable) connection created in I), but shows prompt repeatedly
- III. WNetAddConnection2() won't bring up the prompt if *wrong* credentials had been entered just recently, even with CONNECT_INTERACTIVE specified! => 2-step proccess
- */
+ III. WNetAddConnection2() won't bring up the prompt if *wrong* credentials had been entered just recently, even with CONNECT_INTERACTIVE specified!
+ ____________________________________________________________________________________________________________
- auto connect = [&](NETRESOURCE& trgRes) //blocks heavily if network is not reachable!!!
- {
- //1. first try to connect without user interaction - blocks!
- DWORD rv = ::WNetAddConnection2(&trgRes, //__in LPNETRESOURCE lpNetResource,
- nullptr, //__in LPCTSTR lpPassword,
- nullptr, //__in LPCTSTR lpUsername,
- 0); //__in DWORD dwFlags
- //53L ERROR_BAD_NETPATH The network path was not found.
- //67L ERROR_BAD_NET_NAME
- //86L ERROR_INVALID_PASSWORD
- //1219L ERROR_SESSION_CREDENTIAL_CONFLICT Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again.
- //1326L ERROR_LOGON_FAILURE Logon failure: unknown user name or bad password.
- //1236L ERROR_CONNECTION_ABORTED
- if (somethingExists(trgRes.lpRemoteName)) //blocks!
- return; //success: connection usable! -> don't care about "rv"
-
- if (rv == ERROR_BAD_NETPATH || //like ERROR_PATH_NOT_FOUND
- rv == ERROR_BAD_NET_NAME|| //like ERROR_FILE_NOT_FOUND
- rv == ERROR_CONNECTION_ABORTED) //failed to connect to a network that existed not too long ago; will later return ERROR_BAD_NETPATH
- return; //no need to show a prompt for an unreachable network device
-
- //2. if first attempt failed, we need to *force* prompt by using CONNECT_PROMPT
- if (allowUserInteraction)
- {
- //avoid problem II.)
- DWORD rv2= ::WNetCancelConnection2(trgRes.lpRemoteName, //_In_ LPCTSTR lpName,
- 0, //_In_ DWORD dwFlags,
- true); //_In_ BOOL fForce
- //2250L ERROR_NOT_CONNECTED
-
- //enforce login prompt
- DWORD rv3 = ::WNetAddConnection2(&trgRes, // __in LPNETRESOURCE lpNetResource,
- nullptr, // __in LPCTSTR lpPassword,
- nullptr, // __in LPCTSTR lpUsername,
- CONNECT_INTERACTIVE | CONNECT_PROMPT); //__in DWORD dwFlags
- (void)rv2;
- (void)rv3;
- }
- };
+ ATTENTION: ::WNetGetConnection() does not return a valid UNC path for every type of network share:
+ network type |::WNetGetConnection rv | lpRemoteName | existing UNC path
+ ------------------------------|-------------------------|---------------------------------|----------------
+ inactive mapped network share | ERROR_CONNECTION_UNAVAIL| \\192.168.1.27\new2 | YES
+ WebDrive | NO_ERROR | \\Webdrive-ZenJu\GNU | NO
+ Box.net (WebDav) | NO_ERROR | \\www.box.net\DavWWWRoot\dav | YES
+ NetDrive | ERROR_NOT_CONNECTED | <empty> | NO
+ */
- Zstring dirpath = removeLongPathPrefix(dirpathOrig);
+ Zstring dirpath = removeLongPathPrefix(dirpathOrig); //\\?\C:\path is not a UNC path: https://msdn.microsoft.com/en-us/library/windows/desktop/bb773712
trim(dirpath, true, false);
//1. locally mapped network share
@@ -684,26 +786,22 @@ void zen::loginNetworkShare(const Zstring& dirpathOrig, bool allowUserInteractio
{
DWORD bufferSize = 10000;
std::vector<wchar_t> remoteNameBuffer(bufferSize);
-
- //map local -> remote
-
- //note: following function call does NOT block!
+ //note: THE following call does NOT block!
DWORD rv = ::WNetGetConnection(driveLetter.c_str(), //__in LPCTSTR lpLocalName in the form "<driveletter>:"
&remoteNameBuffer[0], //__out LPTSTR lpRemoteName,
&bufferSize); //__inout LPDWORD lpnLength
if (rv == ERROR_CONNECTION_UNAVAIL) //remoteNameBuffer will be filled nevertheless!
{
- //ERROR_CONNECTION_UNAVAIL: network mapping is existing, but not connected
-
+ //1201 ERROR_CONNECTION_UNAVAIL: "device is not currently connected but it is a remembered connection."
+ //2250 ERROR_NOT_CONNECTED: "This network connection does not exist"
Zstring networkShare = &remoteNameBuffer[0];
if (!networkShare.empty())
{
- NETRESOURCE trgRes = {};
- trgRes.dwType = RESOURCETYPE_DISK;
- trgRes.lpLocalName = const_cast<LPWSTR>(driveLetter.c_str()); //lpNetResource is marked "__in", seems WNetAddConnection2 is not const correct!
- trgRes.lpRemoteName = const_cast<LPWSTR>(networkShare.c_str()); //
-
- connect(trgRes); //blocks!
+ NetworkConnector::Credentials cred = {};
+ cred.localName = driveLetter;
+ cred.remoteName = networkShare;
+ cred.allowUserInteraction = allowUserInteraction;
+ NetworkConnector::getInstance().establishConnection(cred); //throw FileError
}
}
}
@@ -722,14 +820,15 @@ void zen::loginNetworkShare(const Zstring& dirpathOrig, bool allowUserInteractio
if (!networkShare.empty())
{
- //::WNetGetResourceInformation seems to fail with ERROR_BAD_NET_NAME even for existing unconnected network shares!
- // => unconditionally try to connect to network share, seems we cannot reliably detect connection status otherwise
-
- NETRESOURCE trgRes = {};
- trgRes.dwType = RESOURCETYPE_DISK;
- trgRes.lpRemoteName = const_cast<LPWSTR>(networkShare.c_str()); //trgRes is "__in"
-
- connect(trgRes); //blocks!
+ //test if network share is connected: WNetGetResourceInformation() seems to fail with ERROR_BAD_NET_NAME even for existing unconnected network shares! => alternative:
+ const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(networkShare).c_str());
+ if (attr == INVALID_FILE_ATTRIBUTES && ::GetLastError() == ERROR_LOGON_FAILURE)
+ {
+ NetworkConnector::Credentials cred = {};
+ cred.remoteName = networkShare;
+ cred.allowUserInteraction = allowUserInteraction;
+ NetworkConnector::getInstance().establishConnection(cred); //throw FileError
+ }
}
}
}
diff --git a/FreeFileSync/Source/lib/resolve_path.h b/FreeFileSync/Source/lib/resolve_path.h
index 34d50360..d6ab29ad 100644
--- a/FreeFileSync/Source/lib/resolve_path.h
+++ b/FreeFileSync/Source/lib/resolve_path.h
@@ -31,7 +31,7 @@ std::vector<Zstring> getDirectoryAliases(const Zstring& folderPathPhrase); //may
#ifdef ZEN_WIN
//*blocks* if network is not reachable or when showing login prompt dialog!
- void loginNetworkShare(const Zstring& dirpath, bool allowUserInteraction); //noexcept; user interaction: show OS password prompt
+ void connectNetworkShare(const Zstring& dirpath, bool allowUserInteraction /*show OS password prompt*/); //throw FileError
#endif
}
diff --git a/FreeFileSync/Source/lib/status_handler.h b/FreeFileSync/Source/lib/status_handler.h
index 92956fb2..b2048329 100644
--- a/FreeFileSync/Source/lib/status_handler.h
+++ b/FreeFileSync/Source/lib/status_handler.h
@@ -84,7 +84,7 @@ protected:
if (!abortRequested) statusText_ = text;
requestUiRefresh(); /*throw X */
}
- void reportInfo (const std::wstring& text) override { assert(!text.empty()); if (!abortRequested) statusText_ = text; requestUiRefresh(); /*throw X */ } //log text in derived class
+ void reportInfo(const std::wstring& text) override { assert(!text.empty()); if (!abortRequested) statusText_ = text; requestUiRefresh(); /*throw X */ } //log text in derived class
//implement AbortCallback
void requestAbortion() override
diff --git a/FreeFileSync/Source/synchronization.cpp b/FreeFileSync/Source/synchronization.cpp
index 1d1eb1d1..093cc600 100644
--- a/FreeFileSync/Source/synchronization.cpp
+++ b/FreeFileSync/Source/synchronization.cpp
@@ -618,7 +618,7 @@ public:
SynchronizeFolderPair(ProcessCallback& procCallback,
bool verifyCopiedFiles,
bool copyFilePermissions,
- bool transactionalFileCopy,
+ bool failSafeFileCopy,
#ifdef ZEN_WIN
shadow::ShadowCopy* shadowCopyHandler,
#endif
@@ -632,16 +632,7 @@ public:
delHandlingRight_(delHandlingRight),
verifyCopiedFiles_(verifyCopiedFiles),
copyFilePermissions_(copyFilePermissions),
- transactionalFileCopy_(transactionalFileCopy),
- txtCreatingFile (_("Creating file %x" )),
- txtCreatingLink (_("Creating symbolic link %x" )),
- txtCreatingFolder (_("Creating folder %x" )),
- txtOverwritingFile (_("Updating file %x" )),
- txtOverwritingLink (_("Updating symbolic link %x")),
- txtVerifying (_("Verifying file %x" )),
- txtWritingAttributes(_("Updating attributes of %x" )),
- txtMovingFile (_("Moving file %x to %y"))
- {}
+ failSafeFileCopy_(failSafeFileCopy) {}
void startSync(BaseFolderPair& baseFolder)
{
@@ -707,17 +698,17 @@ private:
const bool verifyCopiedFiles_;
const bool copyFilePermissions_;
- const bool transactionalFileCopy_;
+ const bool failSafeFileCopy_;
//preload status texts
- const std::wstring txtCreatingFile;
- const std::wstring txtCreatingLink;
- const std::wstring txtCreatingFolder;
- const std::wstring txtOverwritingFile;
- const std::wstring txtOverwritingLink;
- const std::wstring txtVerifying;
- const std::wstring txtWritingAttributes;
- const std::wstring txtMovingFile;
+ const std::wstring txtCreatingFile {_("Creating file %x" )};
+ const std::wstring txtCreatingLink {_("Creating symbolic link %x")};
+ const std::wstring txtCreatingFolder {_("Creating folder %x" )};
+ const std::wstring txtOverwritingFile {_("Updating file %x" )};
+ const std::wstring txtOverwritingLink {_("Updating symbolic link %x")};
+ const std::wstring txtVerifying {_("Verifying file %x" )};
+ const std::wstring txtWritingAttributes{_("Updating attributes of %x")};
+ const std::wstring txtMovingFile {_("Moving file %x to %y" )};
};
//---------------------------------------------------------------------------------------------------------------
@@ -1282,7 +1273,7 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn
//if fail-safe file copy is active, then the next operation will be a simple "rename"
//=> don't risk reportStatus() throwing GuiAbortProcess() leaving the target deleted rather than updated!
- if (!transactionalFileCopy_)
+ if (!failSafeFileCopy_)
reportStatus(txtOverwritingFile, AFS::getDisplayPath(targetPathResolvedOld)); //restore status text copy file
};
@@ -1614,19 +1605,54 @@ void verifyFiles(const AbstractPath& sourcePath, const AbstractPath& targetPath,
if (Opt<Zstring> nativeTargetPath = AFS::getNativeItemPath(targetPath))
{
#ifdef ZEN_WIN
- const HANDLE fileHandle = ::CreateFile(applyLongPathPrefix(*nativeTargetPath).c_str(), //_In_ LPCTSTR lpFileName,
- GENERIC_WRITE | //_In_ DWORD dwDesiredAccess,
- GENERIC_READ, //=> request read-access, too, just like "copy /v" command
- FILE_SHARE_READ | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
- nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
- OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
- FILE_ATTRIBUTE_NORMAL, //_In_ DWORD dwFlagsAndAttributes,
- nullptr); //_In_opt_ HANDLE hTemplateFile
- if (fileHandle == INVALID_HANDLE_VALUE)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(*nativeTargetPath)), L"CreateFile");
- ZEN_ON_SCOPE_EXIT(::CloseHandle(fileHandle));
-
- if (!::FlushFileBuffers(fileHandle))
+ //temporarily reset read-only flag if required
+ DWORD attribs = INVALID_FILE_ATTRIBUTES;
+ ZEN_ON_SCOPE_EXIT(
+ if (attribs != INVALID_FILE_ATTRIBUTES)
+ ::SetFileAttributes(applyLongPathPrefix(*nativeTargetPath).c_str(), attribs);
+ );
+
+ auto openFile = [&]
+ {
+ return ::CreateFile(applyLongPathPrefix(*nativeTargetPath).c_str(), //_In_ LPCTSTR lpFileName,
+ GENERIC_WRITE | //_In_ DWORD dwDesiredAccess,
+ GENERIC_READ, //=> request read-access, too, just like "copy /v" command
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ FILE_ATTRIBUTE_NORMAL, //_In_ DWORD dwFlagsAndAttributes,
+ nullptr); //_In_opt_ HANDLE hTemplateFile
+ };
+
+ HANDLE hFile = openFile();
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls!
+
+ //CreateFile/FILE_SHARE_WRITE fails for read-only file => temporarily remove attribute
+ if (ec == ERROR_ACCESS_DENIED) //fails if file is read-only (or for "other" reasons)
+ {
+ const DWORD tmpAttr = ::GetFileAttributes(applyLongPathPrefix(*nativeTargetPath).c_str());
+ if (tmpAttr == INVALID_FILE_ATTRIBUTES)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(*nativeTargetPath)), L"GetFileAttributes");
+
+ if (tmpAttr & FILE_ATTRIBUTE_READONLY)
+ {
+ if (!::SetFileAttributes(applyLongPathPrefix(*nativeTargetPath).c_str(), FILE_ATTRIBUTE_NORMAL))
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(*nativeTargetPath)), L"SetFileAttributes");
+
+ attribs = tmpAttr; //reapplied on scope exit
+ hFile = openFile(); //without read-only this should work
+ ec = ::GetLastError();
+ }
+ }
+ //begin of "regular" error reporting
+ if (hFile == INVALID_HANDLE_VALUE)
+ throw FileError(replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(*nativeTargetPath)), formatSystemError(L"CreateFile", ec));
+ }
+ ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
+
+ if (!::FlushFileBuffers(hFile))
THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(*nativeTargetPath)), L"FlushFileBuffers");
#elif defined ZEN_LINUX || defined ZEN_MAC
@@ -1663,7 +1689,7 @@ AFS::FileAttribAfterCopy SynchronizeFolderPair::copyFileWithCallback(const Abstr
{
AFS::FileAttribAfterCopy newAttr = AFS::copyFileTransactional(sourcePathTmp, targetPath, //throw FileError, ErrorFileLocked
copyFilePermissions_,
- transactionalFileCopy_,
+ failSafeFileCopy_,
onDeleteTargetFile,
onNotifyCopyStatus);
@@ -1806,36 +1832,13 @@ void zen::synchronize(const TimeComp& timeStamp,
bool verifyCopiedFiles,
bool copyLockedFiles,
bool copyFilePermissions,
- bool transactionalFileCopy,
+ bool failSafeFileCopy,
bool runWithBackgroundPriority,
int folderAccessTimeout,
const std::vector<FolderPairSyncCfg>& syncConfig,
FolderComparison& folderCmp,
ProcessCallback& callback)
{
- //specify process and resource handling priorities
- std::unique_ptr<ScheduleForBackgroundProcessing> backgroundPrio;
- if (runWithBackgroundPriority)
- try
- {
- backgroundPrio = std::make_unique<ScheduleForBackgroundProcessing>(); //throw FileError
- }
- catch (const FileError& e) //not an error in this context
- {
- callback.reportInfo(e.toString()); //may throw!
- }
-
- //prevent operating system going into sleep state
- std::unique_ptr<PreventStandby> noStandby;
- try
- {
- noStandby = std::make_unique<PreventStandby>(); //throw FileError
- }
- catch (const FileError& e) //not an error in this context
- {
- callback.reportInfo(e.toString()); //may throw!
- }
-
//PERF_START;
if (syncConfig.size() != folderCmp.size())
@@ -1861,10 +1864,35 @@ void zen::synchronize(const TimeComp& timeStamp,
ProcessCallback::PHASE_SYNCHRONIZING);
}
- std::vector<FolderPairJobType> jobType(folderCmp.size(), FolderPairJobType::PROCESS); //folder pairs may be skipped after fatal errors were found
+ //-------------------------------------------------------------------------------
+
+ //specify process and resource handling priorities
+ std::unique_ptr<ScheduleForBackgroundProcessing> backgroundPrio;
+ if (runWithBackgroundPriority)
+ try
+ {
+ backgroundPrio = std::make_unique<ScheduleForBackgroundProcessing>(); //throw FileError
+ }
+ catch (const FileError& e) //not an error in this context
+ {
+ callback.reportInfo(e.toString()); //may throw!
+ }
+
+ //prevent operating system going into sleep state
+ std::unique_ptr<PreventStandby> noStandby;
+ try
+ {
+ noStandby = std::make_unique<PreventStandby>(); //throw FileError
+ }
+ catch (const FileError& e) //not an error in this context
+ {
+ callback.reportInfo(e.toString()); //may throw!
+ }
//-------------------execute basic checks all at once before starting sync--------------------------------------
+ std::vector<FolderPairJobType> jobType(folderCmp.size(), FolderPairJobType::PROCESS); //folder pairs may be skipped after fatal errors were found
+
std::vector<SyncStatistics::ConflictInfo> unresolvedConflicts;
//aggregate information
@@ -2225,7 +2253,7 @@ void zen::synchronize(const TimeComp& timeStamp,
callback);
- SynchronizeFolderPair syncFP(callback, verifyCopiedFiles, copyPermissionsFp, transactionalFileCopy,
+ SynchronizeFolderPair syncFP(callback, verifyCopiedFiles, copyPermissionsFp, failSafeFileCopy,
#ifdef ZEN_WIN
shadowCopyHandler.get(),
#endif
diff --git a/FreeFileSync/Source/synchronization.h b/FreeFileSync/Source/synchronization.h
index 795c6370..d066e65d 100644
--- a/FreeFileSync/Source/synchronization.h
+++ b/FreeFileSync/Source/synchronization.h
@@ -90,7 +90,7 @@ void synchronize(const TimeComp& timeStamp,
bool verifyCopiedFiles,
bool copyLockedFiles,
bool copyFilePermissions,
- bool transactionalFileCopy,
+ bool failSafeFileCopy,
bool runWithBackgroundPriority,
int folderAccessTimeout,
const std::vector<FolderPairSyncCfg>& syncConfig, //CONTRACT: syncConfig and folderCmp correspond row-wise!
diff --git a/FreeFileSync/Source/ui/custom_grid.cpp b/FreeFileSync/Source/ui/custom_grid.cpp
index 9e352d58..342d0a6a 100644
--- a/FreeFileSync/Source/ui/custom_grid.cpp
+++ b/FreeFileSync/Source/ui/custom_grid.cpp
@@ -517,7 +517,6 @@ private:
break;
}
-
if (fileIcon.IsOk())
{
wxRect rectIcon = rectTmp;
@@ -526,10 +525,10 @@ private:
auto drawIcon = [&](const wxBitmap& icon)
{
if (isActive)
- drawBitmapRtlNoMirror(dc, icon, rectIcon, wxALIGN_CENTER, this->buffer); //without "this->" GCC 4.7.2 compiler crash on Debian
+ drawBitmapRtlNoMirror(dc, icon, rectIcon, wxALIGN_CENTER);
else
drawBitmapRtlNoMirror(dc, wxBitmap(icon.ConvertToImage().ConvertToGreyscale(1.0 / 3, 1.0 / 3, 1.0 / 3)), //treat all channels equally!
- rectIcon, wxALIGN_CENTER, this->buffer);
+ rectIcon, wxALIGN_CENTER);
};
drawIcon(fileIcon);
@@ -542,22 +541,24 @@ private:
rectTmp.width -= iconSize;
}
- std::unique_ptr<wxDCTextColourChanger> dummy3;
- if (getRowDisplayType(row) != DisplayType::NORMAL)
- dummy3 = std::make_unique<wxDCTextColourChanger>(dc, *wxBLACK); //accessibility: always set both foreground AND background colors!
+ wxDCTextColourChanger dummy(dc);
+ if (!isActive)
+ dummy.Set(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
+ else if (getRowDisplayType(row) != DisplayType::NORMAL)
+ dummy.Set(*wxBLACK); //accessibility: always set both foreground AND background colors!
//draw text
if (static_cast<ColumnTypeRim>(colType) == ColumnTypeRim::SIZE && refGrid().GetLayoutDirection() != wxLayout_RightToLeft)
{
//have file size right-justified (but don't change for RTL languages)
rectTmp.width -= GAP_SIZE;
- drawCellText(dc, rectTmp, getValue(row, colType), isActive, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
+ drawCellText(dc, rectTmp, getValue(row, colType), wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
}
else
{
rectTmp.x += GAP_SIZE;
rectTmp.width -= GAP_SIZE;
- drawCellText(dc, rectTmp, getValue(row, colType), isActive, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
+ drawCellText(dc, rectTmp, getValue(row, colType), wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
}
}
@@ -618,8 +619,7 @@ private:
if (colType == static_cast<ColumnType>(sortInfo->type_) && (side == LEFT_SIDE) == sortInfo->onLeft_)
{
const wxBitmap& marker = getResourceImage(sortInfo->ascending_ ? L"sortAscending" : L"sortDescending");
- wxPoint markerBegin = rectInside.GetTopLeft() + wxPoint((rectInside.width - marker.GetWidth()) / 2, 0);
- dc.DrawBitmap(marker, markerBegin, true); //respect 2-pixel gap
+ drawBitmapRtlNoMirror(dc, marker, rectInside, wxALIGN_CENTER_HORIZONTAL);
}
}
}
@@ -715,7 +715,7 @@ private:
std::vector<char> failedLoads; //effectively a vector<bool> of size "number of rows"
- Opt<wxBitmap> buffer; //avoid costs of recreating this temporal variable
+ Opt<wxBitmap> renderBuf; //avoid costs of recreating this temporal variable
};
@@ -937,9 +937,9 @@ private:
const bool drawMouseHover = static_cast<HoverAreaCenter>(rowHover) == HoverAreaCenter::CHECK_BOX;
if (fsObj->isActive())
- drawBitmapRtlMirror(dc, getResourceImage(drawMouseHover ? L"checkbox_true_hover" : L"checkbox_true"), rect, wxALIGN_CENTER, buffer);
+ drawBitmapRtlMirror(dc, getResourceImage(drawMouseHover ? L"checkbox_true_hover" : L"checkbox_true"), rect, wxALIGN_CENTER, renderBuf);
else //default
- drawBitmapRtlMirror(dc, getResourceImage(drawMouseHover ? L"checkbox_false_hover" : L"checkbox_false"), rect, wxALIGN_CENTER, buffer);
+ drawBitmapRtlMirror(dc, getResourceImage(drawMouseHover ? L"checkbox_false_hover" : L"checkbox_false"), rect, wxALIGN_CENTER, renderBuf);
}
break;
@@ -957,14 +957,14 @@ private:
//wxWidgets screws up again and has wxALIGN_RIGHT off by one pixel! -> use wxALIGN_LEFT instead
const wxRect rectNotch(rectTmp.x + rectTmp.width - notch.GetWidth(), rectTmp.y, notch.GetWidth(), rectTmp.height);
- drawBitmapRtlMirror(dc, notch, rectNotch, wxALIGN_LEFT, buffer);
+ drawBitmapRtlMirror(dc, notch, rectNotch, wxALIGN_LEFT, renderBuf);
rectTmp.width -= notch.GetWidth();
}
if (!highlightSyncAction_)
- drawBitmapRtlMirror(dc, getCmpResultImage(fsObj->getCategory()), rectTmp, wxALIGN_CENTER, buffer);
+ drawBitmapRtlMirror(dc, getCmpResultImage(fsObj->getCategory()), rectTmp, wxALIGN_CENTER, renderBuf);
else if (fsObj->getCategory() != FILE_EQUAL) //don't show = in both middle columns
- drawBitmapRtlMirror(dc, greyScale(getCmpResultImage(fsObj->getCategory())), rectTmp, wxALIGN_CENTER, buffer);
+ drawBitmapRtlMirror(dc, greyScale(getCmpResultImage(fsObj->getCategory())), rectTmp, wxALIGN_CENTER, renderBuf);
}
break;
@@ -979,19 +979,19 @@ private:
switch (rowHoverCenter)
{
case HoverAreaCenter::DIR_LEFT:
- drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::LEFT)), rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, buffer);
+ drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::LEFT)), rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, renderBuf);
break;
case HoverAreaCenter::DIR_NONE:
- drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::NONE)), rect, wxALIGN_CENTER, buffer);
+ drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::NONE)), rect, wxALIGN_CENTER, renderBuf);
break;
case HoverAreaCenter::DIR_RIGHT:
- drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::RIGHT)), rect, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, buffer);
+ drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::RIGHT)), rect, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, renderBuf);
break;
case HoverAreaCenter::CHECK_BOX:
if (highlightSyncAction_)
- drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->getSyncOperation()), rect, wxALIGN_CENTER, buffer);
+ drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->getSyncOperation()), rect, wxALIGN_CENTER, renderBuf);
else if (fsObj->getSyncOperation() != SO_EQUAL) //don't show = in both middle columns
- drawBitmapRtlMirror(dc, greyScale(getSyncOpImage(fsObj->getSyncOperation())), rect, wxALIGN_CENTER, buffer);
+ drawBitmapRtlMirror(dc, greyScale(getSyncOpImage(fsObj->getSyncOperation())), rect, wxALIGN_CENTER, renderBuf);
break;
}
}
@@ -1059,7 +1059,7 @@ private:
drawColumnLabelBackground(dc, rectInside, highlighted);
const wxBitmap& cmpIcon = getResourceImage(L"compare_small");
- drawBitmapRtlNoMirror(dc, highlightSyncAction_ ? greyScale(cmpIcon) : cmpIcon, rectInside, wxALIGN_CENTER, buffer);
+ drawBitmapRtlNoMirror(dc, highlightSyncAction_ ? greyScale(cmpIcon) : cmpIcon, rectInside, wxALIGN_CENTER);
}
break;
@@ -1069,7 +1069,7 @@ private:
drawColumnLabelBackground(dc, rectInside, highlighted);
const wxBitmap& syncIcon = getResourceImage(L"sync_small");
- drawBitmapRtlNoMirror(dc, highlightSyncAction_ ? syncIcon : greyScale(syncIcon), rectInside, wxALIGN_CENTER, buffer);
+ drawBitmapRtlNoMirror(dc, highlightSyncAction_ ? syncIcon : greyScale(syncIcon), rectInside, wxALIGN_CENTER);
}
break;
}
@@ -1234,7 +1234,7 @@ private:
bool highlightSyncAction_ = false;
bool selectionInProgress = false;
- Opt<wxBitmap> buffer; //avoid costs of recreating this temporal variable
+ Opt<wxBitmap> renderBuf; //avoid costs of recreating this temporal variable
Tooltip toolTip;
wxImage notch;
};
diff --git a/FreeFileSync/Source/ui/folder_selector.cpp b/FreeFileSync/Source/ui/folder_selector.cpp
index f9123cf7..63a5f4f0 100644
--- a/FreeFileSync/Source/ui/folder_selector.cpp
+++ b/FreeFileSync/Source/ui/folder_selector.cpp
@@ -60,10 +60,12 @@ bool acceptShellItemPaths(const std::vector<Zstring>& shellItemPaths)
//accept files or folders from:
//- file system paths
//- MTP paths
-
- if (shellItemPaths.empty()) return false;
- return acceptsItemPathPhraseNative(shellItemPaths[0]) || //
- acceptsItemPathPhraseMtp (shellItemPaths[0]); //noexcept
+ return std::any_of(shellItemPaths.begin(), shellItemPaths.end(),
+ [&](const Zstring& shellItemPath)
+ {
+ return acceptsItemPathPhraseNative(shellItemPath) || //
+ acceptsItemPathPhraseMtp (shellItemPath); //noexcept
+ });
}
diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp
index ebd9d706..c217a408 100644
--- a/FreeFileSync/Source/ui/gui_generated.cpp
+++ b/FreeFileSync/Source/ui/gui_generated.cpp
@@ -42,6 +42,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
m_menuFile->AppendSeparator();
+ wxMenuItem* m_menuItem4;
m_menuItem4 = new wxMenuItem( m_menuFile, wxID_EXIT, wxString( _("E&xit") ) , wxEmptyString, wxITEM_NORMAL );
m_menuFile->Append( m_menuItem4 );
@@ -1078,18 +1079,22 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
wxBoxSizer* bSizer275;
bSizer275 = new wxBoxSizer( wxVERTICAL );
- bSizerLocalCompSettings = new wxBoxSizer( wxVERTICAL );
+ bSizerHeaderCompSettings = new wxBoxSizer( wxVERTICAL );
+
+ m_staticTextMainCompSettings = new wxStaticText( m_panelCompSettingsHolder, wxID_ANY, _("Main settings:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextMainCompSettings->Wrap( -1 );
+ bSizerHeaderCompSettings->Add( m_staticTextMainCompSettings, 0, wxALL, 10 );
m_checkBoxUseLocalCmpOptions = new wxCheckBox( m_panelCompSettingsHolder, wxID_ANY, _("Use local settings:"), wxDefaultPosition, wxDefaultSize, 0 );
m_checkBoxUseLocalCmpOptions->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- bSizerLocalCompSettings->Add( m_checkBoxUseLocalCmpOptions, 0, wxALL|wxEXPAND, 10 );
+ bSizerHeaderCompSettings->Add( m_checkBoxUseLocalCmpOptions, 0, wxALL|wxEXPAND, 10 );
- m_staticline59 = new wxStaticLine( m_panelCompSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizerLocalCompSettings->Add( m_staticline59, 0, wxEXPAND, 5 );
+ m_staticlineCompHeader = new wxStaticLine( m_panelCompSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizerHeaderCompSettings->Add( m_staticlineCompHeader, 0, wxEXPAND, 5 );
- bSizer275->Add( bSizerLocalCompSettings, 0, wxEXPAND, 5 );
+ bSizer275->Add( bSizerHeaderCompSettings, 0, wxEXPAND, 5 );
m_panelComparisonSettings = new wxPanel( m_panelCompSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panelComparisonSettings->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
@@ -1248,17 +1253,21 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
wxBoxSizer* bSizer278;
bSizer278 = new wxBoxSizer( wxVERTICAL );
- bSizerLocalFilterSettings = new wxBoxSizer( wxVERTICAL );
+ bSizerHeaderFilterSettings = new wxBoxSizer( wxVERTICAL );
+
+ m_staticTextMainFilterSettings = new wxStaticText( m_panelFilterSettingsHolder, wxID_ANY, _("Main settings:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextMainFilterSettings->Wrap( -1 );
+ bSizerHeaderFilterSettings->Add( m_staticTextMainFilterSettings, 0, wxALL, 10 );
- m_staticText144 = new wxStaticText( m_panelFilterSettingsHolder, wxID_ANY, _("Local settings:"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticText144->Wrap( -1 );
- bSizerLocalFilterSettings->Add( m_staticText144, 0, wxALL, 10 );
+ m_staticTextLocalFilterSettings = new wxStaticText( m_panelFilterSettingsHolder, wxID_ANY, _("Local settings:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextLocalFilterSettings->Wrap( -1 );
+ bSizerHeaderFilterSettings->Add( m_staticTextLocalFilterSettings, 0, wxALL, 10 );
- m_staticline61 = new wxStaticLine( m_panelFilterSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizerLocalFilterSettings->Add( m_staticline61, 0, wxEXPAND, 5 );
+ m_staticlineFilterHeader = new wxStaticLine( m_panelFilterSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizerHeaderFilterSettings->Add( m_staticlineFilterHeader, 0, wxEXPAND, 5 );
- bSizer278->Add( bSizerLocalFilterSettings, 0, wxEXPAND, 5 );
+ bSizer278->Add( bSizerHeaderFilterSettings, 0, wxEXPAND, 5 );
m_panelFilterSettings = new wxPanel( m_panelFilterSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panelFilterSettings->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
@@ -1472,16 +1481,20 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
wxBoxSizer* bSizer276;
bSizer276 = new wxBoxSizer( wxVERTICAL );
- bSizerLocalSyncSettings = new wxBoxSizer( wxVERTICAL );
+ bSizerHeaderSyncSettings = new wxBoxSizer( wxVERTICAL );
+
+ m_staticTextMainSyncSettings = new wxStaticText( m_panelSyncSettingsHolder, wxID_ANY, _("Main settings:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextMainSyncSettings->Wrap( -1 );
+ bSizerHeaderSyncSettings->Add( m_staticTextMainSyncSettings, 0, wxALL, 10 );
m_checkBoxUseLocalSyncOptions = new wxCheckBox( m_panelSyncSettingsHolder, wxID_ANY, _("Use local settings:"), wxDefaultPosition, wxDefaultSize, 0 );
- bSizerLocalSyncSettings->Add( m_checkBoxUseLocalSyncOptions, 0, wxALL|wxEXPAND, 10 );
+ bSizerHeaderSyncSettings->Add( m_checkBoxUseLocalSyncOptions, 0, wxALL|wxEXPAND, 10 );
- m_staticline60 = new wxStaticLine( m_panelSyncSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizerLocalSyncSettings->Add( m_staticline60, 0, wxEXPAND, 5 );
+ m_staticlineSyncHeader = new wxStaticLine( m_panelSyncSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizerHeaderSyncSettings->Add( m_staticlineSyncHeader, 0, wxEXPAND, 5 );
- bSizer276->Add( bSizerLocalSyncSettings, 0, wxEXPAND, 5 );
+ bSizer276->Add( bSizerHeaderSyncSettings, 0, wxEXPAND, 5 );
m_panelSyncSettings = new wxPanel( m_panelSyncSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panelSyncSettings->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
@@ -1523,13 +1536,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer236->Add( m_toggleBtnCustom, 0, wxEXPAND, 5 );
- bSizer235->Add( bSizer236, 0, wxRIGHT|wxLEFT, 5 );
-
- m_checkBoxDetectMove = new wxCheckBox( m_panelSyncSettings, wxID_ANY, _("Detect moved files"), wxDefaultPosition, wxDefaultSize, 0 );
- m_checkBoxDetectMove->SetValue(true);
- m_checkBoxDetectMove->SetToolTip( _("- Not supported by all file systems\n- Requires and creates database files\n- Detection not available for first sync") );
-
- bSizer235->Add( m_checkBoxDetectMove, 0, wxEXPAND|wxALL, 5 );
+ bSizer235->Add( bSizer236, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
bSizer237->Add( bSizer235, 0, wxALL, 5 );
@@ -1540,11 +1547,8 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
wxBoxSizer* bSizer238;
bSizer238 = new wxBoxSizer( wxVERTICAL );
- m_textCtrlSyncVarDescription = new wxTextCtrl( m_panelSyncSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER );
- bSizer238->Add( m_textCtrlSyncVarDescription, 1, wxEXPAND|wxLEFT, 5 );
- m_staticline43 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizer238->Add( m_staticline43, 0, wxEXPAND, 5 );
+ bSizer238->Add( 0, 0, 1, wxEXPAND, 5 );
bSizerSyncConfig = new wxBoxSizer( wxHORIZONTAL );
@@ -1653,7 +1657,34 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer238->Add( bSizerSyncConfig, 0, wxALL, 10 );
- bSizer237->Add( bSizer238, 1, wxEXPAND, 5 );
+ bSizer238->Add( 0, 0, 1, wxEXPAND, 5 );
+
+ m_staticline431 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer238->Add( m_staticline431, 0, wxEXPAND, 5 );
+
+ wxBoxSizer* bSizer201;
+ bSizer201 = new wxBoxSizer( wxHORIZONTAL );
+
+ m_checkBoxDetectMove = new wxCheckBox( m_panelSyncSettings, wxID_ANY, _("Detect moved files"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_checkBoxDetectMove->SetValue(true);
+ m_checkBoxDetectMove->SetToolTip( _("- Not supported by all file systems\n- Requires and creates database files\n- Detection not available for first sync") );
+
+ bSizer201->Add( m_checkBoxDetectMove, 0, wxALL|wxEXPAND, 5 );
+
+ m_hyperlink242 = new wxHyperlinkCtrl( m_panelSyncSettings, wxID_ANY, _("More information"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ bSizer201->Add( m_hyperlink242, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 );
+
+
+ bSizer238->Add( bSizer201, 0, wxALL, 5 );
+
+
+ bSizer237->Add( bSizer238, 0, wxEXPAND, 5 );
+
+ m_staticline531 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
+ bSizer237->Add( m_staticline531, 0, wxEXPAND, 5 );
+
+ m_textCtrlSyncVarDescription = new wxTextCtrl( m_panelSyncSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER );
+ bSizer237->Add( m_textCtrlSyncVarDescription, 1, wxLEFT|wxEXPAND, 5 );
bSizer232->Add( bSizer237, 0, wxEXPAND, 5 );
@@ -1661,38 +1692,42 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
m_staticline54 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bSizer232->Add( m_staticline54, 0, wxEXPAND, 5 );
- wxBoxSizer* bSizer184;
- bSizer184 = new wxBoxSizer( wxVERTICAL );
+ bSizerDelHandling = new wxBoxSizer( wxHORIZONTAL );
- wxBoxSizer* bSizer180;
- bSizer180 = new wxBoxSizer( wxHORIZONTAL );
+ wxBoxSizer* bSizer202;
+ bSizer202 = new wxBoxSizer( wxVERTICAL );
m_staticText87 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Delete files:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText87->Wrap( -1 );
- bSizer180->Add( m_staticText87, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 );
+ bSizer202->Add( m_staticText87, 0, wxALL, 5 );
- m_radioBtnPermanent = new wxRadioButton( m_panelSyncSettings, wxID_ANY, _("&Permanent"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP );
- m_radioBtnPermanent->SetToolTip( _("Delete or overwrite files permanently") );
+ m_bpButtonDeletionType = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW );
+ bSizer202->Add( m_bpButtonDeletionType, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
- bSizer180->Add( m_radioBtnPermanent, 0, wxTOP|wxBOTTOM|wxLEFT, 5 );
- m_radioBtnRecycler = new wxRadioButton( m_panelSyncSettings, wxID_ANY, _("&Recycle bin"), wxDefaultPosition, wxDefaultSize, 0 );
+ bSizerDelHandling->Add( bSizer202, 0, 0, 5 );
+
+ wxBoxSizer* bSizer2011;
+ bSizer2011 = new wxBoxSizer( wxVERTICAL );
+
+ m_radioBtnRecycler = new wxRadioButton( m_panelSyncSettings, wxID_ANY, _("&Recycle bin"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP );
+ m_radioBtnRecycler->SetValue( true );
m_radioBtnRecycler->SetToolTip( _("Back up deleted and overwritten files in the recycle bin") );
- bSizer180->Add( m_radioBtnRecycler, 0, wxTOP|wxBOTTOM|wxLEFT, 5 );
+ bSizer2011->Add( m_radioBtnRecycler, 0, wxEXPAND|wxALL, 5 );
- m_radioBtnVersioning = new wxRadioButton( m_panelSyncSettings, wxID_ANY, _("&Versioning"), wxDefaultPosition, wxDefaultSize, 0 );
- m_radioBtnVersioning->SetToolTip( _("Move files to a user-defined folder") );
+ m_radioBtnPermanent = new wxRadioButton( m_panelSyncSettings, wxID_ANY, _("&Permanent"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_radioBtnPermanent->SetToolTip( _("Delete or overwrite files permanently") );
- bSizer180->Add( m_radioBtnVersioning, 0, wxALL, 5 );
+ bSizer2011->Add( m_radioBtnPermanent, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 );
+ m_radioBtnVersioning = new wxRadioButton( m_panelSyncSettings, wxID_ANY, _("&Versioning"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_radioBtnVersioning->SetToolTip( _("Move files to a user-defined folder") );
- bSizer184->Add( bSizer180, 0, 0, 5 );
+ bSizer2011->Add( m_radioBtnVersioning, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 );
- bSizerVersioning = new wxBoxSizer( wxHORIZONTAL );
- m_bpButtonDeletionType = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW );
- bSizerVersioning->Add( m_bpButtonDeletionType, 0, wxALIGN_CENTER_VERTICAL, 5 );
+ bSizerDelHandling->Add( bSizer2011, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_panelVersioning = new wxPanel( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panelVersioning->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
@@ -1717,13 +1752,25 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer156->Add( m_bpButtonSelectAltFolder, 0, wxEXPAND, 5 );
- bSizer191->Add( bSizer156, 0, wxEXPAND|wxBOTTOM, 5 );
+ bSizer191->Add( bSizer156, 0, wxEXPAND|wxALL, 5 );
- bSizer192 = new wxBoxSizer( wxHORIZONTAL );
+ wxBoxSizer* bSizer198;
+ bSizer198 = new wxBoxSizer( wxHORIZONTAL );
m_staticText93 = new wxStaticText( m_panelVersioning, wxID_ANY, _("Naming convention:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText93->Wrap( -1 );
- bSizer192->Add( m_staticText93, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ bSizer198->Add( m_staticText93, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 );
+
+
+ bSizer198->Add( 0, 0, 1, wxEXPAND, 5 );
+
+ m_hyperlink17 = new wxHyperlinkCtrl( m_panelVersioning, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
+ bSizer198->Add( m_hyperlink17, 0, 0, 5 );
+
+
+ bSizer191->Add( bSizer198, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+
+ bSizer192 = new wxBoxSizer( wxHORIZONTAL );
wxArrayString m_choiceVersioningStyleChoices;
m_choiceVersioningStyle = new wxChoice( m_panelVersioning, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceVersioningStyleChoices, 0 );
@@ -1750,62 +1797,45 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer192->Add( m_staticTextNamingCvtPart3, 0, wxALIGN_CENTER_VERTICAL, 5 );
- bSizer192->Add( 0, 0, 1, wxEXPAND, 5 );
-
- m_hyperlink17 = new wxHyperlinkCtrl( m_panelVersioning, wxID_ANY, _("Show examples"), wxEmptyString, wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE );
- bSizer192->Add( m_hyperlink17, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
-
-
- bSizer191->Add( bSizer192, 0, wxEXPAND, 5 );
+ bSizer191->Add( bSizer192, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
m_panelVersioning->SetSizer( bSizer191 );
m_panelVersioning->Layout();
bSizer191->Fit( m_panelVersioning );
- bSizerVersioning->Add( m_panelVersioning, 1, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
-
-
- bSizer184->Add( bSizerVersioning, 0, wxTOP|wxEXPAND, 5 );
+ bSizerDelHandling->Add( m_panelVersioning, 1, 0, 5 );
- bSizer232->Add( bSizer184, 0, wxALL|wxEXPAND, 10 );
-
- bSizerMiscConfig = new wxBoxSizer( wxVERTICAL );
+ bSizer232->Add( bSizerDelHandling, 0, wxALL|wxEXPAND, 5 );
m_staticline582 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizerMiscConfig->Add( m_staticline582, 0, wxEXPAND, 5 );
+ bSizer232->Add( m_staticline582, 0, wxEXPAND, 5 );
- wxBoxSizer* bSizer1732;
- bSizer1732 = new wxBoxSizer( wxHORIZONTAL );
-
- wxBoxSizer* bSizer174;
- bSizer174 = new wxBoxSizer( wxVERTICAL );
+ bSizerMiscConfig = new wxBoxSizer( wxHORIZONTAL );
m_staticText88 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Handle errors:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText88->Wrap( -1 );
- bSizer174->Add( m_staticText88, 0, wxBOTTOM, 5 );
+ bSizerMiscConfig->Add( m_staticText88, 0, wxTOP|wxBOTTOM|wxLEFT, 10 );
wxBoxSizer* bSizer175;
- bSizer175 = new wxBoxSizer( wxHORIZONTAL );
-
- m_radioBtnIgnoreErrors = new wxRadioButton( m_panelSyncSettings, wxID_ANY, _("&Ignore"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP );
- m_radioBtnIgnoreErrors->SetToolTip( _("Hide all error and warning messages") );
-
- bSizer175->Add( m_radioBtnIgnoreErrors, 0, wxTOP|wxBOTTOM|wxLEFT, 5 );
+ bSizer175 = new wxBoxSizer( wxVERTICAL );
- m_radioBtnPopupOnErrors = new wxRadioButton( m_panelSyncSettings, wxID_ANY, _("&Pop-up"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_radioBtnPopupOnErrors = new wxRadioButton( m_panelSyncSettings, wxID_ANY, _("&Pop-up"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP );
+ m_radioBtnPopupOnErrors->SetValue( true );
m_radioBtnPopupOnErrors->SetToolTip( _("Show pop-up on errors or warnings") );
- bSizer175->Add( m_radioBtnPopupOnErrors, 0, wxALL, 5 );
+ bSizer175->Add( m_radioBtnPopupOnErrors, 0, wxEXPAND|wxALL, 5 );
+ m_radioBtnIgnoreErrors = new wxRadioButton( m_panelSyncSettings, wxID_ANY, _("&Ignore"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_radioBtnIgnoreErrors->SetToolTip( _("Hide all error and warning messages") );
- bSizer174->Add( bSizer175, 0, 0, 5 );
+ bSizer175->Add( m_radioBtnIgnoreErrors, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
- bSizer1732->Add( bSizer174, 0, wxALL, 10 );
+ bSizerMiscConfig->Add( bSizer175, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_staticline57 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
- bSizer1732->Add( m_staticline57, 0, wxEXPAND, 5 );
+ bSizerMiscConfig->Add( m_staticline57, 0, wxEXPAND, 5 );
bSizerOnCompletion = new wxBoxSizer( wxVERTICAL );
@@ -1817,10 +1847,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizerOnCompletion->Add( m_comboBoxOnCompletion, 0, wxEXPAND, 5 );
- bSizer1732->Add( bSizerOnCompletion, 1, wxALL, 10 );
-
-
- bSizerMiscConfig->Add( bSizer1732, 1, wxEXPAND, 5 );
+ bSizerMiscConfig->Add( bSizerOnCompletion, 1, wxALL, 10 );
bSizer232->Add( bSizerMiscConfig, 1, wxEXPAND, 5 );
@@ -1894,21 +1921,22 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
m_toggleBtnUpdate->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnSyncUpdate ), NULL, this );
m_toggleBtnCustom->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( ConfigDlgGenerated::OnSyncCustomDouble ), NULL, this );
m_toggleBtnCustom->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnSyncCustom ), NULL, this );
- m_checkBoxDetectMove->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleDetectMovedFiles ), NULL, this );
m_bpButtonLeftOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnExLeftSideOnly ), NULL, this );
m_bpButtonLeftNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnLeftNewer ), NULL, this );
m_bpButtonDifferent->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnDifferent ), NULL, this );
m_bpButtonConflict->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnConflict ), NULL, this );
m_bpButtonRightNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnRightNewer ), NULL, this );
m_bpButtonRightOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnExRightSideOnly ), NULL, this );
- m_radioBtnPermanent->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnDeletionPermanent ), NULL, this );
+ m_checkBoxDetectMove->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleDetectMovedFiles ), NULL, this );
+ m_hyperlink242->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpDetectMovedFiles ), NULL, this );
+ m_bpButtonDeletionType->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleDeletionType ), NULL, this );
m_radioBtnRecycler->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnDeletionRecycler ), NULL, this );
+ m_radioBtnPermanent->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnDeletionPermanent ), NULL, this );
m_radioBtnVersioning->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnDeletionVersioning ), NULL, this );
- m_bpButtonDeletionType->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnToggleDeletionType ), NULL, this );
- m_choiceVersioningStyle->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeSyncOption ), NULL, this );
m_hyperlink17->Connect( wxEVT_COMMAND_HYPERLINK, wxHyperlinkEventHandler( ConfigDlgGenerated::OnHelpVersioning ), NULL, this );
- m_radioBtnIgnoreErrors->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnErrorIgnore ), NULL, this );
+ m_choiceVersioningStyle->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnChangeSyncOption ), NULL, this );
m_radioBtnPopupOnErrors->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnErrorPopup ), NULL, this );
+ m_radioBtnIgnoreErrors->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( ConfigDlgGenerated::OnErrorIgnore ), NULL, this );
m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnOkay ), NULL, this );
m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( ConfigDlgGenerated::OnCancel ), NULL, this );
}
@@ -3006,36 +3034,31 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
wxBoxSizer* bSizer180;
bSizer180 = new wxBoxSizer( wxHORIZONTAL );
- wxBoxSizer* bSizer171;
- bSizer171 = new wxBoxSizer( wxVERTICAL );
-
m_staticText82 = new wxStaticText( m_panel35, wxID_ANY, _("Handle errors:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText82->Wrap( -1 );
- bSizer171->Add( m_staticText82, 0, wxALL, 5 );
+ bSizer180->Add( m_staticText82, 0, wxTOP|wxBOTTOM|wxLEFT, 10 );
wxBoxSizer* bSizer169;
- bSizer169 = new wxBoxSizer( wxHORIZONTAL );
-
- m_radioBtnIgnoreErrors = new wxRadioButton( m_panel35, wxID_ANY, _("&Ignore"), wxDefaultPosition, wxDefaultSize, 0 );
- m_radioBtnIgnoreErrors->SetToolTip( _("Hide all error and warning messages") );
-
- bSizer169->Add( m_radioBtnIgnoreErrors, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer169 = new wxBoxSizer( wxVERTICAL );
m_radioBtnPopupOnErrors = new wxRadioButton( m_panel35, wxID_ANY, _("&Pop-up"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_radioBtnPopupOnErrors->SetValue( true );
m_radioBtnPopupOnErrors->SetToolTip( _("Show pop-up on errors or warnings") );
- bSizer169->Add( m_radioBtnPopupOnErrors, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer169->Add( m_radioBtnPopupOnErrors, 0, wxEXPAND|wxALL, 5 );
- m_radioBtnStopOnError = new wxRadioButton( m_panel35, wxID_ANY, _("&Stop"), wxDefaultPosition, wxDefaultSize, 0 );
- m_radioBtnStopOnError->SetToolTip( _("Stop synchronization at first error") );
+ m_radioBtnIgnoreErrors = new wxRadioButton( m_panel35, wxID_ANY, _("&Ignore"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_radioBtnIgnoreErrors->SetToolTip( _("Hide all error and warning messages") );
- bSizer169->Add( m_radioBtnStopOnError, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer169->Add( m_radioBtnIgnoreErrors, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+ m_radioBtnStopOnError = new wxRadioButton( m_panel35, wxID_ANY, _("&Stop"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_radioBtnStopOnError->SetToolTip( _("Stop synchronization at first error") );
- bSizer171->Add( bSizer169, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+ bSizer169->Add( m_radioBtnStopOnError, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
- bSizer180->Add( bSizer171, 0, wxALL, 5 );
+ bSizer180->Add( bSizer169, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_staticline26 = new wxStaticLine( m_panel35, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
bSizer180->Add( m_staticline26, 0, wxEXPAND, 5 );
@@ -3043,21 +3066,15 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
wxBoxSizer* bSizer170;
bSizer170 = new wxBoxSizer( wxVERTICAL );
- m_checkBoxRunMinimized = new wxCheckBox( m_panel35, wxID_ANY, _("Run minimized"), wxDefaultPosition, wxDefaultSize, 0 );
- bSizer170->Add( m_checkBoxRunMinimized, 0, wxEXPAND|wxALL, 5 );
-
- wxBoxSizer* bSizer179;
- bSizer179 = new wxBoxSizer( wxHORIZONTAL );
-
m_staticText81 = new wxStaticText( m_panel35, wxID_ANY, _("On completion:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText81->Wrap( -1 );
- bSizer179->Add( m_staticText81, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ bSizer170->Add( m_staticText81, 0, wxALL, 5 );
m_comboBoxOnCompletion = new OnCompletionBox( m_panel35, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
- bSizer179->Add( m_comboBoxOnCompletion, 1, wxALIGN_CENTER_VERTICAL, 5 );
-
+ bSizer170->Add( m_comboBoxOnCompletion, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
- bSizer170->Add( bSizer179, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+ m_checkBoxRunMinimized = new wxCheckBox( m_panel35, wxID_ANY, _("Run minimized"), wxDefaultPosition, wxDefaultSize, 0 );
+ bSizer170->Add( m_checkBoxRunMinimized, 0, wxEXPAND|wxALL, 5 );
bSizer180->Add( bSizer170, 1, wxALL, 5 );
@@ -3147,8 +3164,8 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
// Connect Events
this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( BatchDlgGenerated::OnClose ) );
- m_radioBtnIgnoreErrors->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( BatchDlgGenerated::OnErrorIgnore ), NULL, this );
m_radioBtnPopupOnErrors->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( BatchDlgGenerated::OnErrorPopup ), NULL, this );
+ m_radioBtnIgnoreErrors->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( BatchDlgGenerated::OnErrorIgnore ), NULL, this );
m_radioBtnStopOnError->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( BatchDlgGenerated::OnErrorStop ), NULL, this );
m_checkBoxGenerateLogfile->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnToggleGenerateLogfile ), NULL, this );
m_checkBoxLogfilesLimit->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnToggleLogfilesLimit ), NULL, this );
diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h
index 105a989f..842b7a10 100644
--- a/FreeFileSync/Source/ui/gui_generated.h
+++ b/FreeFileSync/Source/ui/gui_generated.h
@@ -74,7 +74,6 @@ protected:
wxMenuItem* m_menuItemSave;
wxMenuItem* m_menuItemSaveAs;
wxMenuItem* m_menuItemSaveAsBatch;
- wxMenuItem* m_menuItem4;
wxMenu* m_menu4;
wxMenuItem* m_menuItemCompare;
wxMenuItem* m_menuItemCompSettings;
@@ -271,9 +270,10 @@ protected:
wxListBox* m_listBoxFolderPair;
wxNotebook* m_notebook;
wxPanel* m_panelCompSettingsHolder;
- wxBoxSizer* bSizerLocalCompSettings;
+ wxBoxSizer* bSizerHeaderCompSettings;
+ wxStaticText* m_staticTextMainCompSettings;
wxCheckBox* m_checkBoxUseLocalCmpOptions;
- wxStaticLine* m_staticline59;
+ wxStaticLine* m_staticlineCompHeader;
wxPanel* m_panelComparisonSettings;
wxStaticText* m_staticText91;
wxStaticBitmap* m_bitmapByTimeSize;
@@ -298,9 +298,10 @@ protected:
wxStaticLine* m_staticline441;
wxStaticLine* m_staticline331;
wxPanel* m_panelFilterSettingsHolder;
- wxBoxSizer* bSizerLocalFilterSettings;
- wxStaticText* m_staticText144;
- wxStaticLine* m_staticline61;
+ wxBoxSizer* bSizerHeaderFilterSettings;
+ wxStaticText* m_staticTextMainFilterSettings;
+ wxStaticText* m_staticTextLocalFilterSettings;
+ wxStaticLine* m_staticlineFilterHeader;
wxPanel* m_panelFilterSettings;
wxStaticBitmap* m_bitmapInclude;
wxStaticText* m_staticText78;
@@ -329,19 +330,17 @@ protected:
wxStaticLine* m_staticline46;
wxButton* m_buttonClear;
wxPanel* m_panelSyncSettingsHolder;
- wxBoxSizer* bSizerLocalSyncSettings;
+ wxBoxSizer* bSizerHeaderSyncSettings;
+ wxStaticText* m_staticTextMainSyncSettings;
wxCheckBox* m_checkBoxUseLocalSyncOptions;
- wxStaticLine* m_staticline60;
+ wxStaticLine* m_staticlineSyncHeader;
wxPanel* m_panelSyncSettings;
wxStaticText* m_staticText86;
wxToggleButton* m_toggleBtnTwoWay;
wxToggleButton* m_toggleBtnMirror;
wxToggleButton* m_toggleBtnUpdate;
wxToggleButton* m_toggleBtnCustom;
- wxCheckBox* m_checkBoxDetectMove;
wxStaticLine* m_staticline53;
- wxTextCtrl* m_textCtrlSyncVarDescription;
- wxStaticLine* m_staticline43;
wxBoxSizer* bSizerSyncConfig;
wxStaticText* m_staticText119;
wxStaticText* m_staticText120;
@@ -359,28 +358,33 @@ protected:
wxBitmapButton* m_bpButtonRightNewer;
wxBitmapButton* m_bpButtonRightOnly;
wxStaticBitmap* m_bitmapDatabase;
+ wxStaticLine* m_staticline431;
+ wxCheckBox* m_checkBoxDetectMove;
+ wxHyperlinkCtrl* m_hyperlink242;
+ wxStaticLine* m_staticline531;
+ wxTextCtrl* m_textCtrlSyncVarDescription;
wxStaticLine* m_staticline54;
+ wxBoxSizer* bSizerDelHandling;
wxStaticText* m_staticText87;
- wxRadioButton* m_radioBtnPermanent;
+ wxBitmapButton* m_bpButtonDeletionType;
wxRadioButton* m_radioBtnRecycler;
+ wxRadioButton* m_radioBtnPermanent;
wxRadioButton* m_radioBtnVersioning;
- wxBoxSizer* bSizerVersioning;
- wxBitmapButton* m_bpButtonDeletionType;
wxPanel* m_panelVersioning;
FolderHistoryBox* m_versioningFolderPath;
wxButton* m_buttonSelectVersioningFolder;
- wxBoxSizer* bSizer192;
wxStaticText* m_staticText93;
+ wxHyperlinkCtrl* m_hyperlink17;
+ wxBoxSizer* bSizer192;
wxChoice* m_choiceVersioningStyle;
wxStaticText* m_staticTextNamingCvtPart1;
wxStaticText* m_staticTextNamingCvtPart2Bold;
wxStaticText* m_staticTextNamingCvtPart3;
- wxHyperlinkCtrl* m_hyperlink17;
- wxBoxSizer* bSizerMiscConfig;
wxStaticLine* m_staticline582;
+ wxBoxSizer* bSizerMiscConfig;
wxStaticText* m_staticText88;
- wxRadioButton* m_radioBtnIgnoreErrors;
wxRadioButton* m_radioBtnPopupOnErrors;
+ wxRadioButton* m_radioBtnIgnoreErrors;
wxStaticLine* m_staticline57;
wxBoxSizer* bSizerOnCompletion;
wxStaticText* m_staticText89;
@@ -416,21 +420,22 @@ protected:
virtual void OnSyncUpdate( wxCommandEvent& event ) { event.Skip(); }
virtual void OnSyncCustomDouble( wxMouseEvent& event ) { event.Skip(); }
virtual void OnSyncCustom( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnToggleDetectMovedFiles( wxCommandEvent& event ) { event.Skip(); }
virtual void OnExLeftSideOnly( wxCommandEvent& event ) { event.Skip(); }
virtual void OnLeftNewer( wxCommandEvent& event ) { event.Skip(); }
virtual void OnDifferent( wxCommandEvent& event ) { event.Skip(); }
virtual void OnConflict( wxCommandEvent& event ) { event.Skip(); }
virtual void OnRightNewer( wxCommandEvent& event ) { event.Skip(); }
virtual void OnExRightSideOnly( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnDeletionPermanent( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnToggleDetectMovedFiles( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnHelpDetectMovedFiles( wxHyperlinkEvent& event ) { event.Skip(); }
+ virtual void OnToggleDeletionType( wxCommandEvent& event ) { event.Skip(); }
virtual void OnDeletionRecycler( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnDeletionPermanent( wxCommandEvent& event ) { event.Skip(); }
virtual void OnDeletionVersioning( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnToggleDeletionType( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnChangeSyncOption( wxCommandEvent& event ) { event.Skip(); }
virtual void OnHelpVersioning( wxHyperlinkEvent& event ) { event.Skip(); }
- virtual void OnErrorIgnore( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnChangeSyncOption( wxCommandEvent& event ) { event.Skip(); }
virtual void OnErrorPopup( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnErrorIgnore( wxCommandEvent& event ) { event.Skip(); }
virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); }
virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); }
@@ -721,13 +726,13 @@ protected:
wxStaticLine* m_staticline18;
wxPanel* m_panel35;
wxStaticText* m_staticText82;
- wxRadioButton* m_radioBtnIgnoreErrors;
wxRadioButton* m_radioBtnPopupOnErrors;
+ wxRadioButton* m_radioBtnIgnoreErrors;
wxRadioButton* m_radioBtnStopOnError;
wxStaticLine* m_staticline26;
- wxCheckBox* m_checkBoxRunMinimized;
wxStaticText* m_staticText81;
OnCompletionBox* m_comboBoxOnCompletion;
+ wxCheckBox* m_checkBoxRunMinimized;
wxStaticLine* m_staticline25;
wxCheckBox* m_checkBoxGenerateLogfile;
wxPanel* m_panelLogfile;
@@ -742,8 +747,8 @@ protected:
// Virtual event handlers, overide them in your derived class
virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
- virtual void OnErrorIgnore( wxCommandEvent& event ) { event.Skip(); }
virtual void OnErrorPopup( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnErrorIgnore( wxCommandEvent& event ) { event.Skip(); }
virtual void OnErrorStop( wxCommandEvent& event ) { event.Skip(); }
virtual void OnToggleGenerateLogfile( wxCommandEvent& event ) { event.Skip(); }
virtual void OnToggleLogfilesLimit( wxCommandEvent& event ) { event.Skip(); }
diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp
index 9335560b..82f0b707 100644
--- a/FreeFileSync/Source/ui/gui_status_handler.cpp
+++ b/FreeFileSync/Source/ui/gui_status_handler.cpp
@@ -146,32 +146,52 @@ void StatusHandlerTemporaryPanel::initNewPhase(int objectsTotal, std::int64_t da
}
+void StatusHandlerTemporaryPanel::reportInfo(const std::wstring& text)
+{
+ StatusHandler::reportInfo(text);
+ errorLog.logMsg(text, TYPE_INFO);
+}
+
+
ProcessCallback::Response StatusHandlerTemporaryPanel::reportError(const std::wstring& errorMessage, size_t retryNumber)
{
//no need to implement auto-retry here: 1. user is watching 2. comparison is fast
//=> similar behavior like "ignoreErrors" which is also not used for the comparison phase in GUI mode
- if (ignoreErrors)
- return ProcessCallback::IGNORE_ERROR;
-
- forceUiRefresh();
+ //always, except for "retry":
+ auto guardWriteLog = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { errorLog.logMsg(errorMessage, TYPE_ERROR); });
- bool ignoreNextErrors = false;
- switch (showConfirmationDialog3(&mainDlg, DialogInfoType::ERROR2, PopupDialogCfg3().
- setDetailInstructions(errorMessage).
- setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT),
- _("&Ignore"), _("&Retry")))
+ switch (handleError_)
{
- case ConfirmationButton3::DO_IT: //ignore
- ignoreErrors = ignoreNextErrors;
- return ProcessCallback::IGNORE_ERROR;
+ case ON_GUIERROR_POPUP:
+ {
+ forceUiRefresh();
+
+ bool ignoreNextErrors = false;
+ switch (showConfirmationDialog3(&mainDlg, DialogInfoType::ERROR2, PopupDialogCfg3().
+ setDetailInstructions(errorMessage).
+ setCheckBox(ignoreNextErrors, _("&Ignore subsequent errors"), ConfirmationButton3::DONT_DO_IT),
+ _("&Ignore"), _("&Retry")))
+ {
+ case ConfirmationButton3::DO_IT: //ignore
+ if (ignoreNextErrors) //falsify only
+ handleError_ = ON_GUIERROR_IGNORE;
+ return ProcessCallback::IGNORE_ERROR;
- case ConfirmationButton3::DONT_DO_IT: //retry
- return ProcessCallback::RETRY;
+ case ConfirmationButton3::DONT_DO_IT: //retry
+ guardWriteLog.dismiss();
+ errorLog.logMsg(errorMessage + L"\n-> " + _("Retrying operation..."), TYPE_INFO); //explain why there are duplicate "doing operation X" info messages in the log!
+ return ProcessCallback::RETRY;
- case ConfirmationButton3::CANCEL:
- abortProcessNow();
- break;
+ case ConfirmationButton3::CANCEL:
+ abortProcessNow();
+ break;
+ }
+ }
+ break;
+
+ case ON_GUIERROR_IGNORE:
+ return ProcessCallback::IGNORE_ERROR;
}
assert(false);
@@ -181,6 +201,8 @@ ProcessCallback::Response StatusHandlerTemporaryPanel::reportError(const std::ws
void StatusHandlerTemporaryPanel::reportFatalError(const std::wstring& errorMessage)
{
+ errorLog.logMsg(errorMessage, TYPE_FATAL_ERROR);
+
forceUiRefresh();
showNotificationDialog(&mainDlg, DialogInfoType::ERROR2, PopupDialogCfg().setTitle(_("Serious Error")).setDetailInstructions(errorMessage));
}
@@ -188,24 +210,35 @@ void StatusHandlerTemporaryPanel::reportFatalError(const std::wstring& errorMess
void StatusHandlerTemporaryPanel::reportWarning(const std::wstring& warningMessage, bool& warningActive)
{
- if (!warningActive || ignoreErrors) //if errors are ignored, then warnings should also
- return;
+ errorLog.logMsg(warningMessage, TYPE_WARNING);
- forceUiRefresh();
+ if (!warningActive) //if errors are ignored, then warnings should also
+ return;
- //show pop-up and ask user how to handle warning
- bool dontWarnAgain = false;
- switch (showConfirmationDialog(&mainDlg, DialogInfoType::WARNING,
- PopupDialogCfg().setDetailInstructions(warningMessage).
- setCheckBox(dontWarnAgain, _("&Don't show this warning again")),
- _("&Ignore")))
+ switch (handleError_)
{
- case ConfirmationButton::DO_IT:
- warningActive = !dontWarnAgain;
- break;
- case ConfirmationButton::CANCEL:
- abortProcessNow();
- break;
+ case ON_GUIERROR_POPUP:
+ {
+ forceUiRefresh();
+
+ bool dontWarnAgain = false;
+ switch (showConfirmationDialog(&mainDlg, DialogInfoType::WARNING,
+ PopupDialogCfg().setDetailInstructions(warningMessage).
+ setCheckBox(dontWarnAgain, _("&Don't show this warning again")),
+ _("&Ignore")))
+ {
+ case ConfirmationButton::DO_IT:
+ warningActive = !dontWarnAgain;
+ break;
+ case ConfirmationButton::CANCEL:
+ abortProcessNow();
+ break;
+ }
+ }
+ break;
+
+ case ON_GUIERROR_IGNORE:
+ break; //if errors are ignored, then warnings should also
}
}
@@ -216,16 +249,16 @@ void StatusHandlerTemporaryPanel::forceUiRefresh()
}
-void StatusHandlerTemporaryPanel::OnAbortCompare(wxCommandEvent& event)
+void StatusHandlerTemporaryPanel::abortProcessNow()
{
- requestAbortion();
+ requestAbortion(); //just make sure...
+ throw GuiAbortProcess();
}
-void StatusHandlerTemporaryPanel::abortProcessNow()
+void StatusHandlerTemporaryPanel::OnAbortCompare(wxCommandEvent& event)
{
- requestAbortion(); //just make sure...
- throw GuiAbortProcess();
+ requestAbortion();
}
//########################################################################################################
@@ -278,25 +311,25 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog()
}
//------------ end of sync: begin of cleanup --------------------------------------
- const int totalErrors = errorLog.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log
- const int totalWarnings = errorLog.getItemCount(TYPE_WARNING);
+ const int totalErrors = errorLog_.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR); //evaluate before finalizing log
+ const int totalWarnings = errorLog_.getItemCount(TYPE_WARNING);
//finalize error log
std::wstring finalStatus;
if (abortIsRequested())
{
finalStatus = _("Synchronization stopped");
- errorLog.logMsg(finalStatus, TYPE_ERROR);
+ errorLog_.logMsg(finalStatus, TYPE_ERROR);
}
else if (totalErrors > 0)
{
finalStatus = _("Synchronization completed with errors");
- errorLog.logMsg(finalStatus, TYPE_ERROR);
+ errorLog_.logMsg(finalStatus, TYPE_ERROR);
}
else if (totalWarnings > 0)
{
finalStatus = _("Synchronization completed with warnings");
- errorLog.logMsg(finalStatus, TYPE_WARNING); //give status code same warning priority as display category!
+ errorLog_.logMsg(finalStatus, TYPE_WARNING); //give status code same warning priority as display category!
}
else
{
@@ -305,7 +338,7 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog()
finalStatus = _("Nothing to synchronize"); //even if "ignored conflicts" occurred!
else
finalStatus = _("Synchronization completed successfully");
- errorLog.logMsg(finalStatus, TYPE_INFO);
+ errorLog_.logMsg(finalStatus, TYPE_INFO);
}
const SummaryInfo summary =
@@ -319,7 +352,7 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog()
//----------------- write results into LastSyncs.log------------------------
try
{
- saveToLastSyncsLog(summary, errorLog, lastSyncsLogFileSizeMax_, OnUpdateLogfileStatusNoThrow(*this, utfCvrtTo<std::wstring>(getLastSyncsLogfilePath()))); //throw FileError
+ saveToLastSyncsLog(summary, errorLog_, lastSyncsLogFileSizeMax_, OnUpdateLogfileStatusNoThrow(*this, utfCvrtTo<std::wstring>(getLastSyncsLogfilePath()))); //throw FileError
}
catch (FileError&) { assert(false); }
@@ -329,13 +362,13 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog()
if (showFinalResults)
{
if (abortIsRequested())
- progressDlg->processHasFinished(SyncProgressDialog::RESULT_ABORTED, errorLog); //enable okay and close events
+ progressDlg->processHasFinished(SyncProgressDialog::RESULT_ABORTED, errorLog_); //enable okay and close events
else if (totalErrors > 0)
- progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_ERROR, errorLog);
+ progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_ERROR, errorLog_);
else if (totalWarnings > 0)
- progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS, errorLog);
+ progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_WARNINGS, errorLog_);
else
- progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS, errorLog);
+ progressDlg->processHasFinished(SyncProgressDialog::RESULT_FINISHED_WITH_SUCCESS, errorLog_);
}
else
progressDlg->closeWindowDirectly();
@@ -375,7 +408,7 @@ void StatusHandlerFloatingDialog::updateProcessedData(int objectsDelta, std::int
void StatusHandlerFloatingDialog::reportInfo(const std::wstring& text)
{
StatusHandler::reportInfo(text);
- errorLog.logMsg(text, TYPE_INFO);
+ errorLog_.logMsg(text, TYPE_INFO);
}
@@ -384,8 +417,8 @@ ProcessCallback::Response StatusHandlerFloatingDialog::reportError(const std::ws
//auto-retry
if (retryNumber < automaticRetryCount_)
{
- errorLog.logMsg(errorMessage + L"\n-> " +
- _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", automaticRetryDelay_), TYPE_INFO);
+ errorLog_.logMsg(errorMessage + L"\n-> " +
+ _P("Automatic retry in 1 second...", "Automatic retry in %x seconds...", automaticRetryDelay_), TYPE_INFO);
//delay
const int iterations = static_cast<int>(1000 * automaticRetryDelay_ / UI_UPDATE_INTERVAL); //always round down: don't allow for negative remaining time below
for (int i = 0; i < iterations; ++i)
@@ -399,7 +432,7 @@ ProcessCallback::Response StatusHandlerFloatingDialog::reportError(const std::ws
//always, except for "retry":
- auto guardWriteLog = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { errorLog.logMsg(errorMessage, TYPE_ERROR); });
+ auto guardWriteLog = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { errorLog_.logMsg(errorMessage, TYPE_ERROR); });
switch (handleError_)
{
@@ -422,7 +455,7 @@ ProcessCallback::Response StatusHandlerFloatingDialog::reportError(const std::ws
case ConfirmationButton3::DONT_DO_IT: //retry
guardWriteLog.dismiss();
- errorLog.logMsg(errorMessage + L"\n-> " + _("Retrying operation..."), TYPE_INFO); //explain why there are duplicate "doing operation X" info messages in the log!
+ errorLog_.logMsg(errorMessage + L"\n-> " + _("Retrying operation..."), TYPE_INFO); //explain why there are duplicate "doing operation X" info messages in the log!
return ProcessCallback::RETRY;
case ConfirmationButton3::CANCEL:
@@ -443,7 +476,7 @@ ProcessCallback::Response StatusHandlerFloatingDialog::reportError(const std::ws
void StatusHandlerFloatingDialog::reportFatalError(const std::wstring& errorMessage)
{
- errorLog.logMsg(errorMessage, TYPE_FATAL_ERROR);
+ errorLog_.logMsg(errorMessage, TYPE_FATAL_ERROR);
switch (handleError_)
{
@@ -479,7 +512,7 @@ void StatusHandlerFloatingDialog::reportFatalError(const std::wstring& errorMess
void StatusHandlerFloatingDialog::reportWarning(const std::wstring& warningMessage, bool& warningActive)
{
- errorLog.logMsg(warningMessage, TYPE_WARNING);
+ errorLog_.logMsg(warningMessage, TYPE_WARNING);
if (!warningActive)
return;
diff --git a/FreeFileSync/Source/ui/gui_status_handler.h b/FreeFileSync/Source/ui/gui_status_handler.h
index d8931cbf..eeed35d2 100644
--- a/FreeFileSync/Source/ui/gui_status_handler.h
+++ b/FreeFileSync/Source/ui/gui_status_handler.h
@@ -28,20 +28,24 @@ public:
~StatusHandlerTemporaryPanel();
void initNewPhase(int objectsTotal, std::int64_t dataTotal, Phase phaseID) override;
- void forceUiRefresh() override;
+ void reportInfo (const std::wstring& text) override;
Response reportError (const std::wstring& text, size_t retryNumber) override;
void reportFatalError(const std::wstring& errorMessage) override;
void reportWarning (const std::wstring& warningMessage, bool& warningActive) override;
+ void forceUiRefresh() override;
void abortProcessNow() override final; //throw GuiAbortProcess
+ zen::ErrorLog getErrorLog() const { return errorLog; }
+
private:
void OnKeyPressed(wxKeyEvent& event);
void OnAbortCompare(wxCommandEvent& event); //handle abort button click
MainDialog& mainDlg;
- bool ignoreErrors = false;
+ xmlAccess::OnGuiError handleError_ = xmlAccess::ON_GUIERROR_POPUP;
+ zen::ErrorLog errorLog;
};
@@ -62,13 +66,13 @@ public:
void initNewPhase (int objectsTotal, std::int64_t dataTotal, Phase phaseID) override;
void updateProcessedData(int objectsDelta, std::int64_t dataDelta ) override;
- void reportInfo (const std::wstring& text ) override;
- void forceUiRefresh () override;
+ void reportInfo (const std::wstring& text ) override;
Response reportError (const std::wstring& text, size_t retryNumber ) override;
void reportFatalError(const std::wstring& errorMessage ) override;
void reportWarning (const std::wstring& warningMessage, bool& warningActive) override;
+ void forceUiRefresh() override;
void abortProcessNow() override final; //throw GuiAbortProcess
private:
@@ -77,7 +81,7 @@ private:
SyncProgressDialog* progressDlg; //managed to have shorter lifetime than this handler!
const size_t lastSyncsLogFileSizeMax_;
xmlAccess::OnGuiError handleError_;
- zen::ErrorLog errorLog;
+ zen::ErrorLog errorLog_;
const size_t automaticRetryCount_;
const size_t automaticRetryDelay_;
const std::wstring jobName_;
diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp
index 8e1713e2..5d3ac556 100644
--- a/FreeFileSync/Source/ui/main_dlg.cpp
+++ b/FreeFileSync/Source/ui/main_dlg.cpp
@@ -610,8 +610,6 @@ MainDialog::MainDialog(const Zstring& globalConfigFile,
gridDataView = std::make_shared<GridView>();
treeDataView = std::make_shared<TreeView>();
- cleanedUp = false;
-
#ifdef ZEN_WIN
new PanelMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere... //ownership passed to "this"
#endif
@@ -954,8 +952,7 @@ void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSe
m_checkBoxMatchCase->SetValue(globalCfg.gui.mainDlg.textSearchRespectCase);
//wxAuiManager erroneously loads panel captions, we don't want that
- typedef std::vector<std::pair<wxString, wxString>> CaptionNameMapping;
- CaptionNameMapping captionNameMap;
+ std::vector<std::pair<wxString, wxString>>captionNameMap;
const wxAuiPaneInfoArray& paneArray = auiMgr.GetAllPanes();
for (size_t i = 0; i < paneArray.size(); ++i)
captionNameMap.emplace_back(paneArray[i].caption, paneArray[i].name);
@@ -1309,6 +1306,7 @@ AbstractPath getExistingParentFolder(const FileSystemObject& fsObj)
}
}
+
void MainDialog::openExternalApplication(const wxString& commandline, const std::vector<FileSystemObject*>& selection, bool leftSide)
{
if (commandline.empty())
@@ -3636,13 +3634,14 @@ void MainDialog::OnCompare(wxCommandEvent& event)
//handle status display and error messages
StatusHandlerTemporaryPanel statusHandler(*this);
- const std::vector<zen::FolderPairCfg> cmpConfig = extractCompareCfg(getConfig().mainCfg, globalCfg.fileTimeTolerance);
+ const std::vector<zen::FolderPairCfg> cmpConfig = extractCompareCfg(getConfig().mainCfg);
//GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization
std::unique_ptr<LockHolder> dirLocks;
//COMPARE DIRECTORIES
folderCmp = compare(globalCfg.optDialogs,
+ globalCfg.fileTimeTolerance,
true, //allowUserInteraction
globalCfg.runWithBackgroundPriority,
globalCfg.folderAccessTimeout,
@@ -3667,10 +3666,13 @@ void MainDialog::OnCompare(wxCommandEvent& event)
m_gridNavi->clearSelection(ALLOW_GRID_EVENT);
- //play (optional) sound notification after sync has completed (GUI and batch mode)
- //const Zstring soundFile = zen::getResourceDir() + Zstr("Compare_Complete.wav");
- //if (fileExists(soundFile))
- // wxSound::Play(toWx(soundFile), wxSOUND_ASYNC);
+ //play (optional) sound notification
+ if (!globalCfg.soundFileCompareFinished.empty())
+ {
+ const Zstring soundFile = getResourceDir() + globalCfg.soundFileCompareFinished;
+ if (fileExists(soundFile))
+ wxSound::Play(utfCvrtTo<wxString>(soundFile), wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as batch!
+ }
//add to folder history after successful comparison only
folderHistoryLeft ->addItem(toZ(m_folderPathLeft ->GetValue()));
@@ -3826,10 +3828,13 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
globalCfg.automaticRetryCount,
globalCfg.automaticRetryDelay,
xmlAccess::extractJobName(activeCfgFilename),
- globalCfg.soundFileSyncComplete,
+ globalCfg.soundFileSyncFinished,
guiCfg.mainCfg.onCompletion,
globalCfg.gui.onCompletionHistory);
+ //inform about (important) non-default global settings
+ logNonDefaultSettings(globalCfg, statusHandler); //let's report here rather than before comparison (user might have changed global settings in the meantime!)
+
//wxBusyCursor dummy; -> redundant: progress already shown in progress dialog!
//GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization
@@ -3861,7 +3866,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
globalCfg.verifyFileCopy,
globalCfg.copyLockedFiles,
globalCfg.copyFilePermissions,
- globalCfg.failsafeFileCopy,
+ globalCfg.failSafeFileCopy,
globalCfg.runWithBackgroundPriority,
globalCfg.folderAccessTimeout,
syncProcessCfg,
@@ -4780,7 +4785,7 @@ void MainDialog::OnMenuCheckVersionAutomatically(wxCommandEvent& event)
{
flashStatusInformation(_("Searching for program updates..."));
//synchronous update check is sufficient here:
- evalPeriodicUpdateCheck(this, globalCfg.gui.lastUpdateCheck, globalCfg.gui.lastOnlineVersion, retrieveOnlineVersion().get());
+ periodicUpdateCheckEval(this, globalCfg.gui.lastUpdateCheck, globalCfg.gui.lastOnlineVersion, periodicUpdateCheckRunAsync(periodicUpdateCheckPrepare().get()).get());
}
}
@@ -4795,10 +4800,12 @@ void MainDialog::OnRegularUpdateCheck(wxIdleEvent& event)
{
flashStatusInformation(_("Searching for program updates..."));
- guiQueue.processAsync([] { return retrieveOnlineVersion(); },
- [this] (std::shared_ptr<UpdateCheckResult>&& result)
+ std::shared_ptr<UpdateCheckResultPrep> resultPrep = periodicUpdateCheckPrepare(); //run on main thread:
+
+ guiQueue.processAsync([resultPrep] { return periodicUpdateCheckRunAsync(resultPrep.get()); }, //run on worker thread: (long-running part of the check)
+ [this] (std::shared_ptr<UpdateCheckResultAsync>&& resultAsync)
{
- evalPeriodicUpdateCheck(this, globalCfg.gui.lastUpdateCheck, globalCfg.gui.lastOnlineVersion, result.get());
+ periodicUpdateCheckEval(this, globalCfg.gui.lastUpdateCheck, globalCfg.gui.lastOnlineVersion, resultAsync.get()); //run on main thread:
});
}
}
diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h
index 25106545..0288d19f 100644
--- a/FreeFileSync/Source/ui/main_dlg.h
+++ b/FreeFileSync/Source/ui/main_dlg.h
@@ -11,6 +11,7 @@
#include <list>
#include <stack>
#include <memory>
+//#include <zen/error_log.h>
#include <wx+/async_task.h>
#include <wx+/file_drop.h>
#include <wx/aui/aui.h>
@@ -312,8 +313,6 @@ private:
//compare status panel (hidden on start, shown when comparing)
std::unique_ptr<CompareProgressDialog> compareStatus; //always bound
- bool cleanedUp;
-
//toggle to display configuration preview instead of comparison result:
//for read access use: m_bpButtonViewTypeSyncAction->isActive()
//when changing value use:
diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp
index bfa7b506..5dce1c34 100644
--- a/FreeFileSync/Source/ui/progress_indicator.cpp
+++ b/FreeFileSync/Source/ui/progress_indicator.cpp
@@ -27,6 +27,7 @@
#include <wx+/image_resources.h>
#include <zen/file_access.h>
#include <zen/thread.h>
+#include <wx+/rtl.h>
#include "gui_generated.h"
#include "../lib/ffs_paths.h"
#include "../lib/perf_check.h"
@@ -589,7 +590,7 @@ public:
switch (static_cast<ColumnTypeMsg>(colType))
{
case COL_TYPE_MSG_TIME:
- drawCellText(dc, rectTmp, getValue(row, colType), true, wxALIGN_CENTER);
+ drawCellText(dc, rectTmp, getValue(row, colType), wxALIGN_CENTER);
break;
case COL_TYPE_MSG_CATEGORY:
@@ -597,14 +598,14 @@ public:
switch (entry->type)
{
case TYPE_INFO:
- dc.DrawLabel(wxString(), getResourceImage(L"msg_info_small"), rectTmp, wxALIGN_CENTER);
+ drawBitmapRtlNoMirror(dc, getResourceImage(L"msg_info_small"), rectTmp, wxALIGN_CENTER);
break;
case TYPE_WARNING:
- dc.DrawLabel(wxString(), getResourceImage(L"msg_warning_small"), rectTmp, wxALIGN_CENTER);
+ drawBitmapRtlNoMirror(dc, getResourceImage(L"msg_warning_small"), rectTmp, wxALIGN_CENTER);
break;
case TYPE_ERROR:
case TYPE_FATAL_ERROR:
- dc.DrawLabel(wxString(), getResourceImage(L"msg_error_small"), rectTmp, wxALIGN_CENTER);
+ drawBitmapRtlNoMirror(dc, getResourceImage(L"msg_error_small"), rectTmp, wxALIGN_CENTER);
break;
}
break;
@@ -612,7 +613,7 @@ public:
case COL_TYPE_MSG_TEXT:
rectTmp.x += COLUMN_GAP_LEFT;
rectTmp.width -= COLUMN_GAP_LEFT;
- drawCellText(dc, rectTmp, getValue(row, colType), true);
+ drawCellText(dc, rectTmp, getValue(row, colType));
break;
}
}
diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp
index d7b4cd37..09439eea 100644
--- a/FreeFileSync/Source/ui/small_dlgs.cpp
+++ b/FreeFileSync/Source/ui/small_dlgs.cpp
@@ -118,7 +118,7 @@ AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent)
const int BORDER_SIZE = 5;
wxBitmap headerBmp(GetClientSize().GetWidth(), versionImage.GetHeight() + 2 * BORDER_SIZE, 24);
//attention: *must* pass 24 bits, auto-determination fails on Windows high-contrast colors schemes!!!
- //problem only manifests when calling wxDC::DrawBitmap
+ //problem only shows when calling wxDC::DrawBitmap
{
wxMemoryDC dc(headerBmp);
dc.SetBackground(*wxWHITE_BRUSH);
@@ -674,7 +674,7 @@ OptionsDlg::OptionsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSet
m_bpButtonRemoveRow->SetBitmapLabel(getResourceImage(L"item_remove"));
setBitmapTextLabel(*m_buttonResetDialogs, getResourceImage(L"reset_dialogs").ConvertToImage(), m_buttonResetDialogs->GetLabel());
- m_checkBoxFailSafe ->SetValue(globalSettings.failsafeFileCopy);
+ m_checkBoxFailSafe ->SetValue(globalSettings.failSafeFileCopy);
m_checkBoxCopyLocked ->SetValue(globalSettings.copyLockedFiles);
m_checkBoxCopyPermissions->SetValue(globalSettings.copyFilePermissions);
@@ -744,7 +744,7 @@ void OptionsDlg::updateGui()
void OptionsDlg::OnOkay(wxCommandEvent& event)
{
//write settings only when okay-button is pressed (except hidden dialog reset)!
- globalSettingsOut.failsafeFileCopy = m_checkBoxFailSafe->GetValue();
+ globalSettingsOut.failSafeFileCopy = m_checkBoxFailSafe->GetValue();
globalSettingsOut.copyLockedFiles = m_checkBoxCopyLocked->GetValue();
globalSettingsOut.copyFilePermissions = m_checkBoxCopyPermissions->GetValue();
@@ -776,7 +776,7 @@ void OptionsDlg::OnDefault(wxCommandEvent& event)
{
const xmlAccess::XmlGlobalSettings defaultCfg;
- m_checkBoxFailSafe ->SetValue(defaultCfg.failsafeFileCopy);
+ m_checkBoxFailSafe ->SetValue(defaultCfg.failSafeFileCopy);
m_checkBoxCopyLocked ->SetValue(defaultCfg.copyLockedFiles);
m_checkBoxCopyPermissions->SetValue(defaultCfg.copyFilePermissions);
diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp
index 74789c83..2b3fa496 100644
--- a/FreeFileSync/Source/ui/sync_cfg.cpp
+++ b/FreeFileSync/Source/ui/sync_cfg.cpp
@@ -128,7 +128,8 @@ private:
void OnToggleDeletionType(wxCommandEvent& event) override { toggleDeletionPolicy(handleDeletion); updateSyncGui(); }
- void OnHelpVersioning(wxHyperlinkEvent& event) override { displayHelpEntry(L"versioning", this); }
+ void OnHelpDetectMovedFiles(wxHyperlinkEvent& event) override { displayHelpEntry(L"synchronization-settings" , this); }
+ void OnHelpVersioning (wxHyperlinkEvent& event) override { displayHelpEntry(L"versioning", this); }
std::shared_ptr<const SyncConfig> getSyncConfig() const;
void setSyncConfig(std::shared_ptr<const SyncConfig> syncCfg);
@@ -320,7 +321,7 @@ ConfigDialog::ConfigDialog(wxWindow* parent,
add(VER_STYLE_ADD_TIMESTAMP, _("Time stamp"), _("Append a time stamp to each file name"));
//use spacer to keep dialog height stable, no matter if versioning options are visible
- bSizerVersioning->Add(0, m_panelVersioning->GetSize().GetHeight());
+ bSizerDelHandling->Add(0, m_panelVersioning->GetSize().GetHeight());
//-----------------------------------------------------
@@ -844,13 +845,13 @@ void toggleDeletionPolicy(DeletionPolicy& deletionPolicy)
switch (deletionPolicy)
{
case DELETE_PERMANENTLY:
- deletionPolicy = DELETE_TO_RECYCLER;
+ deletionPolicy = DELETE_TO_VERSIONING;
break;
case DELETE_TO_RECYCLER:
- deletionPolicy = DELETE_TO_VERSIONING;
+ deletionPolicy = DELETE_PERMANENTLY;
break;
case DELETE_TO_VERSIONING:
- deletionPolicy = DELETE_PERMANENTLY;
+ deletionPolicy = DELETE_TO_RECYCLER;
break;
}
}
@@ -1064,7 +1065,7 @@ void ConfigDialog::updateMiscGui()
void ConfigDialog::selectFolderPairConfig(int newPairIndexToShow)
{
assert(selectedPairIndexToShow == EMPTY_PAIR_INDEX_SELECTED);
- assert(newPairIndexToShow == -1 || makeUnsigned(newPairIndexToShow) < folderPairConfig_.size());
+ assert(newPairIndexToShow == -1 || makeUnsigned(newPairIndexToShow) < folderPairConfig_.size());
numeric::clamp(newPairIndexToShow, -1, static_cast<int>(folderPairConfig_.size()) - 1);
selectedPairIndexToShow = newPairIndexToShow;
@@ -1073,13 +1074,19 @@ void ConfigDialog::selectFolderPairConfig(int newPairIndexToShow)
//show/hide controls that are only relevant for main/local config
const bool mainConfigSelected = newPairIndexToShow < 0;
//comparison panel:
- bSizerLocalCompSettings->Show(!mainConfigSelected);
+ m_staticTextMainCompSettings->Show( mainConfigSelected && !folderPairConfig_.empty());
+ m_checkBoxUseLocalCmpOptions->Show(!mainConfigSelected && !folderPairConfig_.empty());
+ m_staticlineCompHeader->Show(!folderPairConfig_.empty());
m_panelCompSettingsHolder->Layout(); //fix comp panel glitch on Win 7 125% font size
//filter panel
- bSizerLocalFilterSettings->Show(!mainConfigSelected);
+ m_staticTextMainFilterSettings ->Show( mainConfigSelected && !folderPairConfig_.empty());
+ m_staticTextLocalFilterSettings->Show(!mainConfigSelected && !folderPairConfig_.empty());
+ m_staticlineFilterHeader->Show(!folderPairConfig_.empty());
m_panelFilterSettingsHolder->Layout();
//sync panel:
- bSizerLocalSyncSettings->Show(!mainConfigSelected);
+ m_staticTextMainSyncSettings ->Show( mainConfigSelected && !folderPairConfig_.empty());
+ m_checkBoxUseLocalSyncOptions->Show(!mainConfigSelected && !folderPairConfig_.empty());
+ m_staticlineSyncHeader->Show(!folderPairConfig_.empty());
m_panelSyncSettingsHolder->Layout();
//misc
bSizerMiscConfig->Show(mainConfigSelected);
diff --git a/FreeFileSync/Source/ui/tree_view.cpp b/FreeFileSync/Source/ui/tree_view.cpp
index 590a00c9..a1420ff1 100644
--- a/FreeFileSync/Source/ui/tree_view.cpp
+++ b/FreeFileSync/Source/ui/tree_view.cpp
@@ -869,8 +869,7 @@ private:
if (colType == static_cast<ColumnType>(sortInfo.first))
{
const wxBitmap& marker = getResourceImage(sortInfo.second ? L"sortAscending" : L"sortDescending");
- wxPoint markerBegin = rectInside.GetTopLeft() + wxPoint((rectInside.width - marker.GetWidth()) / 2, 0);
- dc.DrawBitmap(marker, markerBegin, true); //respect 2-pixel gap
+ drawBitmapRtlNoMirror(dc, marker, rectInside, wxALIGN_CENTER_HORIZONTAL);
}
}
}
@@ -950,7 +949,7 @@ private:
}
wxDCTextColourChanger dummy3(dc, *wxBLACK); //accessibility: always set both foreground AND background colors!
- dc.DrawLabel(numberTo<wxString>(node->percent_) + L"%", areaPerc, wxALIGN_CENTER);
+ drawCellText(dc, areaPerc, numberTo<std::wstring>(node->percent_) + L"%", wxALIGN_CENTER);
rectTmp.x += WIDTH_PERCENTAGE_BAR + 2 * GAP_SIZE;
rectTmp.width -= WIDTH_PERCENTAGE_BAR + 2 * GAP_SIZE;
@@ -967,7 +966,7 @@ private:
//clearArea(dc, rectStat, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
clearArea(dc, rectStat, *wxWHITE); //accessibility: always set both foreground AND background colors!
- drawBitmapRtlMirror(dc, bmp, rectStat, wxALIGN_CENTER, buffer);
+ drawBitmapRtlMirror(dc, bmp, rectStat, wxALIGN_CENTER, renderBuf);
};
const bool drawMouseHover = static_cast<HoverAreaNavi>(rowHover) == HoverAreaNavi::NODE;
@@ -1003,13 +1002,19 @@ private:
if (!isActive)
nodeIcon = wxBitmap(nodeIcon.ConvertToImage().ConvertToGreyscale(1.0 / 3, 1.0 / 3, 1.0 / 3)); //treat all channels equally!
- drawBitmapRtlNoMirror(dc, nodeIcon, rectTmp, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, buffer);
+ drawBitmapRtlNoMirror(dc, nodeIcon, rectTmp, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
rectTmp.x += widthNodeIcon + GAP_SIZE;
rectTmp.width -= widthNodeIcon + GAP_SIZE;
if (rectTmp.width > 0)
- drawCellText(dc, rectTmp, getValue(row, colType), isActive, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
+ {
+ wxDCTextColourChanger dummy(dc);
+ if (!isActive)
+ dummy.Set(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
+
+ drawCellText(dc, rectTmp, getValue(row, colType), wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
+ }
}
}
}
@@ -1032,7 +1037,7 @@ private:
rectTmp.width -= 2 * GAP_SIZE;
}
- drawCellText(dc, rectTmp, getValue(row, colType), true, alignment);
+ drawCellText(dc, rectTmp, getValue(row, colType), alignment);
}
}
@@ -1265,7 +1270,7 @@ private:
const wxBitmap dirIcon = IconBuffer::genericDirIcon (IconBuffer::SIZE_SMALL);
const wxBitmap rootBmp;
- Opt<wxBitmap> buffer; //avoid costs of recreating this temporal variable
+ Opt<wxBitmap> renderBuf; //avoid costs of recreating this temporal variable
const int widthNodeIcon;
const int widthLevelStep;
const int widthNodeStatus;
diff --git a/FreeFileSync/Source/ui/version_check.cpp b/FreeFileSync/Source/ui/version_check.cpp
index de76a998..056e57ac 100644
--- a/FreeFileSync/Source/ui/version_check.cpp
+++ b/FreeFileSync/Source/ui/version_check.cpp
@@ -12,57 +12,47 @@
#include <zen/build_info.h>
#include <zen/basic_math.h>
#include <zen/file_error.h>
+#include <zen/thread.h> //std::thread::id
#include <wx+/popup_dlg.h>
+#include <wx+/http.h>
#include "version_id.h"
#include "../lib/ffs_paths.h"
#ifdef ZEN_WIN
- #include <zen/win.h> //tame wininet.h include
#include <zen/win_ver.h>
- #include <zen/com_tools.h>
- #include <wininet.h>
+// #include <zen/com_tools.h>
#elif defined ZEN_MAC
#include <CoreServices/CoreServices.h> //Gestalt()
#endif
-#if defined ZEN_LINUX || defined ZEN_MAC
- #include <zen/thread.h> //std::thread::id
- #include <wx/protocol/http.h>
- #include <wx/app.h>
-#endif
-
-
using namespace zen;
namespace
{
-#ifndef ZEN_WIN
- #ifndef NDEBUG
- const std::thread::id mainThreadId = std::this_thread::get_id();
- #endif
+#ifndef NDEBUG
+ const std::thread::id mainThreadId = std::this_thread::get_id();
#endif
std::wstring getIso639Language()
{
- //respect thread-safety for WinInetAccess => don't use wxWidgets in the Windows build here!!!
+ assert(std::this_thread::get_id() == mainThreadId); //this function is not thread-safe, consider wxWidgets usage
#ifdef ZEN_WIN //use a more reliable function than wxWidgets:
const int bufSize = 10;
wchar_t buf[bufSize] = {};
- int rv = ::GetLocaleInfo(LOCALE_USER_DEFAULT, //_In_ LCID Locale,
- LOCALE_SISO639LANGNAME,//_In_ LCTYPE LCType,
- buf, //_Out_opt_ LPTSTR lpLCData,
- bufSize); //_In_ int cchData
+ int rv = ::GetLocaleInfo(LOCALE_USER_DEFAULT, //_In_ LCID Locale,
+ LOCALE_SISO639LANGNAME, //_In_ LCTYPE LCType,
+ buf, //_Out_opt_ LPTSTR lpLCData,
+ bufSize); //_In_ int cchData
if (0 < rv && rv < bufSize)
return buf; //MSDN: "This can be a 3-letter code for languages that don't have a 2-letter code"!
assert(false);
return std::wstring();
-#else
- assert(std::this_thread::get_id() == mainThreadId);
+#else
const std::wstring localeName(wxLocale::GetLanguageCanonicalName(wxLocale::GetSystemLanguage()));
if (localeName.empty())
return std::wstring();
@@ -75,22 +65,21 @@ std::wstring getIso639Language()
std::wstring getIso3166Country()
{
- //respect thread-safety for WinInetAccess => don't use wxWidgets in the Windows build here!!!
+ assert(std::this_thread::get_id() == mainThreadId); //this function is not thread-safe, consider wxWidgets usage
#ifdef ZEN_WIN //use a more reliable function than wxWidgets:
const int bufSize = 10;
wchar_t buf[bufSize] = {};
- int rv = ::GetLocaleInfo(LOCALE_USER_DEFAULT, //_In_ LCID Locale,
- LOCALE_SISO3166CTRYNAME,//_In_ LCTYPE LCType,
- buf, //_Out_opt_ LPTSTR lpLCData,
- bufSize); //_In_ int cchData
+ int rv = ::GetLocaleInfo(LOCALE_USER_DEFAULT, //_In_ LCID Locale,
+ LOCALE_SISO3166CTRYNAME, //_In_ LCTYPE LCType,
+ buf, //_Out_opt_ LPTSTR lpLCData,
+ bufSize); //_In_ int cchData
if (0 < rv && rv < bufSize)
return buf; //MSDN: "This can also return a number, such as "029" for Caribbean."!
assert(false);
return std::wstring();
-#else
- assert(std::this_thread::get_id() == mainThreadId);
+#else
const std::wstring localeName(wxLocale::GetLanguageCanonicalName(wxLocale::GetSystemLanguage()));
if (localeName.empty())
return std::wstring();
@@ -100,24 +89,23 @@ std::wstring getIso3166Country()
}
-std::string geHttpPostParameters() //must be in application/x-www-form-urlencoded format!!!
-{
- //1. coordinate with get_latest_version_number.php
- //2. respect thread-safety for WinInetAccess => don't use wxWidgets in the Windows build here!!!
- std::string params = "ffs_version=" + utfCvrtTo<std::string>(zen::ffsVersion);
+//coordinate with get_latest_version_number.php
+std::vector<std::pair<std::string, std::string>> geHttpPostParameters()
+{
+ assert(std::this_thread::get_id() == mainThreadId); //this function is not thread-safe, e.g. consider wxWidgets usage in isPortableVersion()
+ std::vector<std::pair<std::string, std::string>> params;
- params += "&ffs_type=";
- params += isPortableVersion() ? "Portable" : "Local";
+ params.emplace_back("ffs_version", utfCvrtTo<std::string>(zen::ffsVersion));
+ params.emplace_back("ffs_type", isPortableVersion() ? "Portable" : "Local");
#ifdef ZEN_WIN
- params += "&os_name=Windows";
+ params.emplace_back("os_name", "Windows");
const auto osvMajor = getOsVersion().major;
const auto osvMinor = getOsVersion().minor;
#elif defined ZEN_LINUX
- params += "&os_name=Linux";
- assert(std::this_thread::get_id() == mainThreadId);
+ params.emplace_back("os_name", "Linux");
const wxLinuxDistributionInfo distribInfo = wxGetLinuxDistributionInfo();
assert(contains(distribInfo.Release, L'.'));
@@ -129,271 +117,33 @@ std::string geHttpPostParameters() //must be in application/x-www-form-urlencode
const int osvMinor = stringTo<int>(digits[1]);
#elif defined ZEN_MAC
- params += "&os_name=Mac";
+ params.emplace_back("os_name", "Mac");
+
SInt32 osvMajor = 0;
SInt32 osvMinor = 0;
::Gestalt(gestaltSystemVersionMajor, &osvMajor);
::Gestalt(gestaltSystemVersionMinor, &osvMinor);
#endif
- params += "&os_version=" + numberTo<std::string>(osvMajor) + "." + numberTo<std::string>(osvMinor);
+ params.emplace_back("os_version", numberTo<std::string>(osvMajor) + "." + numberTo<std::string>(osvMinor));
- params +=
#ifdef ZEN_WIN
- running64BitWindows() ? "&os_arch=64" : "&os_arch=32";
+ params.emplace_back("os_arch", running64BitWindows() ? "64" : "32");
#elif defined ZEN_LINUX || defined ZEN_MAC
#ifdef ZEN_BUILD_32BIT
- "&os_arch=32";
+ params.emplace_back("os_arch", "32");
#elif defined ZEN_BUILD_64BIT
- "&os_arch=64";
+ params.emplace_back("os_arch", "64");
#endif
#endif
const std::string isoLang = utfCvrtTo<std::string>(getIso639Language());
const std::string isoCountry = utfCvrtTo<std::string>(getIso3166Country());
- params += "&language=" + (!isoLang .empty() ? isoLang : "zz");
- params += "&country=" + (!isoCountry.empty() ? isoCountry : "ZZ");
-
- return params;
-}
-
-
-std::string sendHttpRequestImpl(const std::wstring& url, //throw FileError
- const std::string* postParams, //issue POST if bound, GET otherwise
- int level = 0)
-{
- assert(!startsWith(url, L"https:")); //not supported by wxHTTP!
- std::wstring urlFmt = url;
- if (startsWith(urlFmt, L"http://"))
- urlFmt = afterFirst(urlFmt, L"://", IF_MISSING_RETURN_NONE);
- const std::wstring server = beforeFirst(urlFmt, L'/', IF_MISSING_RETURN_ALL);
- const std::wstring page = L'/' + afterFirst(urlFmt, L'/', IF_MISSING_RETURN_NONE);
-#ifdef ZEN_WIN
- //WinInet: 1. uses IE proxy settings! :) 2. follows HTTP redirects by default 3. swallows HTTPS if needed
- HINTERNET hInternet = ::InternetOpen(L"FFS-Update-Check", //_In_ LPCTSTR lpszAgent,
- INTERNET_OPEN_TYPE_PRECONFIG, //_In_ DWORD dwAccessType,
- nullptr, //_In_ LPCTSTR lpszProxyName,
- nullptr, //_In_ LPCTSTR lpszProxyBypass,
- 0); //_In_ DWORD dwFlags
- if (!hInternet)
- THROW_LAST_FILE_ERROR(_("Internet access failed."), L"InternetOpen");
- ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hInternet));
-
- HINTERNET hSession = ::InternetConnect(hInternet, //_In_ HINTERNET hInternet,
- server.c_str(), //_In_ LPCTSTR lpszServerName,
- INTERNET_DEFAULT_HTTP_PORT, //_In_ INTERNET_PORT nServerPort,
- nullptr, //_In_ LPCTSTR lpszUsername,
- nullptr, //_In_ LPCTSTR lpszPassword,
- INTERNET_SERVICE_HTTP, //_In_ DWORD dwService,
- 0, //_In_ DWORD dwFlags,
- 0); //_In_ DWORD_PTR dwContext
- if (!hSession)
- THROW_LAST_FILE_ERROR(_("Internet access failed."), L"InternetConnect");
- ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hSession));
-
- const wchar_t* acceptTypes[] = { L"*/*", nullptr };
- DWORD requestFlags = INTERNET_FLAG_KEEP_CONNECTION |
- INTERNET_FLAG_NO_UI |
- INTERNET_FLAG_RELOAD | //
- INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS; //relevant for GET only
- if (postParams)
- requestFlags |= INTERNET_FLAG_NO_AUTO_REDIRECT; //POST would be re-issued as GET during auto-redirect => handle ourselves!
-
- HINTERNET hRequest = ::HttpOpenRequest(hSession, //_In_ HINTERNET hConnect,
- postParams ? L"POST" : L"GET", //_In_ LPCTSTR lpszVerb,
- page.c_str(), //_In_ LPCTSTR lpszObjectName,
- nullptr, //_In_ LPCTSTR lpszVersion,
- nullptr, //_In_ LPCTSTR lpszReferer,
- acceptTypes, //_In_ LPCTSTR *lplpszAcceptTypes,
- requestFlags, //_In_ DWORD dwFlags,
- 0); //_In_ DWORD_PTR dwContext
- if (!hRequest)
- THROW_LAST_FILE_ERROR(_("Internet access failed."), L"HttpOpenRequest");
- ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hRequest));
-
- const std::wstring headers = postParams ? L"Content-type: application/x-www-form-urlencoded" : L"";
- std::string postParamsTmp = postParams ? *postParams : "";
- char* postParamBuf = postParamsTmp.empty() ? nullptr : &*postParamsTmp.begin();
- if (!::HttpSendRequest(hRequest, //_In_ HINTERNET hRequest,
- headers.c_str(), //_In_ LPCTSTR lpszHeaders,
- headers.size(), //_In_ DWORD dwHeadersLength,
- postParamBuf, //_In_ LPVOID lpOptional,
- postParamsTmp.size())) //_In_ DWORD dwOptionalLength
- THROW_LAST_FILE_ERROR(_("Internet access failed."), L"HttpSendRequest");
-
- DWORD sc = 0;
- {
- DWORD bufLen = sizeof(sc);
- if (!::HttpQueryInfo(hRequest, //_In_ HINTERNET hRequest,
- HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, //_In_ DWORD dwInfoLevel,
- &sc, //_Inout_ LPVOID lpvBuffer,
- &bufLen, //_Inout_ LPDWORD lpdwBufferLength,
- nullptr)) //_Inout_ LPDWORD lpdwIndex
- THROW_LAST_FILE_ERROR(_("Internet access failed."), L"HttpQueryInfo: HTTP_QUERY_STATUS_CODE");
- }
-
- //http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
- if (sc / 100 == 3) //e.g. 301, 302, 303, 307... we're not too greedy since we check location, too!
- {
- if (level < 5) //"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop."
- {
- DWORD bufLen = 10000;
- std::wstring location(bufLen, L'\0');
- if (!::HttpQueryInfo(hRequest, HTTP_QUERY_LOCATION, &*location.begin(), &bufLen, nullptr))
- THROW_LAST_FILE_ERROR(_("Internet access failed."), L"HttpQueryInfo: HTTP_QUERY_LOCATION");
- if (bufLen >= location.size()) //HttpQueryInfo expected to write terminating zero
- throw FileError(_("Internet access failed."), L"HttpQueryInfo: HTTP_QUERY_LOCATION, buffer overflow");
- location.resize(bufLen);
-
- if (!location.empty())
- return sendHttpRequestImpl(location, postParams, level + 1);
- }
- throw FileError(_("Internet access failed."), L"Unresolvable redirect.");
- }
-
- if (sc != HTTP_STATUS_OK) //200
- throw FileError(_("Internet access failed."), replaceCpy<std::wstring>(L"HTTP status code %x.", L"%x", numberTo<std::wstring>(sc)));
- //e.g. 404 - HTTP_STATUS_NOT_FOUND
-
- std::string buffer;
- const DWORD blockSize = 64 * 1024;
- //internet says "HttpQueryInfo() + HTTP_QUERY_CONTENT_LENGTH" not supported by all http servers...
- for (;;)
- {
- buffer.resize(buffer.size() + blockSize);
-
- DWORD bytesRead = 0;
- if (!::InternetReadFile(hRequest, //_In_ HINTERNET hFile,
- &*(buffer.begin() + buffer.size() - blockSize), //_Out_ LPVOID lpBuffer,
- blockSize, //_In_ DWORD dwNumberOfBytesToRead,
- &bytesRead)) //_Out_ LPDWORD lpdwNumberOfBytesRead
- THROW_LAST_FILE_ERROR(_("Internet access failed."), L"InternetReadFile");
-
- if (bytesRead < blockSize)
- buffer.resize(buffer.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics
-
- if (bytesRead == 0)
- return buffer;
- }
-
-#else
- assert(std::this_thread::get_id() == mainThreadId);
- assert(wxApp::IsMainLoopRunning());
-
- wxHTTP webAccess;
- webAccess.SetHeader(L"User-Agent", L"FFS-Update-Check");
- webAccess.SetTimeout(10 /*[s]*/); //default: 10 minutes: WTF are these wxWidgets people thinking???
-
- if (!webAccess.Connect(server)) //will *not* fail for non-reachable url here!
- throw FileError(_("Internet access failed."), L"wxHTTP::Connect");
-
- if (postParams)
- if (!webAccess.SetPostText(L"application/x-www-form-urlencoded", utfCvrtTo<wxString>(*postParams)))
- throw FileError(_("Internet access failed."), L"wxHTTP::SetPostText");
-
- std::unique_ptr<wxInputStream> httpStream(webAccess.GetInputStream(page)); //must be deleted BEFORE webAccess is closed
- const int sc = webAccess.GetResponse();
-
- //http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
- if (sc / 100 == 3) //e.g. 301, 302, 303, 307... we're not too greedy since we check location, too!
- {
- if (level < 5) //"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop."
- {
- const std::wstring newUrl(webAccess.GetHeader(L"Location"));
- if (!newUrl.empty())
- return sendHttpRequestImpl(newUrl, postParams, level + 1);
- }
- throw FileError(_("Internet access failed."), L"Unresolvable redirect.");
- }
+ params.emplace_back("language", !isoLang .empty() ? isoLang : "zz");
+ params.emplace_back("country" , !isoCountry.empty() ? isoCountry : "ZZ");
- if (sc != 200) //HTTP_STATUS_OK
- throw FileError(_("Internet access failed."), replaceCpy<std::wstring>(L"HTTP status code %x.", L"%x", numberTo<std::wstring>(sc)));
-
- if (!httpStream || webAccess.GetError() != wxPROTO_NOERR)
- throw FileError(_("Internet access failed."), L"wxHTTP::GetError");
-
- std::string buffer;
- int newValue = 0;
- while ((newValue = httpStream->GetC()) != wxEOF)
- buffer.push_back(static_cast<char>(newValue));
- return buffer;
-#endif
-}
-
-
-bool internetIsAlive() //noexcept
-{
-#ifdef ZEN_WIN
- //::InternetAttemptConnect(0) -> not working as expected: succeeds even when there is no internet connection!
-
- HINTERNET hInternet = ::InternetOpen(L"FreeFileSync", //_In_ LPCTSTR lpszAgent,
- INTERNET_OPEN_TYPE_PRECONFIG, //_In_ DWORD dwAccessType,
- nullptr, //_In_ LPCTSTR lpszProxyName,
- nullptr, //_In_ LPCTSTR lpszProxyBypass,
- 0); //_In_ DWORD dwFlags
- if (!hInternet)
- return false;
- ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hInternet));
-
- //InternetOpenUrl is shortcut for HTTP:GET with InternetConnect + HttpOpenRequest + HttpSendRequest:
- HINTERNET hRequest = ::InternetOpenUrl(hInternet, //_In_ HINTERNET hInternet,
- L"http://www.google.com/", //_In_ LPCTSTR lpszUrl,
- nullptr, //_In_ LPCTSTR lpszHeaders,
- 0, //_In_ DWORD dwHeadersLength,
- INTERNET_FLAG_KEEP_CONNECTION |
- INTERNET_FLAG_NO_UI |
- INTERNET_FLAG_RELOAD |
- INTERNET_FLAG_NO_AUTO_REDIRECT, //_In_ DWORD dwFlags,
- 0); //_In_ DWORD_PTR dwContext
- //fails with ERROR_INTERNET_NAME_NOT_RESOLVED if server not found => the server-relative part is checked by HTTP_QUERY_STATUS_CODE!!!
- if (!hRequest)
- return false;
- ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hRequest));
-
- DWORD sc = 0;
- {
- DWORD bufLen = sizeof(sc);
- if (!::HttpQueryInfo(hRequest, //_In_ HINTERNET hRequest,
- HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, //_In_ DWORD dwInfoLevel,
- &sc, //_Inout_ LPVOID lpvBuffer,
- &bufLen, //_Inout_ LPDWORD lpdwBufferLength,
- nullptr)) //_Inout_ LPDWORD lpdwIndex
- return false;
- }
-
-#else
- const wxString server = L"www.google.com";
- const wxString page = L"/";
-
- wxHTTP webAccess;
- webAccess.SetTimeout(10 /*[s]*/); //default: 10 minutes: WTF are these wxWidgets people thinking???
-
- if (!webAccess.Connect(server)) //will *not* fail for non-reachable url here!
- return false;
-
- std::unique_ptr<wxInputStream> httpStream(webAccess.GetInputStream(page)); //call before checking wxHTTP::GetResponse()
- const int sc = webAccess.GetResponse();
-#endif
- //attention: http://www.google.com/ might redirect to "https" => don't follow, just return "true"!!!
- return sc / 100 == 2 || //e.g. 200
- sc / 100 == 3; //e.g. 301, 302, 303, 307... when in doubt, consider internet alive!
-}
-
-
-#if 0 //not needed yet: -Wunused-function on clang
-inline
-std::string sendHttpGet(const std::wstring& url) //throw FileError
-{
- return sendHttpRequestImpl(url, nullptr); //throw FileError
-}
-#endif
-
-
-inline
-std::string sendHttpPost(const std::wstring& url, const std::string& postParams) //throw FileError
-{
- return sendHttpRequestImpl(url, &postParams); //throw FileError
+ return params;
}
@@ -405,14 +155,14 @@ enum GetVerResult
};
//access is thread-safe on Windows (WinInet), but not on Linux/OS X (wxWidgets)
-GetVerResult getOnlineVersion(std::wstring& version)
+GetVerResult getOnlineVersion(const std::vector<std::pair<std::string, std::string>>& postParams, std::wstring& version)
{
try
{
//harmonize with wxHTTP: get_latest_version_number.php must be accessible without https!!!
- const std::string buffer = sendHttpPost(L"http://www.freefilesync.org/get_latest_version_number.php", geHttpPostParameters()); //throw FileError
+ const std::string buffer = sendHttpPost(L"http://www.freefilesync.org/get_latest_version_number.php", L"FFS-Update-Check", postParams); //throw FileError
version = utfCvrtTo<std::wstring>(buffer);
- trim(version); //Windows: remove trailing blank and newline
+ trim(version);
return version.empty() ? GET_VER_PAGE_NOT_FOUND : GET_VER_SUCCESS; //empty version possible??
}
catch (const FileError&)
@@ -424,11 +174,10 @@ GetVerResult getOnlineVersion(std::wstring& version)
std::vector<size_t> parseVersion(const std::wstring& version)
{
- std::vector<std::wstring> digits = split(version, FFS_VERSION_SEPARATOR);
-
- std::vector<size_t> output;
- std::transform(digits.begin(), digits.end(), std::back_inserter(output), [&](const std::wstring& d) { return stringTo<size_t>(d); });
- return output;
+ std::vector<size_t> output;
+ for (const std::wstring& digit : split(version, FFS_VERSION_SEPARATOR))
+ output.push_back(stringTo<size_t>(digit));
+ return output;
}
}
@@ -461,7 +210,7 @@ void zen::disableUpdateCheck(time_t& lastUpdateCheck)
void zen::checkForUpdateNow(wxWindow* parent, std::wstring& lastOnlineVersion)
{
std::wstring onlineVersion;
- switch (getOnlineVersion(onlineVersion))
+ switch (getOnlineVersion(geHttpPostParameters(), onlineVersion))
{
case GET_VER_SUCCESS:
lastOnlineVersion = onlineVersion;
@@ -488,7 +237,7 @@ void zen::checkForUpdateNow(wxWindow* parent, std::wstring& lastOnlineVersion)
case GET_VER_NO_CONNECTION:
showNotificationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg().
setTitle(_("Check for Program Updates")).
- setMainInstructions(_("Unable to connect to www.freefilesync.org.")));
+ setMainInstructions(replaceCpy(_("Unable to connect to %x."), L"%x", L"www.freefilesync.org.")));
break;
case GET_VER_PAGE_NOT_FOUND:
@@ -520,7 +269,23 @@ bool zen::shouldRunPeriodicUpdateCheck(time_t lastUpdateCheck)
}
-struct zen::UpdateCheckResult
+struct zen::UpdateCheckResultPrep
+{
+ const std::vector<std::pair<std::string, std::string>> postParameters { geHttpPostParameters() };
+};
+
+//run on main thread:
+std::shared_ptr<UpdateCheckResultPrep> zen::periodicUpdateCheckPrepare()
+{
+#ifdef ZEN_WIN
+ return std::make_shared<UpdateCheckResultPrep>();
+#else
+ return nullptr;
+#endif
+}
+
+
+struct zen::UpdateCheckResultAsync
{
#ifdef ZEN_WIN
GetVerResult versionStatus = GET_VER_PAGE_NOT_FOUND;
@@ -528,33 +293,28 @@ struct zen::UpdateCheckResult
#endif
};
-
-std::shared_ptr<UpdateCheckResult> zen::retrieveOnlineVersion()
+//run on worker thread:
+std::shared_ptr<UpdateCheckResultAsync> zen::periodicUpdateCheckRunAsync(const UpdateCheckResultPrep* resultPrep)
{
#ifdef ZEN_WIN
- try
- {
- ComInitializer ci; //throw SysError
-
- auto result = std::make_shared<UpdateCheckResult>();
- result->versionStatus = getOnlineVersion(result->onlineVersion); //access is thread-safe on Windows only!
+ auto result = std::make_shared<UpdateCheckResultAsync>();
+ result->versionStatus = getOnlineVersion(resultPrep->postParameters, result->onlineVersion); //access is thread-safe on Windows only!
return result;
- }
- catch (SysError&) { assert(false); return nullptr; }
#else
return nullptr;
#endif
}
-void zen::evalPeriodicUpdateCheck(wxWindow* parent, time_t& lastUpdateCheck, std::wstring& lastOnlineVersion, const UpdateCheckResult* result)
+//run on main thread:
+void zen::periodicUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, std::wstring& lastOnlineVersion, const UpdateCheckResultAsync* resultAsync)
{
#ifdef ZEN_WIN
- const GetVerResult versionStatus = result->versionStatus;
- const std::wstring onlineVersion = result->onlineVersion;
+ const GetVerResult versionStatus = resultAsync->versionStatus;
+ const std::wstring onlineVersion = resultAsync->onlineVersion;
#else
std::wstring onlineVersion;
- const GetVerResult versionStatus = getOnlineVersion(onlineVersion);
+ const GetVerResult versionStatus = getOnlineVersion(geHttpPostParameters(), onlineVersion);
#endif
switch (versionStatus)
diff --git a/FreeFileSync/Source/ui/version_check.h b/FreeFileSync/Source/ui/version_check.h
index 06bb79f6..6982fe7f 100644
--- a/FreeFileSync/Source/ui/version_check.h
+++ b/FreeFileSync/Source/ui/version_check.h
@@ -14,19 +14,25 @@
namespace zen
{
-bool updateCheckActive(time_t lastUpdateCheck);
+bool updateCheckActive (time_t lastUpdateCheck);
void disableUpdateCheck(time_t& lastUpdateCheck);
bool haveNewerVersionOnline(const std::wstring& onlineVersion);
//periodic update check:
bool shouldRunPeriodicUpdateCheck(time_t lastUpdateCheck);
-//long-runing part of the check: thread-safe => run asynchronously
-struct UpdateCheckResult;
-std::shared_ptr<UpdateCheckResult> retrieveOnlineVersion();
-//eval on main thread:
-void evalPeriodicUpdateCheck(wxWindow* parent, time_t& lastUpdateCheck, std::wstring& lastOnlineVersion, const UpdateCheckResult* result);
+struct UpdateCheckResultPrep;
+struct UpdateCheckResultAsync;
+
+//run on main thread:
+std::shared_ptr<UpdateCheckResultPrep> periodicUpdateCheckPrepare();
+//run on worker thread: (long-running part of the check)
+std::shared_ptr<UpdateCheckResultAsync> periodicUpdateCheckRunAsync(const UpdateCheckResultPrep* resultPrep);
+//run on main thread:
+void periodicUpdateCheckEval(wxWindow* parent, time_t& lastUpdateCheck, std::wstring& lastOnlineVersion, const UpdateCheckResultAsync* resultAsync);
+
+//----------------------------------------------------------------------------
//call from main thread:
void checkForUpdateNow(wxWindow* parent, std::wstring& lastOnlineVersion);
diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h
index 59a94db2..2a835304 100644
--- a/FreeFileSync/Source/version/version.h
+++ b/FreeFileSync/Source/version/version.h
@@ -3,8 +3,8 @@
namespace zen
{
- const wchar_t ffsVersion[] = L"8.0"; //internal linkage!
- const wchar_t FFS_VERSION_SEPARATOR = L'.';
+const wchar_t ffsVersion[] = L"8.1"; //internal linkage!
+const wchar_t FFS_VERSION_SEPARATOR = L'.';
}
#endif
diff --git a/wx+/file_drop.h b/wx+/file_drop.h
index f8943788..cfa0ea6c 100644
--- a/wx+/file_drop.h
+++ b/wx+/file_drop.h
@@ -50,7 +50,7 @@ void MyDlg::OnFilesDropped(FileDropEvent& event);
namespace impl
{
inline
-wxEventType createNewEventType()
+wxEventType getFileDropEventType()
{
//inline functions have external linkage by default => this static is also extern, i.e. program wide unique! but defined in a header... ;)
static wxEventType dummy = wxNewEventType();
@@ -59,7 +59,7 @@ wxEventType createNewEventType()
}
//define new event type
-const wxEventType EVENT_DROP_FILE = impl::createNewEventType();
+const wxEventType EVENT_DROP_FILE = impl::getFileDropEventType();
class FileDropEvent : public wxCommandEvent
{
@@ -101,7 +101,7 @@ public:
~DragDropCleanupWindow() { impl::unregisterDragDrop(dropHwnd); }
private:
- HWND dropHwnd;
+ const HWND dropHwnd;
};
}
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index f80c9c73..9d896dc2 100644
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -88,7 +88,7 @@ void GridData::renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType c
rectTmp.x += COLUMN_GAP_LEFT;
rectTmp.width -= COLUMN_GAP_LEFT;
- drawCellText(dc, rectTmp, getValue(row, colType), true);
+ drawCellText(dc, rectTmp, getValue(row, colType));
}
@@ -122,65 +122,71 @@ void GridData::drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bo
}
-namespace
-{
-const wchar_t ELLIPSIS = L'\u2026'; //...
-
-template <class Function> inline
-std::wstring getTruncatedText(const std::wstring& text, Function textFits)
+void GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& text, int alignment)
{
- if (textFits(text))
- return text;
-
- //unlike Windows 7 Explorer, we truncate UTF-16 correctly: e.g. CJK-Ideogramm encodes to TWO wchar_t: utfCvrtTo<std::wstring>("\xf0\xa4\xbd\x9c");
- size_t low = 0; //number of unicode chars!
- size_t high = unicodeLength(text); //
-
- for (;;)
- {
- const size_t middle = (low + high) / 2;
+ /*
+ performance notes (Windows):
+ - wxDC::GetTextExtent() is by far the most expensive call (20x more expensive than wxDC::DrawText())
+ - wxDC::DrawLabel() is inefficiently implemented; internally calls: wxDC::GetMultiLineTextExtent(), wxDC::GetTextExtent(), wxDC::DrawText()
+ - wxDC::GetMultiLineTextExtent() calls wxDC::GetTextExtent()
+ - wxDC::DrawText also calls wxDC::GetTextExtent()!!
+ => wxDC::DrawLabel() boils down to 3(!) calls to wxDC::GetTextExtent()!!!
+ - wxDC::DrawLabel results in GetTextExtent() call even for empty strings!!!
+ => skip the wxDC::DrawLabel() cruft and directly call wxDC::DrawText!
+ */
- std::wstring candidate(strBegin(text), findUnicodePos(text, middle));
- candidate += ELLIPSIS;
+ //truncate large texts and add ellipsis
+ assert(!contains(text, L"\n"));
+ const wchar_t ELLIPSIS = L'\u2026'; //"..."
+
+ std::wstring textTrunc = text;
+ wxSize extentTrunc = dc.GetTextExtent(textTrunc);
+ if (extentTrunc.GetWidth() > rect.width)
+ {
+ //unlike Windows 7 Explorer, we truncate UTF-16 correctly: e.g. CJK-Ideogramm encodes to TWO wchar_t: utfCvrtTo<std::wstring>("\xf0\xa4\xbd\x9c");
+ size_t low = 0; //number of unicode chars!
+ size_t high = unicodeLength(text); //
+ if (high > 1)
+ for (;;)
+ {
+ const size_t middle = (low + high) / 2; //=> never 0 when "high - low > 1"
+ if (high - low <= 1)
+ {
+ if (low == 0)
+ {
+ textTrunc = ELLIPSIS;
+ extentTrunc = dc.GetTextExtent(ELLIPSIS);
+ }
+ break;
+ }
- if (high - low <= 1)
- return candidate;
+ const std::wstring& candidate = std::wstring(strBegin(text), findUnicodePos(text, middle)) + ELLIPSIS;
+ const wxSize extentCand = dc.GetTextExtent(candidate); //perf: most expensive call of this routine!
- if (textFits(candidate))
- low = middle;
- else
- high = middle;
+ if (extentCand.GetWidth() <= rect.width)
+ {
+ low = middle;
+ textTrunc = candidate;
+ extentTrunc = extentCand;
+ }
+ else
+ high = middle;
+ }
}
-}
-
-
-void drawTextLabelFitting(wxDC& dc, const std::wstring& text, const wxRect& rect, int alignment)
-{
- RecursiveDcClipper clip(dc, rect); //wxDC::DrawLabel doesn't care about width, WTF?
-
- /*
- performance notes:
- wxDC::DrawLabel() is implemented in terms of both wxDC::GetMultiLineTextExtent() and wxDC::DrawText()
- wxDC::GetMultiLineTextExtent() is implemented in terms of wxDC::GetTextExtent()
-
- average total times:
- Windows Linux
- single wxDC::DrawText() 7µs 50µs
- wxDC::DrawLabel() + 10µs 90µs
- repeated GetTextExtent()
- */
- //truncate large texts and add ellipsis
- auto textFits = [&](const std::wstring& phrase) { return dc.GetTextExtent(phrase).GetWidth() <= rect.GetWidth(); };
- dc.DrawLabel(getTruncatedText(text, textFits), rect, alignment);
-}
-}
+ wxPoint pt = rect.GetTopLeft();
+ if (alignment & wxALIGN_RIGHT) //note: wxALIGN_LEFT == 0!
+ pt.x += rect.width - extentTrunc.GetWidth();
+ else if (alignment & wxALIGN_CENTER_HORIZONTAL)
+ pt.x += (rect.width - extentTrunc.GetWidth()) / 2;
+ if (alignment & wxALIGN_BOTTOM) //note: wxALIGN_TOP == 0!
+ pt.y += rect.height - extentTrunc.GetHeight();
+ else if (alignment & wxALIGN_CENTER_VERTICAL)
+ pt.y += (rect.height - extentTrunc.GetHeight()) / 2;
-void GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& text, bool enabled, int alignment)
-{
- wxDCTextColourChanger dummy(dc, enabled ? dc.GetTextForeground() : wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
- drawTextLabelFitting(dc, text, rect, alignment);
+ RecursiveDcClipper clip(dc, rect);
+ dc.DrawText(textTrunc, pt);
}
@@ -226,7 +232,7 @@ void GridData::drawColumnLabelBackground(wxDC& dc, const wxRect& rect, bool high
void GridData::drawColumnLabelText(wxDC& dc, const wxRect& rect, const std::wstring& text)
{
wxDCTextColourChanger dummy(dc, getColorLabelText()); //accessibility: always set both foreground AND background colors!
- drawTextLabelFitting(dc, text, rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
+ drawCellText(dc, rect, text, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
}
//----------------------------------------------------------------------------------------------------------------
@@ -524,10 +530,8 @@ private:
//label text
wxRect textRect = rect;
textRect.Deflate(1);
- {
- RecursiveDcClipper clip(dc, textRect); //wxDC::DrawLabel doesn't care about with, WTF?
- dc.DrawLabel(formatRow(row), textRect, wxALIGN_CENTRE);
- }
+
+ GridData::drawCellText(dc, textRect, formatRow(row), wxALIGN_CENTRE);
//border lines
{
@@ -1668,7 +1672,7 @@ void Grid::scrollDelta(int deltaX, int deltaY)
scrollPosX = std::max(0, scrollPosX); //wxScrollHelper::Scroll() will exit prematurely if input happens to be "-1"!
scrollPosY = std::max(0, scrollPosY); //
- Scroll(scrollPosX, scrollPosY);
+ Scroll(scrollPosX, scrollPosY); //internally calls wxWindows::Update()!
updateWindowSizes(); //may show horizontal scroll bar
}
@@ -2014,7 +2018,7 @@ void Grid::makeRowVisible(size_t row)
if (clientPosY < 0)
{
const int scrollPosY = labelRect.y / pixelsPerUnitY;
- Scroll(scrollPosX, scrollPosY);
+ Scroll(scrollPosX, scrollPosY); //internally calls wxWindows::Update()!
updateWindowSizes(); //may show horizontal scroll bar
}
else if (clientPosY + labelRect.height > rowLabelWin_->GetClientSize().GetHeight())
@@ -2076,7 +2080,7 @@ void Grid::scrollTo(size_t row)
if (scrollPosYOld != scrollPosYNew) //support polling
{
- Scroll(scrollPosXOld, scrollPosYNew);
+ Scroll(scrollPosXOld, scrollPosYNew); //internally calls wxWindows::Update()!
updateWindowSizes(); //may show horizontal scroll bar
Refresh();
}
diff --git a/wx+/grid.h b/wx+/grid.h
index cba86a24..aacd34bc 100644
--- a/wx+/grid.h
+++ b/wx+/grid.h
@@ -102,7 +102,7 @@ public:
virtual size_t getRowCount() const = 0;
- //cell area
+ //cell area:
virtual std::wstring getValue(size_t row, ColumnType colType) const = 0;
virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected); //default implementation
virtual void renderCell (wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected, HoverArea rowHover);
@@ -110,17 +110,17 @@ public:
virtual std::wstring getToolTip (size_t row, ColumnType colType) const { return std::wstring(); }
virtual HoverArea getRowMouseHover(size_t row, ColumnType colType, int cellRelativePosX, int cellWidth) { return HoverArea::NONE; }
- //label area
+ //label area:
virtual std::wstring getColumnLabel(ColumnType colType) const = 0;
virtual void renderColumnLabel(Grid& grid, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted); //default implementation
virtual std::wstring getToolTip(ColumnType colType) const { return std::wstring(); }
static const int COLUMN_GAP_LEFT; //for left-aligned text
-protected: //optional helper routines
+ //optional helper routines:
+ static void drawCellText (wxDC& dc, const wxRect& rect, const std::wstring& text, int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
static wxRect drawCellBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle
static void drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bool selected, const wxColor& backgroundColor);
- static void drawCellText (wxDC& dc, const wxRect& rect, const std::wstring& text, bool enabled, int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
static wxRect drawColumnLabelBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle
static void drawColumnLabelBackground(wxDC& dc, const wxRect& rect, bool highlighted);
diff --git a/wx+/http.cpp b/wx+/http.cpp
new file mode 100644
index 00000000..f73587b3
--- /dev/null
+++ b/wx+/http.cpp
@@ -0,0 +1,302 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#include "http.h"
+#ifdef ZEN_WIN
+ #include <zen/win.h> //tame wininet.h include
+ #include <wininet.h>
+#endif
+
+#if defined ZEN_LINUX || defined ZEN_MAC
+ #include <zen/thread.h> //std::thread::id
+ #include <wx/protocol/http.h>
+#endif
+
+using namespace zen;
+
+
+namespace
+{
+#ifdef ZEN_WIN
+ #if defined NDEBUG && defined __WXWINDOWS__
+ #error don not use wxWidgets for this component!
+ #endif
+#else
+ #ifndef NDEBUG
+ const std::thread::id mainThreadId = std::this_thread::get_id();
+ #endif
+#endif
+
+
+std::string sendHttpRequestImpl(const std::wstring& url, //throw FileError
+ const std::wstring& userAgent,
+ const std::string* postParams, //issue POST if bound, GET otherwise
+ int level = 0)
+{
+ assert(!startsWith(makeUpperCopy(url), L"HTTPS:")); //not supported by wxHTTP!
+ const std::wstring urlFmt = startsWith(makeUpperCopy(url), L"HTTP://") ? afterFirst(url, L"://", IF_MISSING_RETURN_NONE) : url;
+ const std::wstring server = beforeFirst(urlFmt, L'/', IF_MISSING_RETURN_ALL);
+ const std::wstring page = L'/' + afterFirst(urlFmt, L'/', IF_MISSING_RETURN_NONE);
+
+#ifdef ZEN_WIN
+ //WinInet: 1. uses IE proxy settings! :) 2. follows HTTP redirects by default 3. swallows HTTPS if needed
+ HINTERNET hInternet = ::InternetOpen(userAgent.c_str(), //_In_ LPCTSTR lpszAgent,
+ INTERNET_OPEN_TYPE_PRECONFIG, //_In_ DWORD dwAccessType,
+ nullptr, //_In_ LPCTSTR lpszProxyName,
+ nullptr, //_In_ LPCTSTR lpszProxyBypass,
+ 0); //_In_ DWORD dwFlags
+ if (!hInternet)
+ THROW_LAST_FILE_ERROR(_("Internet access failed."), L"InternetOpen");
+ ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hInternet));
+
+ HINTERNET hSession = ::InternetConnect(hInternet, //_In_ HINTERNET hInternet,
+ server.c_str(), //_In_ LPCTSTR lpszServerName,
+ INTERNET_DEFAULT_HTTP_PORT, //_In_ INTERNET_PORT nServerPort,
+ nullptr, //_In_ LPCTSTR lpszUsername,
+ nullptr, //_In_ LPCTSTR lpszPassword,
+ INTERNET_SERVICE_HTTP, //_In_ DWORD dwService,
+ 0, //_In_ DWORD dwFlags,
+ 0); //_In_ DWORD_PTR dwContext
+ if (!hSession)
+ THROW_LAST_FILE_ERROR(_("Internet access failed."), L"InternetConnect");
+ ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hSession));
+
+ const wchar_t* acceptTypes[] = { L"*/*", nullptr };
+ DWORD requestFlags = INTERNET_FLAG_KEEP_CONNECTION |
+ INTERNET_FLAG_NO_UI |
+ INTERNET_FLAG_RELOAD | //
+ INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS; //relevant for GET only
+ if (postParams)
+ requestFlags |= INTERNET_FLAG_NO_AUTO_REDIRECT; //POST would be re-issued as GET during auto-redirect => handle ourselves!
+
+ HINTERNET hRequest = ::HttpOpenRequest(hSession, //_In_ HINTERNET hConnect,
+ postParams ? L"POST" : L"GET", //_In_ LPCTSTR lpszVerb,
+ page.c_str(), //_In_ LPCTSTR lpszObjectName,
+ nullptr, //_In_ LPCTSTR lpszVersion,
+ nullptr, //_In_ LPCTSTR lpszReferer,
+ acceptTypes, //_In_ LPCTSTR *lplpszAcceptTypes,
+ requestFlags, //_In_ DWORD dwFlags,
+ 0); //_In_ DWORD_PTR dwContext
+ if (!hRequest)
+ THROW_LAST_FILE_ERROR(_("Internet access failed."), L"HttpOpenRequest");
+ ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hRequest));
+
+ const std::wstring headers = postParams ? L"Content-type: application/x-www-form-urlencoded" : L"";
+ std::string postParamsTmp = postParams ? *postParams : "";
+ char* postParamBuf = postParamsTmp.empty() ? nullptr : &*postParamsTmp.begin();
+ if (!::HttpSendRequest(hRequest, //_In_ HINTERNET hRequest,
+ headers.c_str(), //_In_ LPCTSTR lpszHeaders,
+ static_cast<DWORD>(headers.size()), //_In_ DWORD dwHeadersLength,
+ postParamBuf, //_In_ LPVOID lpOptional,
+ static_cast<DWORD>(postParamsTmp.size()))) //_In_ DWORD dwOptionalLength
+ THROW_LAST_FILE_ERROR(_("Internet access failed."), L"HttpSendRequest");
+
+ DWORD sc = 0;
+ {
+ DWORD bufLen = sizeof(sc);
+ if (!::HttpQueryInfo(hRequest, //_In_ HINTERNET hRequest,
+ HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, //_In_ DWORD dwInfoLevel,
+ &sc, //_Inout_ LPVOID lpvBuffer,
+ &bufLen, //_Inout_ LPDWORD lpdwBufferLength,
+ nullptr)) //_Inout_ LPDWORD lpdwIndex
+ THROW_LAST_FILE_ERROR(_("Internet access failed."), L"HttpQueryInfo: HTTP_QUERY_STATUS_CODE");
+ }
+
+ //http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
+ if (sc / 100 == 3) //e.g. 301, 302, 303, 307... we're not too greedy since we check location, too!
+ {
+ if (level < 5) //"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop."
+ {
+ DWORD bufLen = 10000;
+ std::wstring location(bufLen, L'\0');
+ if (!::HttpQueryInfo(hRequest, HTTP_QUERY_LOCATION, &*location.begin(), &bufLen, nullptr))
+ THROW_LAST_FILE_ERROR(_("Internet access failed."), L"HttpQueryInfo: HTTP_QUERY_LOCATION");
+ if (bufLen >= location.size()) //HttpQueryInfo expected to write terminating zero
+ throw FileError(_("Internet access failed."), L"HttpQueryInfo: HTTP_QUERY_LOCATION, buffer overflow");
+ location.resize(bufLen);
+
+ if (!location.empty())
+ return sendHttpRequestImpl(location, userAgent, postParams, level + 1);
+ }
+ throw FileError(_("Internet access failed."), L"Unresolvable redirect.");
+ }
+
+ if (sc != HTTP_STATUS_OK) //200
+ throw FileError(_("Internet access failed."), replaceCpy<std::wstring>(L"HTTP status code %x.", L"%x", numberTo<std::wstring>(sc)));
+ //e.g. 404 - HTTP_STATUS_NOT_FOUND
+
+ std::string buffer;
+ const DWORD blockSize = 64 * 1024;
+ //internet says "HttpQueryInfo() + HTTP_QUERY_CONTENT_LENGTH" not supported by all http servers...
+ for (;;)
+ {
+ buffer.resize(buffer.size() + blockSize);
+
+ DWORD bytesRead = 0;
+ if (!::InternetReadFile(hRequest, //_In_ HINTERNET hFile,
+ &*(buffer.begin() + buffer.size() - blockSize), //_Out_ LPVOID lpBuffer,
+ blockSize, //_In_ DWORD dwNumberOfBytesToRead,
+ &bytesRead)) //_Out_ LPDWORD lpdwNumberOfBytesRead
+ THROW_LAST_FILE_ERROR(_("Internet access failed."), L"InternetReadFile");
+
+ if (bytesRead > blockSize) //better safe than sorry
+ throw FileError(_("Internet access failed."), L"InternetReadFile: buffer overflow.");
+
+ if (bytesRead < blockSize)
+ buffer.resize(buffer.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics
+
+ if (bytesRead == 0)
+ return buffer;
+ }
+
+#else
+ assert(std::this_thread::get_id() == mainThreadId);
+ assert(wxApp::IsMainLoopRunning());
+
+ wxHTTP webAccess;
+ webAccess.SetHeader(L"User-Agent", userAgent);
+ webAccess.SetTimeout(10 /*[s]*/); //default: 10 minutes: WTF are these wxWidgets people thinking???
+
+ if (!webAccess.Connect(server)) //will *not* fail for non-reachable url here!
+ throw FileError(_("Internet access failed."), L"wxHTTP::Connect");
+
+ if (postParams)
+ if (!webAccess.SetPostText(L"application/x-www-form-urlencoded", utfCvrtTo<wxString>(*postParams)))
+ throw FileError(_("Internet access failed."), L"wxHTTP::SetPostText");
+
+ std::unique_ptr<wxInputStream> httpStream(webAccess.GetInputStream(page)); //must be deleted BEFORE webAccess is closed
+ const int sc = webAccess.GetResponse();
+
+ //http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
+ if (sc / 100 == 3) //e.g. 301, 302, 303, 307... we're not too greedy since we check location, too!
+ {
+ if (level < 5) //"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop."
+ {
+ const std::wstring newUrl(webAccess.GetHeader(L"Location"));
+ if (!newUrl.empty())
+ return sendHttpRequestImpl(newUrl, userAgent, postParams, level + 1);
+ }
+ throw FileError(_("Internet access failed."), L"Unresolvable redirect.");
+ }
+
+ if (sc != 200) //HTTP_STATUS_OK
+ throw FileError(_("Internet access failed."), replaceCpy<std::wstring>(L"HTTP status code %x.", L"%x", numberTo<std::wstring>(sc)));
+
+ if (!httpStream || webAccess.GetError() != wxPROTO_NOERR)
+ throw FileError(_("Internet access failed."), L"wxHTTP::GetError");
+
+ std::string buffer;
+ int newValue = 0;
+ while ((newValue = httpStream->GetC()) != wxEOF)
+ buffer.push_back(static_cast<char>(newValue));
+ return buffer;
+#endif
+}
+
+
+//encode into "application/x-www-form-urlencoded"
+std::string urlencode(const std::string& str)
+{
+ std::string out;
+ for (const char c : str) //follow PHP spec: https://github.com/php/php-src/blob/master/ext/standard/url.c#L500
+ if (c == ' ')
+ out += '+';
+ else if (('0' <= c && c <= '9') ||
+ ('A' <= c && c <= 'Z') ||
+ ('a' <= c && c <= 'z') ||
+ c == '-' || c == '.' || c == '_') //note: "~" is encoded by PHP!
+ out += c;
+ else
+ {
+ const char hexDigits[]= "0123456789ABCDEF";
+ out += '%';
+ out += hexDigits[static_cast<unsigned char>(c) / 16];
+ out += hexDigits[static_cast<unsigned char>(c) % 16];
+ }
+ return out;
+}
+}
+
+
+std::string zen::sendHttpPost(const std::wstring& url, const std::wstring& userAgent, const std::vector<std::pair<std::string, std::string>>& postParams) //throw FileError
+{
+ //convert post parameters into "application/x-www-form-urlencoded"
+ std::string flatParams;
+ for (const auto& pair : postParams)
+ flatParams += urlencode(pair.first) + '=' + urlencode(pair.second) + '&';
+ //encode both key and value: https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
+ if (!flatParams.empty())
+ flatParams.pop_back();
+
+ return sendHttpRequestImpl(url, userAgent, &flatParams); //throw FileError
+}
+
+
+std::string zen::sendHttpGet(const std::wstring& url, const std::wstring& userAgent) //throw FileError
+{
+ return sendHttpRequestImpl(url, userAgent, nullptr); //throw FileError
+}
+
+
+bool zen::internetIsAlive() //noexcept
+{
+#ifdef ZEN_WIN
+ //::InternetAttemptConnect(0) -> not working as expected: succeeds even when there is no internet connection!
+
+ HINTERNET hInternet = ::InternetOpen(L"FreeFileSync", //_In_ LPCTSTR lpszAgent,
+ INTERNET_OPEN_TYPE_PRECONFIG, //_In_ DWORD dwAccessType,
+ nullptr, //_In_ LPCTSTR lpszProxyName,
+ nullptr, //_In_ LPCTSTR lpszProxyBypass,
+ 0); //_In_ DWORD dwFlags
+ if (!hInternet)
+ return false;
+ ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hInternet));
+
+ //InternetOpenUrl is shortcut for HTTP:GET with InternetConnect + HttpOpenRequest + HttpSendRequest:
+ HINTERNET hRequest = ::InternetOpenUrl(hInternet, //_In_ HINTERNET hInternet,
+ L"http://www.google.com/", //_In_ LPCTSTR lpszUrl,
+ nullptr, //_In_ LPCTSTR lpszHeaders,
+ 0, //_In_ DWORD dwHeadersLength,
+ INTERNET_FLAG_KEEP_CONNECTION |
+ INTERNET_FLAG_NO_UI |
+ INTERNET_FLAG_RELOAD |
+ INTERNET_FLAG_NO_AUTO_REDIRECT, //_In_ DWORD dwFlags,
+ 0); //_In_ DWORD_PTR dwContext
+ //fails with ERROR_INTERNET_NAME_NOT_RESOLVED if server not found => the server-relative part is checked by HTTP_QUERY_STATUS_CODE!!!
+ if (!hRequest)
+ return false;
+ ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hRequest));
+
+ DWORD sc = 0;
+ {
+ DWORD bufLen = sizeof(sc);
+ if (!::HttpQueryInfo(hRequest, //_In_ HINTERNET hRequest,
+ HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, //_In_ DWORD dwInfoLevel,
+ &sc, //_Inout_ LPVOID lpvBuffer,
+ &bufLen, //_Inout_ LPDWORD lpdwBufferLength,
+ nullptr)) //_Inout_ LPDWORD lpdwIndex
+ return false;
+ }
+
+#else
+ assert(std::this_thread::get_id() == mainThreadId);
+
+ const wxString server = L"www.google.com";
+ const wxString page = L"/";
+
+ wxHTTP webAccess;
+ webAccess.SetTimeout(10 /*[s]*/); //default: 10 minutes: WTF are these wxWidgets people thinking???
+
+ if (!webAccess.Connect(server)) //will *not* fail for non-reachable url here!
+ return false;
+
+ std::unique_ptr<wxInputStream> httpStream(webAccess.GetInputStream(page)); //call before checking wxHTTP::GetResponse()
+ const int sc = webAccess.GetResponse();
+#endif
+ //attention: http://www.google.com/ might redirect to "https" => don't follow, just return "true"!!!
+ return sc / 100 == 2 || //e.g. 200
+ sc / 100 == 3; //e.g. 301, 302, 303, 307... when in doubt, consider internet alive!
+}
diff --git a/wx+/http.h b/wx+/http.h
new file mode 100644
index 00000000..90375f42
--- /dev/null
+++ b/wx+/http.h
@@ -0,0 +1,25 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef HTTP_h_879083425703425702
+#define HTTP_h_879083425703425702
+
+#include <zen/file_error.h>
+
+namespace zen
+{
+/*
+ TREAD-SAFETY
+ ------------
+ Windows: WinInet-based => may be called from worker thread
+ Linux: wxWidgets-based => don't call from worker thread
+*/
+std::string sendHttpPost(const std::wstring& url, const std::wstring& userAgent, const std::vector<std::pair<std::string, std::string>>& postParams); //throw FileError
+std::string sendHttpGet (const std::wstring& url, const std::wstring& userAgent); //throw FileError
+bool internetIsAlive(); //noexcept
+}
+
+#endif //HTTP_h_879083425703425702
diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp
index 7c6de43f..d6c6ab68 100644
--- a/wx+/image_resources.cpp
+++ b/wx+/image_resources.cpp
@@ -83,7 +83,7 @@ void GlobalResources::init(const Zstring& filepath)
wxZipInputStream streamIn(input, wxConvUTF8);
//do NOT rely on wxConvLocal! On failure shows unhelpful popup "Cannot convert from the charset 'Unknown encoding (-1)'!"
- while (true)
+ for (;;)
{
std::unique_ptr<wxZipEntry> entry(streamIn.GetNextEntry()); //take ownership!
if (!entry)
diff --git a/wx+/rtl.h b/wx+/rtl.h
index e2c6962d..f562f003 100644
--- a/wx+/rtl.h
+++ b/wx+/rtl.h
@@ -7,34 +7,17 @@
#ifndef RTL_H_0183487180058718273432148
#define RTL_H_0183487180058718273432148
-//#include <memory>
#include <zen/optional.h>
#include <wx/dcmemory.h>
-#include <wx/dcmirror.h>
#include <wx/image.h>
-#include <wx/icon.h>
#include <wx/app.h>
namespace zen
{
//functions supporting right-to-left GUI layout
-
-void drawBitmapRtlMirror(wxDC& dc,
- const wxBitmap& image,
- const wxRect& rect,
- int alignment,
- Opt<wxBitmap>& buffer); //mirror image if layout is RTL + fix some strange wxDC::Blit bug on RTL
-
-void drawBitmapRtlNoMirror(wxDC& dc, //wxDC::DrawLabel does already NOT mirror by default (but does a crappy job at it, surprise)
- const wxBitmap& image,
- const wxRect& rect,
- int alignment,
- Opt<wxBitmap>& buffer);
-
-void drawIconRtlNoMirror(wxDC& dc, //wxDC::DrawIcon DOES mirror by default
- const wxIcon& icon,
- const wxPoint& pt,
- Opt<wxBitmap>& buffer);
+void drawBitmapRtlMirror (wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer);
+void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment);
+//wxDC::DrawIcon DOES mirror by default -> implement RTL support when needed
wxBitmap mirrorIfRtl(const wxBitmap& bmp);
@@ -48,10 +31,31 @@ wxBitmap mirrorIfRtl(const wxBitmap& bmp);
//---------------------- implementation ------------------------
-namespace
+namespace impl
{
-template <class DrawImageFun>
-void drawRtlImpl(wxDC& dc, const wxRect& rect, Opt<wxBitmap>& buffer, bool doMirror, DrawImageFun draw)
+//don't use wxDC::DrawLabel: it results in expensive GetTextExtent() call even when passing an empty string!!!
+//also avoid wxDC::DrawLabel 1-off alignment bugs
+inline
+void drawBitmapAligned(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment)
+{
+ wxPoint pt = rect.GetTopLeft();
+ if (alignment & wxALIGN_RIGHT) //note: wxALIGN_LEFT == 0!
+ pt.x += rect.width - image.GetWidth();
+ else if (alignment & wxALIGN_CENTER_HORIZONTAL)
+ pt.x += (rect.width - image.GetWidth()) / 2;
+
+ if (alignment & wxALIGN_BOTTOM) //note: wxALIGN_TOP == 0!
+ pt.y += rect.height - image.GetHeight();
+ else if (alignment & wxALIGN_CENTER_VERTICAL)
+ pt.y += (rect.height - image.GetHeight()) / 2;
+
+ dc.DrawBitmap(image, pt);
+}
+}
+
+
+inline
+void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer)
{
if (dc.GetLayoutDirection() == wxLayout_RightToLeft)
{
@@ -61,50 +65,20 @@ void drawRtlImpl(wxDC& dc, const wxRect& rect, Opt<wxBitmap>& buffer, bool doMir
wxMemoryDC memDc(*buffer);
memDc.Blit(wxPoint(0, 0), rect.GetSize(), &dc, rect.GetTopLeft()); //blit in: background is mirrored due to memDc, dc having different layout direction!
- if (!doMirror)
- {
- *buffer = wxBitmap(buffer->ConvertToImage().Mirror());
- memDc.SelectObject(*buffer);
- }
-
- draw(memDc, wxRect(0, 0, rect.width, rect.height));
- //note: we cannot simply use memDc.SetLayoutDirection(wxLayout_RightToLeft) due to some strange 1 pixel bug! so it's a quadruple mirror! :>
-
- if (!doMirror)
- {
- *buffer = wxBitmap(buffer->ConvertToImage().Mirror());
- memDc.SelectObject(*buffer);
- }
+ impl::drawBitmapAligned(memDc, image, wxRect(0, 0, rect.width, rect.height), alignment);
+ //note: we cannot simply use memDc.SetLayoutDirection(wxLayout_RightToLeft) due to some strange 1 pixel bug!
dc.Blit(rect.GetTopLeft(), rect.GetSize(), &memDc, wxPoint(0, 0)); //blit out: mirror once again
}
else
- draw(dc, rect);
-}
-}
-
-
-inline
-void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer)
-{
- return drawRtlImpl(dc, rect, buffer, true, [&](wxDC& dc2, const wxRect& rect2) { dc2.DrawLabel(wxString(), image, rect2, alignment); });
+ impl::drawBitmapAligned(dc, image, rect, alignment);
}
-inline
-void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer)
-{
- if (dc.GetLayoutDirection() == wxLayout_RightToLeft)
- if ((alignment & wxALIGN_CENTER_HORIZONTAL) == 0) //we still *do* want to mirror alignment!
- alignment ^= wxALIGN_RIGHT;
- static_assert(wxALIGN_LEFT == 0, "doh");
- return drawRtlImpl(dc, rect, buffer, false, [&](wxDC& dc2, const wxRect& rect2) { dc2.DrawLabel(wxString(), image, rect2, alignment); });
-}
inline
-void drawIconRtlNoMirror(wxDC& dc, const wxIcon& icon, const wxPoint& pt, Opt<wxBitmap>& buffer)
+void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment)
{
- wxRect rect(pt.x, pt.y, icon.GetWidth(), icon.GetHeight());
- return drawRtlImpl(dc, rect, buffer, false, [&](wxDC& dc2, const wxRect& rect2) { dc2.DrawIcon(icon, rect2.GetTopLeft()); });
+ return impl::drawBitmapAligned(dc, image, rect, alignment); //wxDC::DrawBitmap does NOT mirror by default
}
diff --git a/zen/build_info.h b/zen/build_info.h
index 09e1c5a7..c430a49a 100644
--- a/zen/build_info.h
+++ b/zen/build_info.h
@@ -12,7 +12,7 @@ namespace zen
//determine build info: defines ZEN_BUILD_32BIT or ZEN_BUILD_64BIT
#ifdef ZEN_WIN
- #ifdef _WIN64
+ #ifdef _WIN64 //_WIN32 is defined by the compiler for both 32 and 64 bit builds, unlike _WIN64
#define ZEN_BUILD_64BIT
#else
#define ZEN_BUILD_32BIT
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index 702a8e32..bb78939f 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -564,9 +564,12 @@ DirWatcher::DirWatcher(const Zstring& dirPath) :
baseDirPath(dirPath),
pimpl_(std::make_unique<Pimpl>())
{
- CFStringRef dirpathCf = osx::createCFString(baseDirPath.c_str()); //returns nullptr on error
- if (!dirpathCf)
- throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"Function call failed: createCFString"); //no error code documented!
+ CFStringRef dirpathCf = nullptr;
+ try
+ {
+ dirpathCf = osx::createCFString(baseDirPath.c_str()); //throw SysError
+ }
+ catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), e.toString()); }
ZEN_ON_SCOPE_EXIT(::CFRelease(dirpathCf));
CFArrayRef dirpathCfArray = ::CFArrayCreate(nullptr, //CFAllocatorRef allocator,
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index ea13d381..78da0220 100644
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -167,6 +167,31 @@ bool isFatDrive(const Zstring& filePath) //noexcept
return &buffer[0] == Zstring(L"FAT") ||
&buffer[0] == Zstring(L"FAT32");
}
+
+
+#ifdef ZEN_WIN_VISTA_AND_LATER
+bool isFatDrive(HANDLE hFile) //noexcept
+{
+ const DWORD bufferSize = MAX_PATH + 1; //"The length of the file system name buffer, in TCHARs. The maximum buffer size is MAX_PATH + 1"
+ std::vector<wchar_t> buffer(bufferSize);
+
+ if (!::GetVolumeInformationByHandleW(hFile, //_In_ HANDLE hFile,
+ nullptr, //_Out_writes_opt_(nVolumeNameSize) LPWSTR lpVolumeNameBuffer,
+ 0, //_In_ DWORD nVolumeNameSize,
+ nullptr, //_Out_opt_ LPDWORD lpVolumeSerialNumber,
+ nullptr, //_Out_opt_ LPDWORD lpMaximumComponentLength,
+ nullptr, //_Out_opt_ LPDWORD lpFileSystemFlags,
+ &buffer[0], //_Out_writes_opt_(nFileSystemNameSize) LPWSTR lpFileSystemNameBuffer,
+ bufferSize)) //_In_ DWORD nFileSystemNameSize
+ {
+ assert(false);
+ return false;
+ }
+
+ return &buffer[0] == Zstring(L"FAT") ||
+ &buffer[0] == Zstring(L"FAT32");
+}
+#endif
#endif
}
@@ -247,6 +272,43 @@ std::uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, retu
}
+VolumeId zen::getVolumeId(const Zstring& itemPath) //throw FileError
+{
+#ifdef ZEN_WIN
+ //this works for:
+ //- root paths "C:\", "D:\"
+ //- network shares: \\share\dirname
+ //- indirection: subst S: %USERPROFILE%
+ // -> GetVolumePathName() + GetVolumeInformation() OTOH incorrectly resolves "S:\Desktop\somedir" to "S:\Desktop\" - nice try...
+ const HANDLE hItem = ::CreateFile(zen::applyLongPathPrefix(itemPath).c_str(), //_In_ LPCTSTR lpFileName,
+ 0, //_In_ DWORD dwDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ // FILE_FLAG_OPEN_REPARSE_POINT -> no, we follow symlinks!
+ FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes,
+ /*needed to open a directory*/
+ nullptr); //_In_opt_ HANDLE hTemplateFile
+ if (hItem == INVALID_HANDLE_VALUE)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"CreateFile");
+ ZEN_ON_SCOPE_EXIT(::CloseHandle(hItem));
+
+ BY_HANDLE_FILE_INFORMATION fileInfo = {};
+ if (!::GetFileInformationByHandle(hItem, &fileInfo))
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"GetFileInformationByHandle");
+
+ return fileInfo.dwVolumeSerialNumber;
+
+#elif defined ZEN_LINUX || defined ZEN_MAC
+ struct ::stat fileInfo = {};
+ if (::stat(itemPath.c_str(), &fileInfo) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"stat");
+
+ return fileInfo.st_dev;
+#endif
+}
+
+
bool zen::removeFile(const Zstring& filePath) //throw FileError
{
#ifdef ZEN_WIN
@@ -398,7 +460,7 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro
if (!::MoveFileEx(pathSourceFmt.c_str(), //__in LPCTSTR lpExistingFileName,
pathTargetFmt.c_str(), //__in_opt LPCTSTR lpNewFileName,
- 0)) //__in DWORD dwFlags
+ 0)) //__in DWORD dwFlags
{
DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls!
@@ -412,7 +474,7 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro
//try again...
if (::MoveFileEx(pathSourceFmt.c_str(), //__in LPCTSTR lpExistingFileName,
pathTargetFmt.c_str(), //__in_opt LPCTSTR lpNewFileName,
- 0)) //__in DWORD dwFlags
+ 0)) //__in DWORD dwFlags
{
//(try to) restore file attributes
::SetFileAttributes(pathTargetFmt.c_str(), oldAttr); //don't handle error
@@ -623,7 +685,7 @@ void setFileTimeByHandle(HANDLE hFile, //throw FileError
nullptr, //__in_opt const FILETIME *lpLastAccessTime,
&lastWriteTime)) //__in_opt const FILETIME *lpLastWriteTime
{
- DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls!
+ const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls!
auto toLargeInteger = [](const FILETIME& ft) -> LARGE_INTEGER
{
@@ -667,8 +729,7 @@ void setFileTimeByHandle(HANDLE hFile, //throw FileError
setFileInfo(basicInfo2); //throw FileError
}
catch (FileError&) {}
-
- ec = ERROR_SUCCESS;
+ return;
}
}
#endif
@@ -676,7 +737,11 @@ void setFileTimeByHandle(HANDLE hFile, //throw FileError
std::wstring errorMsg = replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath));
//add more meaningful message: FAT accepts only a subset of the NTFS date range
- if (ec == ERROR_INVALID_PARAMETER && isFatDrive(filePath))
+#ifdef ZEN_WIN_VISTA_AND_LATER
+ if (ec == ERROR_INVALID_PARAMETER && isFatDrive(hFile))
+#else
+ if (ec == ERROR_INVALID_PARAMETER && isFatDrive(filePath))
+#endif
{
//let's not fail due to an invalid creation time on FAT: http://www.freefilesync.org/forum/viewtopic.php?t=2278
if (creationTime) //retry (single-level recursion at most!)
@@ -718,8 +783,7 @@ void setFileTimeByHandle(HANDLE hFile, //throw FileError
}
}
- if (ec != ERROR_SUCCESS)
- throw FileError(errorMsg, formatSystemError(L"SetFileTime", ec));
+ throw FileError(errorMsg, formatSystemError(L"SetFileTime", ec));
}
}
@@ -1398,7 +1462,7 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath,
//- automatically copies symbolic links if encountered: unfortunately it doesn't copy symlinks over network shares but silently creates empty folders instead (on XP)!
//- it isn't able to copy most junctions because of missing permissions (although target path can be retrieved alternatively!)
if (!::CreateDirectory(applyLongPathPrefixCreateDir(targetPath).c_str(), //__in LPCTSTR lpPathName,
- nullptr)) //__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes
+ nullptr)) //__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes
{
DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls!
@@ -1473,7 +1537,7 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath,
if (::GetFileInformationByHandle(hDirSrc, &dirInfo))
{
::SetFileAttributes(applyLongPathPrefix(targetPath).c_str(), dirInfo.dwFileAttributes);
- //copy "read-only and system attributes": http://blogs.msdn.com/b/oldnewthing/archive/2003/09/30/55100.aspx
+ //copy "read-only and system attributes": https://blogs.msdn.microsoft.com/oldnewthing/20030930-00/?p=42353/
const bool isEncrypted = (dirInfo.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0;
const bool isCompressed = (dirInfo.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0;
@@ -1544,7 +1608,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY);
}();
- typedef BOOLEAN (WINAPI* CreateSymbolicLinkFunc)(LPCTSTR lpSymlinkFileName, LPCTSTR lpTargetFileName, DWORD dwFlags);
+ using CreateSymbolicLinkFunc = BOOLEAN (WINAPI*)(LPCTSTR lpSymlinkFileName, LPCTSTR lpTargetFileName, DWORD dwFlags);
const SysDllFun<CreateSymbolicLinkFunc> createSymbolicLink(L"kernel32.dll", "CreateSymbolicLinkW");
if (!createSymbolicLink)
@@ -1663,7 +1727,10 @@ bool canCopyAsSparse(DWORD fileAttrSource, Function getTargetFsFlags) //throw ()
DWORD targetFsFlags = 0;
if (!getTargetFsFlags(targetFsFlags))
- return false;
+ {
+ assert(false);
+ return false;
+ }
assert(targetFsFlags != 0);
return (targetFsFlags & FILE_SUPPORTS_SPARSE_FILES) != 0;
@@ -2069,9 +2136,8 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
0, //_In_ DWORD nOutBufferSize,
&bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned
nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped
- {} //may legitimately fail with ERROR_INVALID_FUNCTION if
-
- // - if target folder is encrypted
+ {} //may legitimately fail with ERROR_INVALID_FUNCTION if:
+ // - target folder is encrypted
// - target volume does not support compressed attribute
//#############################################################################
}
diff --git a/zen/file_access.h b/zen/file_access.h
index 026027e7..9c1b37ef 100644
--- a/zen/file_access.h
+++ b/zen/file_access.h
@@ -15,6 +15,8 @@
namespace zen
{
+//note: certain functions require COM initialization! (vista_file_op.h)
+
bool fileExists (const Zstring& filePath); //noexcept; check whether file or file-symlink exists
bool dirExists (const Zstring& dirPath ); //noexcept; check whether directory or dir-symlink exists
bool symlinkExists (const Zstring& linkPath); //noexcept; check whether a symbolic link exists
@@ -31,6 +33,7 @@ void setFileTime(const Zstring& filePath, std::int64_t modificationTime, ProcSym
//symlink handling: always evaluate target
std::uint64_t getFilesize(const Zstring& filePath); //throw FileError
std::uint64_t getFreeDiskSpace(const Zstring& path); //throw FileError, returns 0 if not available
+VolumeId getVolumeId(const Zstring& itemPath); //throw FileError
bool removeFile(const Zstring& filePath); //throw FileError; return "false" if file is not existing
diff --git a/zen/file_error.h b/zen/file_error.h
index f41a878a..61a2e89c 100644
--- a/zen/file_error.h
+++ b/zen/file_error.h
@@ -39,6 +39,7 @@ DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume);
//CAVEAT: thread-local Win32 error code is easily overwritten => evaluate *before* making any (indirect) system calls:
//-> MinGW + Win XP: "throw" statement allocates memory to hold the exception object => error code is cleared
//-> VC 2015, Debug: std::wstring allocator internally calls ::FlsGetValue() => error code is cleared
+// https://connect.microsoft.com/VisualStudio/feedback/details/1775690/calling-operator-new-may-set-lasterror-to-0
#ifdef _MSC_VER
#define THROW_LAST_FILE_ERROR(msg, functionName) \
do \
diff --git a/zen/file_id_def.h b/zen/file_id_def.h
index 24e45795..2880121c 100644
--- a/zen/file_id_def.h
+++ b/zen/file_id_def.h
@@ -20,12 +20,28 @@
namespace zen
{
#ifdef ZEN_WIN
-typedef DWORD DeviceId;
-typedef ULONGLONG FileIndex;
+using VolumeId = DWORD;
+using FileIndex = ULONGLONG;
-typedef std::pair<DeviceId, FileIndex> FileId; //optional! (however, always set on Linux, and *generally* available on Windows)
+#elif defined ZEN_LINUX || defined ZEN_MAC
+namespace impl { typedef struct ::stat StatDummy; } //sigh...
+
+using VolumeId = decltype(impl::StatDummy::st_dev);
+using FileIndex = decltype(impl::StatDummy::st_ino);
+#endif
+
+
+struct FileId //always available on Linux, and *generally* available on Windows)
+{
+ FileId() {}
+ FileId(VolumeId volId, FileIndex fIdx) : volumeId(volId), fileIndex(fIdx) {}
+ VolumeId volumeId = 0;
+ FileIndex fileIndex = 0;
+};
+inline bool operator==(const FileId& lhs, const FileId& rhs) { return lhs.volumeId == rhs.volumeId && lhs.fileIndex == rhs.fileIndex; }
+#ifdef ZEN_WIN
inline
FileId extractFileId(const BY_HANDLE_FILE_INFORMATION& fileInfo)
{
@@ -44,19 +60,11 @@ FileId extractFileId(DWORD volumeSerialNumber, ULONGLONG fileIndex)
FileId(volumeSerialNumber, fileIndex) : FileId();
}
-static_assert(sizeof(FileId().first ) == sizeof(BY_HANDLE_FILE_INFORMATION().dwVolumeSerialNumber), "");
-static_assert(sizeof(FileId().second) == sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexHigh) + sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexLow), "");
-static_assert(sizeof(FileId().second) == sizeof(ULARGE_INTEGER), "");
-
+static_assert(sizeof(FileId().volumeId ) == sizeof(BY_HANDLE_FILE_INFORMATION().dwVolumeSerialNumber), "");
+static_assert(sizeof(FileId().fileIndex) == sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexHigh) + sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexLow), "");
+static_assert(sizeof(FileId().fileIndex) == sizeof(ULARGE_INTEGER), "");
#elif defined ZEN_LINUX || defined ZEN_MAC
-namespace impl { typedef struct ::stat StatDummy; } //sigh...
-
-typedef decltype(impl::StatDummy::st_dev) DeviceId;
-typedef decltype(impl::StatDummy::st_ino) FileIndex;
-
-typedef std::pair<DeviceId, FileIndex> FileId;
-
inline
FileId extractFileId(const struct ::stat& fileInfo)
{
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index b4351ee8..3891abe6 100644
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -114,7 +114,7 @@ FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLock
fileHandle = createHandle(FILE_SHARE_READ | FILE_SHARE_DELETE);
if (fileHandle == INVALID_HANDLE_VALUE)
{
- //=> support reading files which are open for write (e.g. Firefox db files): follow CopyFileEx() by addding FILE_SHARE_WRITE only for second try:
+ //=> support reading files which are open for write (e.g. Firefox .db, .sqlite files): follow CopyFileEx() by addding FILE_SHARE_WRITE only for second try:
if (::GetLastError() == ERROR_SHARING_VIOLATION)
fileHandle = createHandle(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE);
@@ -185,7 +185,7 @@ size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError; m
throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
#ifdef ZEN_WIN
- //posix ::read() semantics: test for end of file: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365690%28v=vs.85%29.aspx
+ //posix ::read() semantics: test for end of file: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365690
DWORD bytesRead = 0;
if (!::ReadFile(fileHandle, //__in HANDLE hFile,
buffer, //__out LPVOID lpBuffer,
@@ -233,7 +233,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil
{
return ::CreateFile(applyLongPathPrefix(filepath).c_str(), //_In_ LPCTSTR lpFileName,
GENERIC_READ | GENERIC_WRITE, //_In_ DWORD dwDesiredAccess,
- /* http://msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx
+ /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858#files
quote: When an application creates a file across a network, it is better
to use GENERIC_READ | GENERIC_WRITE for dwDesiredAccess than to use GENERIC_WRITE alone.
The resulting code is faster, because the redirector can use the cache manager and send fewer SMBs with more data.
@@ -253,7 +253,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil
{
DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls!
- //CREATE_ALWAYS fails with ERROR_ACCESS_DENIED if the existing file is hidden or "system" http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
+ //CREATE_ALWAYS fails with ERROR_ACCESS_DENIED if the existing file is hidden or "system": https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858#files
if (ec == ERROR_ACCESS_DENIED && dwCreationDisposition == CREATE_ALWAYS)
{
const DWORD attrib = ::GetFileAttributes(applyLongPathPrefix(filepath).c_str());
@@ -355,7 +355,7 @@ size_t FileOutput::tryWrite(const void* buffer, size_t bytesToWrite) //throw Fil
throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
#ifdef ZEN_WIN
- DWORD bytesWritten = 0; //this parameter is NOT optional: http://blogs.msdn.com/b/oldnewthing/archive/2013/04/04/10407417.aspx
+ DWORD bytesWritten = 0; //this parameter is NOT optional: https://blogs.msdn.microsoft.com/oldnewthing/20130404-00/?p=4753/
if (!::WriteFile(fileHandle, //__in HANDLE hFile,
buffer, //__out LPVOID lpBuffer,
static_cast<DWORD>(bytesToWrite), //__in DWORD nNumberOfBytesToWrite,
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index aa39e508..89eb6e48 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -68,13 +68,13 @@ void zen::traverseFolder(const Zstring& dirPath,
//skip "." and ".."
const wchar_t* const itemNameRaw = findData.cFileName;
- if (itemNameRaw[0] == 0)
- throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile: Data corruption; item with empty name.");
-
if (itemNameRaw[0] == L'.' &&
(itemNameRaw[1] == 0 || (itemNameRaw[1] == L'.' && itemNameRaw[2] == 0)))
continue;
+ if (itemNameRaw[0] == 0)
+ throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile: Data corruption; item with empty name.");
+
const Zstring& itemPath = appendSeparator(dirPath) + itemNameRaw;
if (zen::isSymlink(findData)) //check first!
@@ -121,9 +121,6 @@ void zen::traverseFolder(const Zstring& dirPath,
//don't return "." and ".."
const char* itemNameRaw = dirEntry->d_name;
- if (itemNameRaw[0] == 0)
- throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name.");
-
if (itemNameRaw[0] == '.' &&
(itemNameRaw[1] == 0 || (itemNameRaw[1] == '.' && itemNameRaw[2] == 0)))
continue;
@@ -134,15 +131,18 @@ void zen::traverseFolder(const Zstring& dirPath,
{
itemName = osx::convertToPrecomposedUtf(itemNameRaw); //throw SysError
}
- catch (const SysError& e) //failure is not an item-level error since wo don't have the proper decomposed name!!!
+ catch (const SysError& e) //failure is not an item-level error since we don't have the proper decomposed name!!!
{
throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)),
L"Failed to generate precomposed file name: " + fmtPath(itemNameRaw) + L"\n" + e.toString()); //too obscure to warrant translation
}
- const Zstring& itemPath = appendSeparator(dirPath) + itemName;
#else
- const Zstring& itemPath = appendSeparator(dirPath) + itemNameRaw;
+ const Zstring& itemName = itemNameRaw;
#endif
+ if (itemName.empty()) //checks result of osx::convertToPrecomposedUtf, too!
+ throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name.");
+
+ const Zstring& itemPath = appendSeparator(dirPath) + itemName;
struct ::stat statData = {};
try
diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp
index 3dfe805b..a880552e 100644
--- a/zen/format_unit.cpp
+++ b/zen/format_unit.cpp
@@ -302,7 +302,7 @@ std::wstring zen::utcToLocalTimeString(std::int64_t utcTime)
#endif
static const bool useNewLocalTimeCalculation = zen::vistaOrLater();
- //http://msdn.microsoft.com/en-us/library/ms724277(VS.85).aspx
+ //https://msdn.microsoft.com/en-us/library/ms724277
if (useNewLocalTimeCalculation) //DST conversion like in Windows 7: NTFS stays fixed, but FAT jumps by one hour
{
SYSTEMTIME systemTimeUtc = {};
diff --git a/zen/guid.h b/zen/guid.h
index e8326ce6..914674af 100644
--- a/zen/guid.h
+++ b/zen/guid.h
@@ -22,7 +22,7 @@
namespace zen
{
inline
-std::string generateGUID() //creates a 16 byte GUID
+std::string generateGUID() //creates a 16-byte GUID
{
boost::uuids::uuid nativeRep = boost::uuids::random_generator()();
//generator is only thread-safe like an int, so we keep it local until we need to optimize perf
diff --git a/zen/scope_guard.h b/zen/scope_guard.h
index 67eb3053..f8c32127 100644
--- a/zen/scope_guard.h
+++ b/zen/scope_guard.h
@@ -22,7 +22,7 @@ inline int getUncaughtExceptionCount() { return std::uncaught_exceptions(); }
#ifdef ZEN_LINUX
static_assert(__GNUC__ < 5 || (__GNUC__ == 5 && (__GNUC_MINOR__ < 3 || (__GNUC_MINOR__ == 3 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support");
#else
- static_assert(__clang_major__ < 7 || (__clang_major__ == 7 && __clang_minor__ <= 0), "check std::uncaught_exceptions support");
+ static_assert(__clang_major__ < 7 || (__clang_major__ == 7 && __clang_minor__ <= 3), "check std::uncaught_exceptions support");
#endif
namespace __cxxabiv1
@@ -61,6 +61,47 @@ enum class ScopeGuardRunMode
template <ScopeGuardRunMode runMode, typename F>
+struct ScopeGuardDestructor;
+
+//specialize scope guard destructor code and get rid of those pesky MSVC "4127 conditional expression is constant"
+template <typename F>
+struct ScopeGuardDestructor<ScopeGuardRunMode::ON_EXIT, F>
+{
+ static void run(F& fun, int exeptionCountOld)
+ {
+ (void)exeptionCountOld; //silence unused parameter warning
+ try { fun(); }
+ catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"!
+ }
+};
+
+
+template <typename F>
+struct ScopeGuardDestructor<ScopeGuardRunMode::ON_SUCCESS, F>
+{
+ static void run(F& fun, int exeptionCountOld)
+ {
+ const bool failed = getUncaughtExceptionCount() > exeptionCountOld;
+ if (!failed)
+ fun(); //throw X
+ }
+};
+
+
+template <typename F>
+struct ScopeGuardDestructor<ScopeGuardRunMode::ON_FAIL, F>
+{
+ static void run(F& fun, int exeptionCountOld)
+ {
+ const bool failed = getUncaughtExceptionCount() > exeptionCountOld;
+ if (failed)
+ try { fun(); }
+ catch (...) { assert(false); }
+ }
+};
+
+
+template <ScopeGuardRunMode runMode, typename F>
class ScopeGuard
{
public:
@@ -74,26 +115,7 @@ public:
~ScopeGuard() noexcept(runMode != ScopeGuardRunMode::ON_SUCCESS)
{
if (!dismissed)
- {
-#ifdef _MSC_VER
-#pragma warning(suppress: 4127) //"conditional expression is constant"
-#endif
- if (runMode != ScopeGuardRunMode::ON_EXIT)
- {
- const bool failed = getUncaughtExceptionCount() > exeptionCount;
- if ((runMode == ScopeGuardRunMode::ON_FAIL) != failed)
- return;
- }
-
-#ifdef _MSC_VER
-#pragma warning(suppress: 4127) //"conditional expression is constant"
-#endif
- if (runMode == ScopeGuardRunMode::ON_SUCCESS)
- fun_(); //throw X
- else
- try { fun_(); }
- catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"!
- }
+ ScopeGuardDestructor<runMode, F>::run(fun_, exeptionCount);
}
void dismiss() { dismissed = true; }
diff --git a/zen/shell_execute.h b/zen/shell_execute.h
index e6dcf7f5..060ba84d 100644
--- a/zen/shell_execute.h
+++ b/zen/shell_execute.h
@@ -82,14 +82,14 @@ void shellExecute(const Zstring& command, ExecutionType type) //throw FileError
trim(commandTmp, true, false); //CommandLineToArgvW() does not like leading spaces
std::vector<Zstring> argv;
- {
- int argc = 0;
- LPWSTR* tmp = ::CommandLineToArgvW(commandTmp.c_str(), &argc);
- if (!tmp)
- THROW_LAST_FILE_ERROR(_("Incorrect command line:") + L"\n" + commandTmp.c_str(), L"CommandLineToArgvW");
+ {
+ int argc = 0;
+ LPWSTR* tmp = ::CommandLineToArgvW(commandTmp.c_str(), &argc);
+ if (!tmp)
+ THROW_LAST_FILE_ERROR(_("Incorrect command line:") + L"\n" + commandTmp.c_str(), L"CommandLineToArgvW");
ZEN_ON_SCOPE_EXIT(::LocalFree(tmp));
std::copy(tmp, tmp + argc, std::back_inserter(argv));
- }
+ }
Zstring filepath;
Zstring arguments;
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index b78dd5dd..058609e6 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -62,6 +62,7 @@ bool equal(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2);
size_t hashBytes(const unsigned char* ptr, size_t len);
+size_t hashBytesAppend(size_t hashVal, const unsigned char* ptr, size_t len);
//support for custom string classes in std::unordered_set/map
@@ -216,20 +217,30 @@ size_t hashBytes(const unsigned char* ptr, size_t len)
//http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
#ifdef ZEN_BUILD_32BIT
const size_t basis = 2166136261U;
- const size_t prime = 16777619U;
#elif defined ZEN_BUILD_64BIT
const size_t basis = 14695981039346656037ULL;
+#endif
+ return hashBytesAppend(basis, ptr, len);
+}
+
+
+inline
+size_t hashBytesAppend(size_t hashVal, const unsigned char* ptr, size_t len)
+{
+#ifdef ZEN_BUILD_32BIT
+ const size_t prime = 16777619U;
+#elif defined ZEN_BUILD_64BIT
const size_t prime = 1099511628211ULL;
#endif
- size_t val = basis;
for (size_t i = 0; i < len; ++i)
{
- val ^= static_cast<size_t>(ptr[i]);
- val *= prime;
+ hashVal ^= static_cast<size_t>(ptr[i]);
+ hashVal *= prime;
}
- return val;
+ return hashVal;
}
+
}
#endif //STL_TOOLS_H_84567184321434
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 92ca1654..ed6a1a54 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -77,7 +77,7 @@ template <class T, class S> T copyStringTo(S&& str);
template <> inline
bool isWhiteSpace(char ch)
{
- assert(ch != 0); //std C++ does not consider 0 as white space
+ assert(ch != 0); //std C++ does not consider 0 as white space
//caveat 1: std::isspace() takes an int, but expects an unsigned char
//caveat 2: some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC)
return static_cast<unsigned char>(ch) < 128 &&
@@ -85,10 +85,10 @@ bool isWhiteSpace(char ch)
}
template <> inline
-bool isWhiteSpace(wchar_t ch)
+bool isWhiteSpace(wchar_t ch)
{
- assert(ch != 0); //std C++ does not consider 0 as white space
- return std::iswspace(ch) != 0;
+ assert(ch != 0); //std C++ does not consider 0 as white space
+ return std::iswspace(ch) != 0;
}
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index f50d1806..c4e166e8 100644
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -135,7 +135,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro
throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"Not a symbolic link or junction.");
//absolute symlinks and junctions use NT namespace naming convention while relative ones do not:
- //http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#NT_Namespaces
+ //https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247#NT_Namespaces
return ntPathToWin32Path(output);
#elif defined ZEN_LINUX || defined ZEN_MAC
@@ -158,7 +158,7 @@ Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError
using namespace zen;
#ifdef ZEN_WIN
//GetFinalPathNameByHandle() is not available before Vista!
- typedef DWORD (WINAPI* GetFinalPathNameByHandleWFunc)(HANDLE hFile, LPTSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags);
+ using GetFinalPathNameByHandleWFunc = DWORD (WINAPI*)(HANDLE hFile, LPTSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags);
const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW");
if (!getFinalPathNameByHandle)
throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\""));
@@ -184,15 +184,12 @@ Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError
&targetPath[0], //__out LPTSTR lpszFilePath,
bufferSize, //__in DWORD cchFilePath,
0); //__in DWORD dwFlags
- if (charsWritten == 0 || charsWritten >= bufferSize)
- {
- const std::wstring errorMsg = replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath));
- if (charsWritten == 0)
- THROW_LAST_FILE_ERROR(errorMsg, L"GetFinalPathNameByHandle");
- throw FileError(errorMsg);
- }
+ if (charsWritten == 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"GetFinalPathNameByHandle");
+ if (charsWritten >= bufferSize)
+ throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"GetFinalPathNameByHandle: buffer overflow.");
- return Zstring(&targetPath[0], charsWritten);
+ return removeLongPathPrefix(Zstring(&targetPath[0], charsWritten)); //MSDN: GetFinalPathNameByHandle() always prepends "\\?\"
#elif defined ZEN_LINUX || defined ZEN_MAC
char* targetPath = ::realpath(linkPath.c_str(), nullptr);
diff --git a/zen/utf.h b/zen/utf.h
index 117b13bc..c0b2b4af 100644
--- a/zen/utf.h
+++ b/zen/utf.h
@@ -238,7 +238,7 @@ void utf8ToCodePoint(CharIterator first, CharIterator last, Function writeOutput
template <class CharString> inline
size_t unicodeLength(const CharString& str, char) //utf8
{
- typedef typename GetCharType<CharString>::Type CharType;
+ using CharType = typename GetCharType<CharString>::Type;
const CharType* strFirst = strBegin(str);
const CharType* const strLast = strFirst + strLength(str);
@@ -258,7 +258,7 @@ size_t unicodeLength(const CharString& str, char) //utf8
template <class WideString> inline
size_t unicodeLengthWide(const WideString& str, Int2Type<2>) //windows: utf16-wchar_t
{
- typedef typename GetCharType<WideString>::Type CharType;
+ using CharType = typename GetCharType<WideString>::Type;
const CharType* strFirst = strBegin(str);
const CharType* const strLast = strFirst + strLength(str);
@@ -302,7 +302,7 @@ namespace implementation
template <class CharString> inline
size_t findUnicodePos(const CharString& str, size_t unicodePos, char) //utf8-char
{
- typedef typename GetCharType<CharString>::Type CharType;
+ using CharType = typename GetCharType<CharString>::Type;
const CharType* strFirst = strBegin(str);
const size_t strLen = strLength(str);
@@ -326,7 +326,7 @@ size_t findUnicodePos(const CharString& str, size_t unicodePos, char) //utf8-cha
template <class WideString> inline
size_t findUnicodePosWide(const WideString& str, size_t unicodePos, Int2Type<2>) //windows: utf16-wchar_t
{
- typedef typename GetCharType<WideString>::Type CharType;
+ using CharType = typename GetCharType<WideString>::Type;
const CharType* strFirst = strBegin(str);
const size_t strLen = strLength(str);
bgstack15